From 16159f37611834bc177338f7f29fecfed4cbf368 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 28 Sep 2023 15:07:14 +0200 Subject: [PATCH 001/479] 106812: Refactor resource policies to have either group or eperson --- .../app/itemimport/ItemImportServiceImpl.java | 2 +- .../authorize/AuthorizeServiceImpl.java | 8 +- .../dspace/authorize/FixDefaultPolicies.java | 3 +- .../java/org/dspace/authorize/PolicySet.java | 9 +- .../authorize/ResourcePolicyServiceImpl.java | 16 +- .../service/ResourcePolicyService.java | 30 +- .../crosswalk/METSRightsCrosswalk.java | 108 ++--- ...ce_group_or_eperson_for_resourcepolicy.sql | 10 + .../status/DefaultAccessStatusHelperTest.java | 25 +- .../org/dspace/builder/CollectionBuilder.java | 3 +- .../dspace/builder/ResourcePolicyBuilder.java | 20 +- .../java/org/dspace/content/BundleTest.java | 6 +- .../java/org/dspace/content/ItemTest.java | 11 +- .../dspace/content/packager/ITDSpaceAIP.java | 11 +- .../dspace/content/service/ItemServiceIT.java | 12 +- .../ResourcePolicyRestRepository.java | 31 +- .../app/rest/BitstreamControllerIT.java | 130 +++--- .../app/rest/BitstreamRestControllerIT.java | 3 +- .../app/rest/BitstreamRestRepositoryIT.java | 13 +- .../app/rest/BundleRestRepositoryIT.java | 2 +- .../app/rest/CollectionRestRepositoryIT.java | 31 +- .../app/rest/CommunityRestRepositoryIT.java | 16 +- .../app/rest/GroupRestRepositoryIT.java | 18 +- ...wningCollectionUpdateRestControllerIT.java | 18 +- .../dspace/app/rest/ItemRestRepositoryIT.java | 3 +- .../rest/ItemTemplateRestControllerIT.java | 12 +- .../rest/ResourcePolicyRestRepositoryIT.java | 425 ++++++++---------- .../app/rest/StatisticsRestRepositoryIT.java | 15 +- .../rest/WorkspaceItemRestRepositoryIT.java | 64 +-- .../authorization/CCLicenseFeatureRestIT.java | 6 +- .../CanManageBitstreamBundlesFeatureIT.java | 20 +- .../CanManageMappingsFeatureIT.java | 9 +- .../authorization/CanSubscribeFeatureIT.java | 5 +- .../rest/authorization/EditItemFeatureIT.java | 12 +- .../GenericAuthorizationFeatureIT.java | 89 ++-- .../ManageAdminGroupFeatureIT.java | 6 +- .../ManageSubmitterGroupFeatureIT.java | 6 +- .../ManageTemplateItemFeatureIT.java | 6 +- .../ManageWorkflowGroupFeatureIT.java | 6 +- .../rest/authorization/SubmitFeatureIT.java | 24 +- 40 files changed, 572 insertions(+), 672 deletions(-) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 4148232cf3ba..a77d854ade26 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1810,7 +1810,7 @@ protected void setPermission(Context c, Group g, int actionID, Bitstream bs) authorizeService.removeAllPolicies(c, bs); // add the policy - ResourcePolicy rp = resourcePolicyService.create(c); + ResourcePolicy rp = resourcePolicyService.create(c, null, g); rp.setdSpaceObject(bs); rp.setAction(actionID); diff --git a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java index fc438c234cda..3f326de3c740 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -550,13 +550,11 @@ public void addPolicies(Context c, List policies, DSpaceObject d List newPolicies = new ArrayList<>(policies.size()); for (ResourcePolicy srp : policies) { - ResourcePolicy rp = resourcePolicyService.create(c); + ResourcePolicy rp = resourcePolicyService.create(c, srp.getEPerson(), srp.getGroup()); // copy over values rp.setdSpaceObject(dest); rp.setAction(srp.getAction()); - rp.setEPerson(srp.getEPerson()); - rp.setGroup(srp.getGroup()); rp.setStartDate(srp.getStartDate()); rp.setEndDate(srp.getEndDate()); rp.setRpName(srp.getRpName()); @@ -670,11 +668,9 @@ public ResourcePolicy createResourcePolicy(Context context, DSpaceObject dso, Gr "We need at least an eperson or a group in order to create a resource policy."); } - ResourcePolicy myPolicy = resourcePolicyService.create(context); + ResourcePolicy myPolicy = resourcePolicyService.create(context, eperson, group); myPolicy.setdSpaceObject(dso); myPolicy.setAction(type); - myPolicy.setGroup(group); - myPolicy.setEPerson(eperson); myPolicy.setRpType(rpType); myPolicy.setRpName(rpName); myPolicy.setRpDescription(rpDescription); diff --git a/dspace-api/src/main/java/org/dspace/authorize/FixDefaultPolicies.java b/dspace-api/src/main/java/org/dspace/authorize/FixDefaultPolicies.java index 61e783920ae5..b430a2635fd9 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/FixDefaultPolicies.java +++ b/dspace-api/src/main/java/org/dspace/authorize/FixDefaultPolicies.java @@ -126,10 +126,9 @@ private static void addAnonymousPolicy(Context c, DSpaceObject t, // now create the default policies for submitted items ResourcePolicyService resourcePolicyService = AuthorizeServiceFactory.getInstance().getResourcePolicyService(); - ResourcePolicy myPolicy = resourcePolicyService.create(c); + ResourcePolicy myPolicy = resourcePolicyService.create(c, null, anonymousGroup); myPolicy.setdSpaceObject(t); myPolicy.setAction(myaction); - myPolicy.setGroup(anonymousGroup); resourcePolicyService.update(c, myPolicy); } } diff --git a/dspace-api/src/main/java/org/dspace/authorize/PolicySet.java b/dspace-api/src/main/java/org/dspace/authorize/PolicySet.java index 72998b9fd18a..e22b8e9df026 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/PolicySet.java +++ b/dspace-api/src/main/java/org/dspace/authorize/PolicySet.java @@ -229,11 +229,10 @@ public static void setPoliciesFilter(Context c, int containerType, // before create a new policy check if an identical policy is already in place if (!authorizeService.isAnIdenticalPolicyAlreadyInPlace(c, myitem, group, actionID, -1)) { // now add the policy - ResourcePolicy rp = resourcePolicyService.create(c); + ResourcePolicy rp = resourcePolicyService.create(c, null, group); rp.setdSpaceObject(myitem); rp.setAction(actionID); - rp.setGroup(group); rp.setRpName(name); rp.setRpDescription(description); @@ -262,11 +261,10 @@ public static void setPoliciesFilter(Context c, int containerType, // before create a new policy check if an identical policy is already in place if (!authorizeService.isAnIdenticalPolicyAlreadyInPlace(c, bundle, group, actionID, -1)) { // now add the policy - ResourcePolicy rp = resourcePolicyService.create(c); + ResourcePolicy rp = resourcePolicyService.create(c, null, group); rp.setdSpaceObject(bundle); rp.setAction(actionID); - rp.setGroup(group); rp.setRpName(name); rp.setRpDescription(description); @@ -305,11 +303,10 @@ public static void setPoliciesFilter(Context c, int containerType, if (!authorizeService .isAnIdenticalPolicyAlreadyInPlace(c, bitstream, group, actionID, -1)) { // now add the policy - ResourcePolicy rp = resourcePolicyService.create(c); + ResourcePolicy rp = resourcePolicyService.create(c, null, group); rp.setdSpaceObject(bitstream); rp.setAction(actionID); - rp.setGroup(group); rp.setRpName(name); rp.setRpDescription(description); diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java index b762107a84c5..7b93b912378e 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java @@ -71,14 +71,22 @@ public ResourcePolicy find(Context context, int id) throws SQLException { * Create a new ResourcePolicy * * @param context DSpace context object + * @param ePerson + * @param group * @return ResourcePolicy * @throws SQLException if database error */ @Override - public ResourcePolicy create(Context context) throws SQLException { + public ResourcePolicy create(Context context, EPerson ePerson, Group group) throws SQLException { // FIXME: Check authorisation // Create a table row - ResourcePolicy resourcePolicy = resourcePolicyDAO.create(context, new ResourcePolicy()); + ResourcePolicy policyToBeCreated = new ResourcePolicy(); + if (ePerson == null && group == null) { + throw new IllegalArgumentException("A resource policy must contain a valid eperson or group"); + } + policyToBeCreated.setEPerson(ePerson); + policyToBeCreated.setGroup(group); + ResourcePolicy resourcePolicy = resourcePolicyDAO.create(context, policyToBeCreated); return resourcePolicy; } @@ -205,9 +213,7 @@ public boolean isDateValid(ResourcePolicy resourcePolicy) { @Override public ResourcePolicy clone(Context context, ResourcePolicy resourcePolicy) throws SQLException, AuthorizeException { - ResourcePolicy clone = create(context); - clone.setGroup(resourcePolicy.getGroup()); - clone.setEPerson(resourcePolicy.getEPerson()); + ResourcePolicy clone = create(context, resourcePolicy.getEPerson(), resourcePolicy.getGroup()); clone.setStartDate((Date) ObjectUtils.clone(resourcePolicy.getStartDate())); clone.setEndDate((Date) ObjectUtils.clone(resourcePolicy.getEndDate())); clone.setRpType((String) ObjectUtils.clone(resourcePolicy.getRpType())); diff --git a/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java b/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java index 43735fcd6089..523bd64006d0 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java +++ b/dspace-api/src/main/java/org/dspace/authorize/service/ResourcePolicyService.java @@ -17,7 +17,6 @@ import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; -import org.dspace.service.DSpaceCRUDService; /** * Service interface class for the ResourcePolicy object. @@ -26,7 +25,34 @@ * * @author kevinvandevelde at atmire.com */ -public interface ResourcePolicyService extends DSpaceCRUDService { +public interface ResourcePolicyService { + + public ResourcePolicy create(Context context, EPerson eperson, Group group) throws SQLException, AuthorizeException; + + public ResourcePolicy find(Context context, int id) throws SQLException; + + /** + * Persist a model object. + * + * @param context + * @param resourcePolicy object to be persisted. + * @throws SQLException passed through. + * @throws AuthorizeException passed through. + */ + public void update(Context context, ResourcePolicy resourcePolicy) throws SQLException, AuthorizeException; + + + /** + * Persist a collection of model objects. + * + * @param context + * @param resourcePolicies object to be persisted. + * @throws SQLException passed through. + * @throws AuthorizeException passed through. + */ + public void update(Context context, List resourcePolicies) throws SQLException, AuthorizeException; + + public void delete(Context context, ResourcePolicy resourcePolicy) throws SQLException, AuthorizeException; public List find(Context c, DSpaceObject o) throws SQLException; diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java index 7f6622841ba7..66a1f334284c 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java @@ -432,31 +432,7 @@ public void ingest(Context context, DSpaceObject dso, List ml, boolean //get what class of context this is String contextClass = element.getAttributeValue("CONTEXTCLASS"); - ResourcePolicy rp = resourcePolicyService.create(context); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - - // get reference to the element - // Note: we are assuming here that there will only ever be ONE - // element. Currently there are no known use cases for multiple. - Element permsElement = element.getChild("Permissions", METSRights_NS); - if (permsElement == null) { - log.error("No element was found. Skipping this element."); - continue; - } - - if (element.getAttributeValue("rpName") != null) { - rp.setRpName(element.getAttributeValue("rpName")); - } - try { - if (element.getAttributeValue("start-date") != null) { - rp.setStartDate(sdf.parse(element.getAttributeValue("start-date"))); - } - if (element.getAttributeValue("end-date") != null) { - rp.setEndDate(sdf.parse(element.getAttributeValue("end-date"))); - } - } catch (ParseException ex) { - log.error("Failed to parse embargo date. The date needs to be in the format 'yyyy-MM-dd'.", ex); - } + ResourcePolicy rp = null; //Check if this permission pertains to Anonymous users if (ANONYMOUS_CONTEXTCLASS.equals(contextClass)) { @@ -464,22 +440,23 @@ public void ingest(Context context, DSpaceObject dso, List ml, boolean Group anonGroup = groupService.findByName(context, Group.ANONYMOUS); if (anonGroup == null) { throw new CrosswalkInternalException( - "The DSpace database has not been properly initialized. The Anonymous Group is " + - "missing from the database."); + "The DSpace database has not been properly initialized. The Anonymous Group is " + + "missing from the database."); } - rp.setGroup(anonGroup); + rp = resourcePolicyService.create(context, null, anonGroup); } else if (ADMIN_CONTEXTCLASS.equals(contextClass)) { // else if this permission declaration pertains to Administrators // get DSpace Administrator group Group adminGroup = groupService.findByName(context, Group.ADMIN); if (adminGroup == null) { throw new CrosswalkInternalException( - "The DSpace database has not been properly initialized. The Administrator Group is " + - "missing from the database."); + "The DSpace database has not been properly initialized. " + + "The Administrator Group is " + + "missing from the database."); } - rp.setGroup(adminGroup); + rp = resourcePolicyService.create(context, null, adminGroup); } else if (GROUP_CONTEXTCLASS.equals(contextClass)) { // else if this permission pertains to another DSpace group try { @@ -498,18 +475,17 @@ public void ingest(Context context, DSpaceObject dso, List ml, boolean //if not found, throw an error -- user should restore group from the SITE AIP if (group == null) { throw new CrosswalkInternalException("Cannot restore Group permissions on object (" - + "type=" + Constants.typeText[dso - .getType()] + ", " - + "handle=" + dso.getHandle() + ", " - + "ID=" + dso.getID() - + "). The Group named '" + groupName + "' is" + - " missing from DSpace. " - + "Please restore this group using the SITE " + - "AIP, or recreate it."); + + "type=" + Constants.typeText[dso.getType()] + ", " + + "handle=" + dso.getHandle() + ", " + + "ID=" + dso.getID() + + "). The Group named '" + groupName + "' is" + + " missing from DSpace. " + + "Please restore this group using the SITE " + + "AIP, or recreate it."); } //assign group to policy - rp.setGroup(group); + rp = resourcePolicyService.create(context, null, group); } catch (PackageException pe) { //A PackageException will only be thrown if translateDefaultGroupName() fails //We'll just wrap it as a CrosswalkException and throw it upwards @@ -535,25 +511,51 @@ public void ingest(Context context, DSpaceObject dso, List ml, boolean //if not found, throw an error -- user should restore person from the SITE AIP if (person == null) { throw new CrosswalkInternalException("Cannot restore Person permissions on object (" - + "type=" + Constants.typeText[dso - .getType()] + ", " - + "handle=" + dso.getHandle() + ", " - + "ID=" + dso.getID() - + "). The Person with email/netid '" + - personEmail + "' is missing from DSpace. " - + "Please restore this Person object using the " + - "SITE AIP, or recreate it."); + + "type=" + Constants.typeText[dso.getType()] + ", " + + "handle=" + dso.getHandle() + ", " + + "ID=" + dso.getID() + + "). The Person with email/netid '" + + personEmail + "' is missing from DSpace. " + + "Please restore this Person object using the " + + "SITE AIP, or recreate it."); } - //assign person to the policy - rp.setEPerson(person); + //create rp with the person + rp = resourcePolicyService.create(context, person, null); } else { log.error("Unrecognized CONTEXTCLASS: " + contextClass); } + if (rp != null) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + + // get reference to the element + // Note: we are assuming here that there will only ever be ONE + // element. Currently there are no known use cases for multiple. + Element permsElement = element.getChild("Permissions", METSRights_NS); + if (permsElement == null) { + log.error("No element was found. Skipping this element."); + continue; + } + + if (element.getAttributeValue("rpName") != null) { + rp.setRpName(element.getAttributeValue("rpName")); + } + try { + if (element.getAttributeValue("start-date") != null) { + rp.setStartDate(sdf.parse(element.getAttributeValue("start-date"))); + } + if (element.getAttributeValue("end-date") != null) { + rp.setEndDate(sdf.parse(element.getAttributeValue("end-date"))); + } + } catch (ParseException ex) { + log.error("Failed to parse embargo date. The date needs to be in the format 'yyyy-MM-dd'.", + ex); + } - //set permissions on policy add to list of policies - rp.setAction(parsePermissions(permsElement)); - policies.add(rp); + //set permissions on policy add to list of policies + rp.setAction(parsePermissions(permsElement)); + policies.add(rp); + } } //end if "Context" element } //end for loop diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql new file mode 100644 index 000000000000..de40d0415dc6 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql @@ -0,0 +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/ +-- + +ALTER TABLE ResourcePolicy ADD CONSTRAINT resourcepolicy_eperson_and_epersongroup_not_nullobject_chk + CHECK (eperson_id is not null or epersongroup_id is not 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 1134990e84f4..59dfbb2095ea 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 @@ -261,10 +261,9 @@ public void testWithEmbargo() throws Exception { bitstream.setName(context, "primary"); bundle.setPrimaryBitstreamID(bitstream); List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); - policy.setRpName("Embargo"); Group group = groupService.findByName(context, Group.ANONYMOUS); - policy.setGroup(group); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); + policy.setRpName("Embargo"); policy.setAction(Constants.READ); policy.setStartDate(new LocalDate(9999, 12, 31).toDate()); policies.add(policy); @@ -290,10 +289,9 @@ public void testWithDateRestriction() throws Exception { bitstream.setName(context, "primary"); bundle.setPrimaryBitstreamID(bitstream); List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); - policy.setRpName("Restriction"); Group group = groupService.findByName(context, Group.ANONYMOUS); - policy.setGroup(group); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); + policy.setRpName("Restriction"); policy.setAction(Constants.READ); policy.setStartDate(new LocalDate(10000, 1, 1).toDate()); policies.add(policy); @@ -317,10 +315,9 @@ public void testWithGroupRestriction() throws Exception { bitstream.setName(context, "primary"); bundle.setPrimaryBitstreamID(bitstream); List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); - policy.setRpName("Restriction"); Group group = groupService.findByName(context, Group.ADMIN); - policy.setGroup(group); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); + policy.setRpName("Restriction"); policy.setAction(Constants.READ); policies.add(policy); authorizeService.removeAllPolicies(context, bitstream); @@ -380,10 +377,9 @@ public void testWithPrimaryAndMultipleBitstreams() throws Exception { new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8))); bundle.setPrimaryBitstreamID(primaryBitstream); List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); - policy.setRpName("Embargo"); Group group = groupService.findByName(context, Group.ANONYMOUS); - policy.setGroup(group); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); + policy.setRpName("Embargo"); policy.setAction(Constants.READ); policy.setStartDate(new LocalDate(9999, 12, 31).toDate()); policies.add(policy); @@ -411,10 +407,9 @@ public void testWithNoPrimaryAndMultipleBitstreams() throws Exception { Bitstream anotherBitstream = bitstreamService.create(context, bundle, new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8))); List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); - policy.setRpName("Embargo"); Group group = groupService.findByName(context, Group.ANONYMOUS); - policy.setGroup(group); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); + policy.setRpName("Embargo"); policy.setAction(Constants.READ); policy.setStartDate(new LocalDate(9999, 12, 31).toDate()); policies.add(policy); diff --git a/dspace-api/src/test/java/org/dspace/builder/CollectionBuilder.java b/dspace-api/src/test/java/org/dspace/builder/CollectionBuilder.java index f287c7aa8d32..74d745c07c75 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CollectionBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CollectionBuilder.java @@ -253,8 +253,7 @@ public CollectionBuilder withAdminGroup(EPerson... members) throws SQLException, public CollectionBuilder withDefaultItemRead(Group group) throws SQLException, AuthorizeException { resourcePolicyService.removePolicies(context, collection, DEFAULT_ITEM_READ); - ResourcePolicy resourcePolicy = resourcePolicyService.create(context); - resourcePolicy.setGroup(group); + ResourcePolicy resourcePolicy = resourcePolicyService.create(context, null, group); resourcePolicy.setAction(DEFAULT_ITEM_READ); resourcePolicy.setdSpaceObject(collection); resourcePolicyService.update(context, resourcePolicy); diff --git a/dspace-api/src/test/java/org/dspace/builder/ResourcePolicyBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ResourcePolicyBuilder.java index 70b1f8d73daf..c3b1b658ee1c 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ResourcePolicyBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ResourcePolicyBuilder.java @@ -110,31 +110,23 @@ public static void delete(Integer id) indexingService.commit(); } - public static ResourcePolicyBuilder createResourcePolicy(Context context) + public static ResourcePolicyBuilder createResourcePolicy(Context context, EPerson ePerson, + Group group) throws SQLException, AuthorizeException { ResourcePolicyBuilder resourcePolicyBuilder = new ResourcePolicyBuilder(context); - return resourcePolicyBuilder.create(context); + return resourcePolicyBuilder.create(context, ePerson, group); } - private ResourcePolicyBuilder create(Context context) + private ResourcePolicyBuilder create(Context context, final EPerson ePerson, + final Group epersonGroup) throws SQLException, AuthorizeException { this.context = context; - resourcePolicy = resourcePolicyService.create(context); + resourcePolicy = resourcePolicyService.create(context, ePerson, epersonGroup); return this; } - public ResourcePolicyBuilder withUser(EPerson ePerson) throws SQLException { - resourcePolicy.setEPerson(ePerson); - return this; - } - - public ResourcePolicyBuilder withGroup(Group epersonGroup) throws SQLException { - resourcePolicy.setGroup(epersonGroup); - return this; - } - public ResourcePolicyBuilder withAction(int action) throws SQLException { resourcePolicy.setAction(action); return this; 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 4ff35f5b4df8..e8ff69c47bed 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleTest.java @@ -598,9 +598,9 @@ public void testInheritCollectionDefaultPolicies() throws AuthorizeException, SQ @Test public void testReplaceAllBitstreamPolicies() throws SQLException, AuthorizeException { List newpolicies = new ArrayList(); - newpolicies.add(resourcePolicyService.create(context)); - newpolicies.add(resourcePolicyService.create(context)); - newpolicies.add(resourcePolicyService.create(context)); + newpolicies.add(resourcePolicyService.create(context, eperson, null)); + newpolicies.add(resourcePolicyService.create(context, eperson, null)); + newpolicies.add(resourcePolicyService.create(context, eperson, null)); bundleService.replaceAllBitstreamPolicies(context, b, newpolicies); List bspolicies = bundleService.getBundlePolicies(context, b); diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index d440597ec416..ada9bbc15905 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -1257,7 +1257,7 @@ public void testGetType() { @Test public void testReplaceAllItemPolicies() throws Exception { List newpolicies = new ArrayList(); - ResourcePolicy pol1 = resourcePolicyService.create(context); + ResourcePolicy pol1 = resourcePolicyService.create(context, eperson, null); newpolicies.add(pol1); itemService.replaceAllItemPolicies(context, it, newpolicies); @@ -1284,9 +1284,9 @@ public void testReplaceAllBitstreamPolicies() throws Exception { bundleService.addBitstream(context, created, result); List newpolicies = new ArrayList(); - newpolicies.add(resourcePolicyService.create(context)); - newpolicies.add(resourcePolicyService.create(context)); - newpolicies.add(resourcePolicyService.create(context)); + newpolicies.add(resourcePolicyService.create(context, eperson, null)); + newpolicies.add(resourcePolicyService.create(context, eperson, null)); + newpolicies.add(resourcePolicyService.create(context, eperson, null)); context.restoreAuthSystemState(); itemService.replaceAllBitstreamPolicies(context, it, newpolicies); @@ -1316,9 +1316,8 @@ public void testRemoveGroupPolicies() throws Exception { context.turnOffAuthorisationSystem(); List newpolicies = new ArrayList(); Group g = groupService.create(context); - ResourcePolicy pol1 = resourcePolicyService.create(context); + ResourcePolicy pol1 = resourcePolicyService.create(context, null, g); newpolicies.add(pol1); - pol1.setGroup(g); itemService.replaceAllItemPolicies(context, it, newpolicies); itemService.removeGroupPolicies(context, it, g); diff --git a/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java b/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java index a634b98130a6..c5a73ed539a7 100644 --- a/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java +++ b/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java @@ -386,9 +386,8 @@ public void testRestoreRestrictedCommunity() throws Exception { // Create a custom resource policy for this community List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); policy.setRpName("Special Read Only"); - policy.setGroup(group); policy.setAction(Constants.READ); policies.add(policy); @@ -600,9 +599,8 @@ public void testRestoreRestrictedCollection() throws Exception { // Create a custom resource policy for this Collection List policies = new ArrayList<>(); - ResourcePolicy policy = resourcePolicyService.create(context); + ResourcePolicy policy = resourcePolicyService.create(context, null, group); policy.setRpName("Special Read Only"); - policy.setGroup(group); policy.setAction(Constants.READ); policies.add(policy); @@ -822,10 +820,9 @@ public void testRestoreRestrictedItem() throws Exception { // Create a custom resource policy for this Item List policies = new ArrayList<>(); - ResourcePolicy admin_policy = resourcePolicyService.create(context); - admin_policy.setRpName("Admin Read-Only"); Group adminGroup = groupService.findByName(context, Group.ADMIN); - admin_policy.setGroup(adminGroup); + ResourcePolicy admin_policy = resourcePolicyService.create(context, null, adminGroup); + admin_policy.setRpName("Admin Read-Only"); admin_policy.setAction(Constants.READ); policies.add(admin_policy); itemService.replaceAllItemPolicies(context, item, policies); diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java index e40577ef36ea..e825acdad894 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java @@ -668,8 +668,7 @@ public void testFindItemsWithEditNoRights() throws Exception { @Test public void testFindAndCountItemsWithEditEPerson() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(item) .withAction(Constants.WRITE) .build(); @@ -682,8 +681,7 @@ public void testFindAndCountItemsWithEditEPerson() throws Exception { @Test public void testFindAndCountItemsWithAdminEPerson() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(item) .withAction(Constants.ADMIN) .build(); @@ -702,8 +700,7 @@ public void testFindAndCountItemsWithEditGroup() throws Exception { .build(); context.restoreAuthSystemState(); - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(item) .withAction(Constants.WRITE) .build(); @@ -722,8 +719,7 @@ public void testFindAndCountItemsWithAdminGroup() throws Exception { .build(); context.restoreAuthSystemState(); - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(item) .withAction(Constants.ADMIN) .build(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java index 0b77f96b9b5f..a79a9fe4eaa4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java @@ -254,14 +254,6 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx if (dspaceObject == null) { throw new UnprocessableEntityException("DSpaceObject with this uuid: " + resourceUuid + " not found"); } - resourcePolicy = resourcePolicyService.create(context); - resourcePolicy.setRpType(resourcePolicyRest.getPolicyType()); - resourcePolicy.setdSpaceObject(dspaceObject); - resourcePolicy.setRpName(resourcePolicyRest.getName()); - resourcePolicy.setRpDescription(resourcePolicyRest.getDescription()); - resourcePolicy.setAction(Constants.getActionID(resourcePolicyRest.getAction())); - resourcePolicy.setStartDate(resourcePolicyRest.getStartDate()); - resourcePolicy.setEndDate(resourcePolicyRest.getEndDate()); if (epersonUuidStr != null) { try { @@ -270,12 +262,11 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx if (ePerson == null) { throw new UnprocessableEntityException("EPerson with uuid: " + epersonUuid + " not found"); } - resourcePolicy.setEPerson(ePerson); - resourcePolicyService.update(context, resourcePolicy); + resourcePolicy = resourcePolicyService.create(context, ePerson, null); + } catch (SQLException excSQL) { throw new RuntimeException(excSQL.getMessage(), excSQL); } - return converter.toRest(resourcePolicy, utils.obtainProjection()); } else { try { UUID groupUuid = UUID.fromString(groupUuidStr); @@ -283,13 +274,27 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx if (group == null) { throw new UnprocessableEntityException("Group with uuid: " + groupUuid + " not found"); } - resourcePolicy.setGroup(group); - resourcePolicyService.update(context, resourcePolicy); + resourcePolicy = resourcePolicyService.create(context, null, group); } catch (SQLException excSQL) { throw new RuntimeException(excSQL.getMessage(), excSQL); } + } + + if (resourcePolicy != null) { + + resourcePolicy.setRpType(resourcePolicyRest.getPolicyType()); + resourcePolicy.setdSpaceObject(dspaceObject); + resourcePolicy.setRpName(resourcePolicyRest.getName()); + resourcePolicy.setRpDescription(resourcePolicyRest.getDescription()); + resourcePolicy.setAction(Constants.getActionID(resourcePolicyRest.getAction())); + resourcePolicy.setStartDate(resourcePolicyRest.getStartDate()); + resourcePolicy.setEndDate(resourcePolicyRest.getEndDate()); + resourcePolicyService.update(context, resourcePolicy); return converter.toRest(resourcePolicy, utils.obtainProjection()); + } else { + throw new UnprocessableEntityException("A resource policy must contain a valid eperson or group"); } + } @Override diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java index a326d195c2a0..c31bb61f060e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java @@ -302,25 +302,25 @@ public void putOnBitstreamInOneBundle() throws Exception { .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -471,31 +471,31 @@ public void putOnBitstreamInMultipleBundles() throws Exception { .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle2).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle2).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -592,25 +592,25 @@ public void putOnBitstreamInNoBundle() throws Exception { .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -708,22 +708,22 @@ public void putOnBitstreamInOneBundleWithNoRemoveRights() throws Exception { .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -810,22 +810,22 @@ public void putOnBitstreamInOneBundleWithNoWriteOnCurrentBundleRights() throws E .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -913,22 +913,22 @@ public void putOnBitstreamInOneBundleWithNoWriteRightsOnTargetBundle() throws Ex .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -1015,22 +1015,22 @@ public void putOnBitstreamInOneBundleWithNoAddRightsOnTargetBundle() throws Exce .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -1117,22 +1117,22 @@ public void putOnBitstreamInOneBundleWithNoWriteRightsOnBitstream() throws Excep .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -1220,22 +1220,22 @@ public void putOnBitstreamInOneBundleWithNoWriteRightsOnCurrentItem() throws Exc .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetItem).build(); @@ -1322,22 +1322,22 @@ public void putOnBitstreamInOneBundleWithNoWriteRightsOnTargetItem() throws Exce .withPassword("test") .withNameInMetadata("Bundle", "Put").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.REMOVE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bundle1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.ADD) .withDspaceObject(targetBundle).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(bitstream).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(putBundlePerson) + ResourcePolicyBuilder.createResourcePolicy(context, putBundlePerson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); 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 4813cc659694..d2223e6baa7a 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 @@ -1086,8 +1086,7 @@ public void updateBitstreamFormatEPerson() throws Exception { context.turnOffAuthorisationSystem(); - createResourcePolicy(context) - .withUser(eperson) + createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(bitstream) .build(); 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 8b34edb938a6..104b73a8cbab 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 @@ -644,7 +644,7 @@ public void findOneBitstreamTest_EmbargoedBitstream_ePersonREADRightsOnBundle() // Replace anon read policy on bundle of bitstream with ePerson READ policy resourcePolicyService.removePolicies(context, bitstream.getBundles().get(0), Constants.READ); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(bitstream.getBundles().get(0)).build(); @@ -707,9 +707,9 @@ public void findOneBitstreamFormatTest_EmbargoedBitstream_ePersonREADRightsOnBun // Replace anon read policy on bundle of bitstream with ePerson READ policy resourcePolicyService.removePolicies(context, bitstream.getBundles().get(0), Constants.READ); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(eperson) - .withAction(Constants.READ) - .withDspaceObject(bitstream.getBundles().get(0)).build(); + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withAction(Constants.READ) + .withDspaceObject(bitstream.getBundles().get(0)).build(); context.restoreAuthSystemState(); @@ -832,7 +832,7 @@ public void findOneBitstreamTest_EmbargoedBitstream_ePersonREADRightsOnItem() th // Replace anon read policy on item of bitstream with ePerson READ policy resourcePolicyService.removePolicies(context, publicItem1, Constants.READ); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(publicItem1).build(); @@ -1480,8 +1480,7 @@ public void testHiddenMetadataForUserWithWriteRights() throws Exception { .build(); } - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(col1) .build(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java index 259580f8c081..6b148e88e132 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java @@ -347,7 +347,7 @@ public void createBundleWithSufficientPermissions() throws Exception { .withPassword("test") .withNameInMetadata("Create", "Bundle").build(); - ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(createBundleEperson) + ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context, createBundleEperson, null) .withAction(Constants.ADD) .withDspaceObject(item).build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java index ee522db170c7..014a31c50112 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java @@ -1811,8 +1811,7 @@ public void testHiddenMetadataForUserWithWriteRights() throws Exception { - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(col1) .build(); @@ -2894,11 +2893,10 @@ public void testSubGroupOfCommunityAdminGroupAuthorizedSearch() throws Exception communityC = CommunityBuilder.createCommunity(context) .withName("the last community is topLevelCommunityC") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COMMUNITY_" + + topLevelCommunityA.getID() + "_ADMIN")) .withDspaceObject(communityB) - .withAction(Constants.ADMIN) - .withGroup(groupService.findByName(context, "COMMUNITY_" + topLevelCommunityA.getID() + "_ADMIN")) - .build(); + .withAction(Constants.ADMIN).build(); collectionB = CollectionBuilder.createCollection(context, subCommunityA) .withName("collectionB is a very original name") .build(); @@ -2950,11 +2948,10 @@ public void testSubGroupOfSubCommunityAdminGroupAuthorizedSearch() throws Except .withName("the last community is topLevelCommunityC") .addParentCommunity(context, topLevelCommunityA) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COMMUNITY_" + + subCommunityA.getID() + "_ADMIN")) .withDspaceObject(communityB) - .withAction(Constants.ADMIN) - .withGroup(groupService.findByName(context, "COMMUNITY_" + subCommunityA.getID() + "_ADMIN")) - .build(); + .withAction(Constants.ADMIN).build(); collectionB = CollectionBuilder.createCollection(context, subCommunityA) .withName("collectionB is a very original name") .build(); @@ -3012,11 +3009,10 @@ public void testSubGroupOfCollectionAdminGroupAuthorizedSearch() throws Exceptio collectionC = CollectionBuilder.createCollection(context, communityC) .withName("the last collection is collectionC") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COLLECTION_" + + collectionA.getID() + "_ADMIN")) .withDspaceObject(collectionB) - .withAction(Constants.ADMIN) - .withGroup(groupService.findByName(context, "COLLECTION_" + collectionA.getID() + "_ADMIN")) - .build(); + .withAction(Constants.ADMIN).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -3065,11 +3061,10 @@ public void testSubGroupOfSubmitterGroupAuthorizedSearch() throws Exception { collectionB = CollectionBuilder.createCollection(context, communityB) .withName("collectionB is a very original name") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COLLECTION_" + + collectionA.getID() + "_SUBMIT")) .withDspaceObject(collectionB) - .withAction(Constants.ADD) - .withGroup(groupService.findByName(context, "COLLECTION_" + collectionA.getID() + "_SUBMIT")) - .build(); + .withAction(Constants.ADD).build(); collectionC = CollectionBuilder.createCollection(context, communityC) .withName("the last collection is collectionC") .build(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java index 30614e6125f2..22e62c24168a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java @@ -2370,10 +2370,10 @@ public void testSubGroupOfCommunityAdminGroupAuthorizedSearch() throws Exception communityC = CommunityBuilder.createCommunity(context) .withName("the last community is topLevelCommunityC") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COMMUNITY_" + + topLevelCommunityA.getID() + "_ADMIN")) .withDspaceObject(communityB) .withAction(Constants.ADMIN) - .withGroup(groupService.findByName(context, "COMMUNITY_" + topLevelCommunityA.getID() + "_ADMIN")) .build(); context.restoreAuthSystemState(); @@ -2423,10 +2423,10 @@ public void testSubGroupOfSubCommunityAdminGroupAuthorizedSearch() throws Except .withName("the last community is topLevelCommunityC") .addParentCommunity(context, topLevelCommunityA) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COMMUNITY_" + + subCommunityA.getID() + "_ADMIN")) .withDspaceObject(communityB) .withAction(Constants.ADMIN) - .withGroup(groupService.findByName(context, "COMMUNITY_" + subCommunityA.getID() + "_ADMIN")) .build(); context.restoreAuthSystemState(); @@ -2473,10 +2473,10 @@ public void testSubGroupOfCollectionAdminGroupAuthorizedSearch() throws Exceptio Collection collectionB = CollectionBuilder.createCollection(context, communityB) .withName("collectionB") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COLLECTION_" + + collectionA.getID() + "_ADMIN")) .withDspaceObject(collectionB) .withAction(Constants.ADMIN) - .withGroup(groupService.findByName(context, "COLLECTION_" + collectionA.getID() + "_ADMIN")) .build(); communityC = CommunityBuilder.createCommunity(context) .withName("the last community is topLevelCommunityC") @@ -2521,10 +2521,10 @@ public void testSubGroupOfSubmitterGroupAuthorizedSearch() throws Exception { Collection collectionB = CollectionBuilder.createCollection(context, communityB) .withName("collectionB") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupService.findByName(context, "COLLECTION_" + + collectionA.getID() + "_SUBMIT")) .withDspaceObject(collectionB) .withAction(Constants.ADD) - .withGroup(groupService.findByName(context, "COLLECTION_" + collectionA.getID() + "_SUBMIT")) .build(); communityC = CommunityBuilder.createCommunity(context) .withName("the last community is topLevelCommunityC") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index fda8b15effa3..ef4bfa2ac591 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -3124,10 +3124,10 @@ public void commAdminAndColAdminCannotExploitItemReadGroupTest() throws Exceptio .build(); Group adminGroup = groupService.findByName(context, Group.ADMIN); - ResourcePolicyBuilder.createResourcePolicy(context).withAction(Constants.DEFAULT_ITEM_READ) - .withGroup(adminGroup).withDspaceObject(child1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withAction(Constants.DEFAULT_ITEM_READ) - .withGroup(adminGroup).withDspaceObject(col1).build(); + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup).withAction(Constants.DEFAULT_ITEM_READ) + .withDspaceObject(child1).build(); + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup).withAction(Constants.DEFAULT_ITEM_READ) + .withDspaceObject(col1).build(); context.restoreAuthSystemState(); String tokenAdminComm = getAuthToken(adminChild1.getEmail(), password); @@ -3187,10 +3187,12 @@ public void commAdminAndColAdminCannotExpoloitBitstreamReadGroupTest() throws Ex .build(); Group adminGroup = groupService.findByName(context, Group.ADMIN); - ResourcePolicyBuilder.createResourcePolicy(context).withAction(Constants.DEFAULT_BITSTREAM_READ) - .withGroup(adminGroup).withDspaceObject(child1).build(); - ResourcePolicyBuilder.createResourcePolicy(context).withAction(Constants.DEFAULT_BITSTREAM_READ) - .withGroup(adminGroup).withDspaceObject(col1).build(); + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withAction(Constants.DEFAULT_BITSTREAM_READ) + .withDspaceObject(child1).build(); + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withAction(Constants.DEFAULT_BITSTREAM_READ) + .withDspaceObject(col1).build(); context.restoreAuthSystemState(); String tokenAdminComm = getAuthToken(adminChild1.getEmail(), password); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java index 73c2c8a3fe59..7d2d1d901ec7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java @@ -131,13 +131,13 @@ public void moveItemTestByMinimallyAuthorizedUser() throws Exception { EPerson itemMoveEperson = EPersonBuilder.createEPerson(context).withEmail("item@move.org").withPassword("test") .withNameInMetadata("Item", "Move").build(); - ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.ADMIN) .withDspaceObject(col1).build(); - ResourcePolicy rp2 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp2 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicy rp3 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp3 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.ADD) .withDspaceObject(col2).build(); @@ -181,10 +181,10 @@ public void moveItemTestByAuthorizedUserWithoutAdd() throws Exception { EPerson itemMoveEperson = EPersonBuilder.createEPerson(context).withEmail("item@move.org").withPassword("test") .withNameInMetadata("Item", "Move").build(); - ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.ADMIN) .withDspaceObject(col1).build(); - ResourcePolicy rp2 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp2 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); @@ -222,10 +222,10 @@ public void moveItemTestByAuthorizedUserWithoutAdmin() throws Exception { EPerson itemMoveEperson = EPersonBuilder.createEPerson(context).withEmail("item@move.org").withPassword("test") .withNameInMetadata("Item", "Move").build(); - ResourcePolicy rp2 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp2 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.WRITE) .withDspaceObject(publicItem1).build(); - ResourcePolicy rp3 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp3 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.ADD) .withDspaceObject(col2).build(); @@ -263,10 +263,10 @@ public void moveItemTestByAuthorizedUserWithoutWrite() throws Exception { EPerson itemMoveEperson = EPersonBuilder.createEPerson(context).withEmail("item@move.org").withPassword("test") .withNameInMetadata("Item", "Move").build(); - ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp1 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.ADMIN) .withDspaceObject(col1).build(); - ResourcePolicy rp3 = ResourcePolicyBuilder.createResourcePolicy(context).withUser(itemMoveEperson) + ResourcePolicy rp3 = ResourcePolicyBuilder.createResourcePolicy(context, itemMoveEperson, null) .withAction(Constants.ADD) .withDspaceObject(col2).build(); 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 801976be9f0d..7558f321e583 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 @@ -3012,8 +3012,7 @@ public void testHiddenMetadataForUserWithWriteRights() throws Exception { context.restoreAuthSystemState(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(item) .build(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java index 1fd9e81ca88d..f3ba6d3aed1f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java @@ -253,9 +253,9 @@ public void patchTemplateItemAsCollectionAdmin() throws Exception { String itemId = installTestTemplate(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(eperson) - .withAction(Constants.ADMIN) - .withDspaceObject(childCollection).build(); + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withAction(Constants.ADMIN) + .withDspaceObject(childCollection).build(); String collAdminToken = getAuthToken(eperson.getEmail(), password); getClient(collAdminToken).perform(patch(getTemplateItemUrlTemplate(itemId)) @@ -374,9 +374,9 @@ public void deleteTemplateItemAsCollectionAdmin() throws Exception { setupTestTemplate(); String itemId = installTestTemplate(); - ResourcePolicyBuilder.createResourcePolicy(context).withUser(eperson) - .withAction(Constants.ADMIN) - .withDspaceObject(childCollection).build(); + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withAction(Constants.ADMIN) + .withDspaceObject(childCollection).build(); String collAdminToken = getAuthToken(eperson.getEmail(), password); getClient(collAdminToken).perform(delete(getTemplateItemUrlTemplate(itemId))) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 02834607113d..e7d2634525d3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -80,8 +80,10 @@ public void findAllTest() throws Exception { context.turnOffAuthorisationSystem(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withDspaceObject(community).withAction(Constants.READ) - .withUser(admin).build(); + ResourcePolicyBuilder.createResourcePolicy(context, admin, null) + .withDspaceObject(community) + .withAction(Constants.READ) + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); @@ -94,8 +96,10 @@ public void findAllUnAuthenticatedTest() throws Exception { context.turnOffAuthorisationSystem(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicyBuilder.createResourcePolicy(context).withDspaceObject(community).withAction(Constants.READ) - .withUser(admin).build(); + ResourcePolicyBuilder.createResourcePolicy(context, admin, null) + .withDspaceObject(community) + .withAction(Constants.READ) + .build(); context.restoreAuthSystemState(); getClient().perform(get("/api/authz/resourcepolicies")).andExpect(status().isUnauthorized()); @@ -112,10 +116,9 @@ public void findOneTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) .withAction(Constants.READ) - .withUser(eperson1) .build(); context.restoreAuthSystemState(); @@ -144,10 +147,9 @@ public void findOneResourcePolicyOfAnonymousGroupTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, groupAnonymous) .withDspaceObject(community) .withAction(Constants.READ) - .withGroup(groupAnonymous) .build(); context.restoreAuthSystemState(); @@ -165,8 +167,10 @@ public void findOneUnAuthenticatedTest() throws Exception { context.turnOffAuthorisationSystem(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context).withDspaceObject(community) - .withAction(Constants.READ).withUser(eperson).build(); + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withDspaceObject(community) + .withAction(Constants.READ) + .build(); context.restoreAuthSystemState(); @@ -201,10 +205,9 @@ public void findOneForbiddenTest() throws Exception { Collection collection = CollectionBuilder.createCollection(context, community) .withName("My collection").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(collection) - .withAction(Constants.WRITE) - .withUser(eperson1).build(); + .withAction(Constants.WRITE).build(); context.restoreAuthSystemState(); @@ -228,10 +231,9 @@ public void findOneAccessGrantToAdminTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) - .withAction(Constants.WRITE) - .withUser(eperson1).build(); + .withAction(Constants.WRITE).build(); context.restoreAuthSystemState(); @@ -262,10 +264,9 @@ public void findOneAccessGrantToSameUserTest() throws Exception { Collection collection = CollectionBuilder.createCollection(context, community) .withName("My collection").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(collection) .withAction(Constants.ADD) - .withGroup(group1) .build(); context.restoreAuthSystemState(); @@ -297,15 +298,13 @@ public void findOneResoucesPolicyByEpersonUuidTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My community_2").build(); - ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) - .withAction(Constants.ADD) - .withUser(eperson1).build(); + .withAction(Constants.ADD).build(); - ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context, eperson2, null) .withDspaceObject(community2) - .withAction(Constants.REMOVE) - .withUser(eperson2).build(); + .withAction(Constants.REMOVE).build(); context.restoreAuthSystemState(); @@ -336,20 +335,18 @@ public void findResoucesPoliciesByEpersonUuidAndResourceUuidTest() throws Except Collection collection = CollectionBuilder.createCollection(context, community).withName("My collection") .build(); - ResourcePolicy resourcePolicyOfCommunity = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfCommunity = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1).build(); + .withAction(Constants.READ).build(); - ResourcePolicy secondResourcePolicyOfCommunity = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.REMOVE) - .withUser(eperson1).build(); + ResourcePolicy secondResourcePolicyOfCommunity = ResourcePolicyBuilder + .createResourcePolicy(context, eperson1, null) + .withDspaceObject(community) + .withAction(Constants.REMOVE).build(); - ResourcePolicy resourcePolicyOfCollection = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfCollection = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(collection) - .withAction(Constants.REMOVE) - .withUser(eperson1).build(); + .withAction(Constants.REMOVE).build(); context.restoreAuthSystemState(); @@ -395,10 +392,9 @@ public void findResoucesPoliciesByEPersonUuidUnAuthenticatedTest() throws Except Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1).build(); + .withAction(Constants.READ).build(); context.restoreAuthSystemState(); @@ -437,15 +433,13 @@ public void findResourcesPoliciesByEPersonUuidForbiddenTest() throws Exception { Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); - ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community).withAction(Constants.WRITE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson1).build(); + .withPolicyType(ResourcePolicy.TYPE_CUSTOM).build(); - ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context, eperson2, null) .withDspaceObject(community2).withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + .withPolicyType(ResourcePolicy.TYPE_CUSTOM).build(); context.restoreAuthSystemState(); @@ -474,16 +468,18 @@ public void findResourcePoliciesOfOneResourceWithoutActionTest() throws Exceptio Community community2 = CommunityBuilder.createCommunity(context).withName("My second community").build(); - ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADMIN) - .withUser(eperson1).build(); + ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson1, null) + .withDspaceObject(community) + .withAction(Constants.ADMIN) + .build(); - ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson2, null) + .withDspaceObject(community2) + .withAction(Constants.ADD) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); ResourcePolicy resourcePolicyAnonymous = authorizeService .findByTypeGroupAction(context, community, EPersonServiceFactory.getInstance() @@ -528,20 +524,23 @@ public void findResourcePoliciesOfOneResourceWithActionTest() throws Exception { Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); - ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADMIN) - .withUser(eperson1).build(); + ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson1, null) + .withDspaceObject(community) + .withAction(Constants.ADMIN) + .build(); - ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withUser(eperson2).build(); + ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson2, null) + .withDspaceObject(community) + .withAction(Constants.ADD) + .build(); - ResourcePolicy secondResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.ADD) - .withUser(eperson2).build(); + ResourcePolicy secondResourcePolicyOfEPerson2 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson2, null) + .withDspaceObject(community2) + .withAction(Constants.ADD) + .build(); context.restoreAuthSystemState(); @@ -592,28 +591,32 @@ public void findResourcePoliciesOfOneResourcePaginationTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADMIN) - .withUser(eperson1).build(); + ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson1, null) + .withDspaceObject(community) + .withAction(Constants.ADMIN) + .build(); - ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson2, null) + .withDspaceObject(community) + .withAction(Constants.ADD) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); - ResourcePolicy firstResourcePolicyOfEPerson3 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.DELETE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson3).build(); + ResourcePolicy firstResourcePolicyOfEPerson3 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson3, null) + .withDspaceObject(community) + .withAction(Constants.DELETE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); - ResourcePolicy firstResourcePolicyOfEPerson4 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy firstResourcePolicyOfEPerson4 = ResourcePolicyBuilder + .createResourcePolicy(context, eperson4, null) .withDspaceObject(community) .withAction(Constants.WRITE) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson4).build(); + .build(); ResourcePolicy resourcePolicyAnonymous = authorizeService .findByTypeGroupAction(context, community, EPersonServiceFactory.getInstance() @@ -701,10 +704,9 @@ public void findResoucesPoliciesByResourceUuidUnAuthenticatedTest() throws Excep Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1).build(); + .withAction(Constants.READ).build(); context.restoreAuthSystemState(); @@ -742,17 +744,15 @@ public void findResourcesPoliciesByResourceUuidForbiddenTest() throws Exception Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); - ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) .withAction(Constants.REMOVE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson1).build(); + .withPolicyType(ResourcePolicy.TYPE_CUSTOM).build(); - ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context, eperson2, null) .withDspaceObject(community2) .withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + .withPolicyType(ResourcePolicy.TYPE_CUSTOM).build(); context.restoreAuthSystemState(); @@ -791,25 +791,22 @@ public void findResourcePoliciesByGroupUuidTest() throws Exception { .withName("My collection") .build(); - ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withAction(Constants.ADD).build(); - ResourcePolicy secondResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy secondResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.READ) - .withGroup(group1).build(); + .withAction(Constants.READ).build(); - ResourcePolicy collectionResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(collection) - .withAction(Constants.WRITE) - .withGroup(group1).build(); + ResourcePolicy collectionResourcePolicyOfGroup1 = ResourcePolicyBuilder + .createResourcePolicy(context, null, group1) + .withDspaceObject(collection) + .withAction(Constants.WRITE).build(); - ResourcePolicy firstResourcePolicyOfGroup2 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy firstResourcePolicyOfGroup2 = ResourcePolicyBuilder.createResourcePolicy(context, null, group2) .withDspaceObject(community2) - .withAction(Constants.ADD) - .withGroup(group2).build(); + .withAction(Constants.ADD).build(); context.restoreAuthSystemState(); @@ -857,15 +854,13 @@ public void findResourcePoliciesByGroupUuidAndResourceUuidTest() throws Exceptio Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My second community").build(); - ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withAction(Constants.ADD).build(); - ResourcePolicy secondResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy secondResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community2) - .withAction(Constants.WRITE) - .withGroup(group1).build(); + .withAction(Constants.WRITE).build(); context.restoreAuthSystemState(); @@ -913,10 +908,9 @@ public void findResoucesPoliciesByGroupUuidUnAuthenticatedTest() throws Exceptio Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withAction(Constants.ADD).build(); context.restoreAuthSystemState(); @@ -953,10 +947,9 @@ public void findResourcesPoliciesByGroupUuidForbiddenTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community).withAction(Constants.WRITE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withGroup(group1).build(); + .withPolicyType(ResourcePolicy.TYPE_CUSTOM).build(); context.restoreAuthSystemState(); @@ -981,10 +974,10 @@ public void findResourcesPoliciesByGroupAnonymousTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community).withAction(Constants.WRITE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withGroup(groupAnonymous).build(); + ResourcePolicy resourcePolicyOfGroup1 = ResourcePolicyBuilder + .createResourcePolicy(context, null, groupAnonymous) + .withDspaceObject(community).withAction(Constants.WRITE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM).build(); context.restoreAuthSystemState(); @@ -1143,9 +1136,8 @@ public void deleteOne() throws Exception { .withName("My community") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) - .withUser(eperson1) .withAction(Constants.ADMIN) .build(); @@ -1170,11 +1162,10 @@ public void deleteOneUnAuthenticatedTest() throws Exception { .withPassword("qwerty01") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(community) .withAction(Constants.DELETE) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson1) .build(); context.restoreAuthSystemState(); @@ -1200,10 +1191,9 @@ public void deleteOneForbiddenTest() throws Exception { Collection collection = CollectionBuilder.createCollection(context, community) .withName("My collection").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withDspaceObject(collection) - .withAction(Constants.ADD) - .withUser(eperson1).build(); + .withAction(Constants.ADD).build(); context.restoreAuthSystemState(); @@ -1253,10 +1243,10 @@ public void patchReplaceStartDateTest() throws Exception { Date data = calendar.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withStartDate(data) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1323,10 +1313,10 @@ public void patchReplaceEndDateTest() throws Exception { Date date = calendar.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withEndDate(date) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1385,10 +1375,10 @@ public void patchAddStartDateTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1446,13 +1436,12 @@ public void patchAddEndDateTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context,null, + EPersonServiceFactory.getInstance().getGroupService() + .findByName(context, Group.ANONYMOUS) + ) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup( - EPersonServiceFactory.getInstance().getGroupService() - .findByName(context, - Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1513,10 +1502,10 @@ public void patchRemoveStartDateTest() throws Exception { Date data = calendar.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withStartDate(data) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1574,10 +1563,10 @@ public void patchReplaceStartDateBadRequestTest() throws Exception { Date date = calendar.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withStartDate(date) .withDescription("my description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) @@ -1625,7 +1614,7 @@ public void patchReplaceStartDateUnAuthenticatedTest() throws Exception { Date date = calendar.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.WRITE) .withDspaceObject(item) .withStartDate(date) @@ -1687,10 +1676,10 @@ public void patchReplaceStartDateForbiddenTest() throws Exception { Date date = calendar.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(item) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withStartDate(date) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1793,10 +1782,9 @@ public void patchReplaceEndDateBeforeStartDateTest() throws Exception { Date endDate = calendarEndDate.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson1, null) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withUser(eperson1) .withStartDate(startDate) .withEndDate(endDate) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) @@ -1851,10 +1839,10 @@ public void patchReplaceDescriptionTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withDescription("my description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1903,10 +1891,10 @@ public void patchAddDescriptionTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(item) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1954,11 +1942,11 @@ public void patchRemoveDescriptionTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(item) .withDescription("my description") - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -1996,7 +1984,7 @@ public void patchReplaceDescriptionUnAuthenticatedTest() throws Exception { Item item = ItemBuilder.createItem(context, collection).build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.WRITE) .withDspaceObject(item) .withDescription("My Description") @@ -2042,10 +2030,10 @@ public void patchReplaceDescriptionForbiddenTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(item) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2098,10 +2086,10 @@ public void patchReplaceDescriptionBadRequestTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withDescription("my description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2146,10 +2134,10 @@ public void patchReplaceNameTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withName("My name") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2197,10 +2185,10 @@ public void patchReplaceNameBadRequestTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withName("My name") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2245,10 +2233,10 @@ public void patchAddNameTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2290,10 +2278,9 @@ public void patchAddActionTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(myItem) - .withUser(eperson) .withName("My Name") .build(); @@ -2334,10 +2321,9 @@ public void patchReplaceActionTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(myItem) - .withUser(eperson) .withName("My name") .withPolicyType(ResourcePolicy.TYPE_SUBMISSION) .build(); @@ -2374,7 +2360,7 @@ public void patchReplaceActionUnauthenticatedTest() throws Exception { Item item = ItemBuilder.createItem(context, collection).build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.WRITE) .withDspaceObject(item) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) @@ -2413,10 +2399,9 @@ public void patchReplaceActionForbiddenTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(item) - .withUser(eperson) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2464,10 +2449,9 @@ public void patchReplaceActionUnprocessableEntityTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withUser(eperson) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2500,10 +2484,9 @@ public void patchAddPolicyTypeTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(myItem) - .withUser(eperson) .withName("My Name") .build(); @@ -2545,10 +2528,9 @@ public void patchRemovePolicyTypeTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(myItem) - .withUser(eperson) .withName("My Name") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2590,10 +2572,9 @@ public void patchReplacePolicyTypeTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(myItem) - .withUser(eperson) .withName("My name") .withPolicyType(ResourcePolicy.TYPE_SUBMISSION) .build(); @@ -2632,7 +2613,7 @@ public void patchReplacePolicyTypeUnauthenticatedTest() throws Exception { Item item = ItemBuilder.createItem(context, collection).build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.WRITE) .withDspaceObject(item) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) @@ -2672,10 +2653,9 @@ public void patchReplacePolicyTypeForbiddenTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(item) - .withUser(eperson) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2722,10 +2702,9 @@ public void patchReplacePolicyTypeBadRequestTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.READ) .withDspaceObject(publicItem1) - .withUser(eperson) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2763,11 +2742,11 @@ public void patchAddNameBadRequestTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(myItem) .withName("My name") - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2811,10 +2790,10 @@ public void patchRemoveNameTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withName("My Name") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2860,10 +2839,10 @@ public void patchRemoveNameForbiddenTest() throws Exception { .withTitle("Public item") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withName("My Name") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -2923,12 +2902,12 @@ public void patchSuccessfulMultipleOperationsTest() throws Exception { Date endDate = calendarEndDate.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) .withStartDate(startDate) .withEndDate(endDate) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3013,12 +2992,12 @@ public void patchWithMultipleOperationsFailTest() throws Exception { Date endDate = calendarEndDate.getTime(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, + EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withAction(Constants.READ) .withDspaceObject(publicItem1) .withName("My Name") .withEndDate(endDate) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3092,25 +3071,21 @@ public void findResourcePoliciesByGroupUuidPaginationTest() throws Exception { .withName("My collection") .build(); - ResourcePolicy rpCommunityADD = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rpCommunityADD = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withAction(Constants.ADD).build(); - ResourcePolicy rpCommunityREAD = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rpCommunityREAD = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.READ) - .withGroup(group1).build(); + .withAction(Constants.READ).build(); - ResourcePolicy rpCommunity2READ = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rpCommunity2READ = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community2) - .withAction(Constants.READ) - .withGroup(group1).build(); + .withAction(Constants.READ).build(); - ResourcePolicy rpCollectionWRITE = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rpCollectionWRITE = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(collection) - .withAction(Constants.WRITE) - .withGroup(group1).build(); + .withAction(Constants.WRITE).build(); context.restoreAuthSystemState(); @@ -3213,10 +3188,9 @@ public void patchReplaceEPersonAdminTest() throws Exception { .withName("Collection 1") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.ADD) .withDspaceObject(col) - .withUser(eperson) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3260,10 +3234,9 @@ public void patchReplaceEPersonForbiddenTest() throws Exception { .withName("Collection 1") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.ADD) .withDspaceObject(col) - .withUser(eperson) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3308,10 +3281,9 @@ public void patchReplaceEPersonUnauthorizedTest() throws Exception { .withName("Collection 1") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.ADD) .withDspaceObject(col) - .withUser(eperson) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3358,10 +3330,9 @@ public void patchReplaceGroupAdminTest() throws Exception { .withName("Collection 1") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, originGroup) .withAction(Constants.ADD) .withDspaceObject(col) - .withGroup(originGroup) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3408,10 +3379,9 @@ public void patchReplaceGroupForbiddenTest() throws Exception { .withName("Collection 1") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, originGroup) .withAction(Constants.ADD) .withDspaceObject(col) - .withGroup(originGroup) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3459,10 +3429,9 @@ public void patchReplaceGroupUnauthorizedTest() throws Exception { .withName("Collection 1") .build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context, null, originGroup) .withAction(Constants.ADD) .withDspaceObject(col) - .withGroup(originGroup) .withDescription("My Description") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); @@ -3501,10 +3470,9 @@ public void updateResourcePolicyOfEPersonToGroupTest() throws Exception { .withName("My community") .build(); - ResourcePolicy resourcePolicyOfEPerson = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfEPerson = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(community) .withAction(Constants.READ) - .withUser(eperson) .build(); context.restoreAuthSystemState(); @@ -3534,10 +3502,9 @@ public void updateResourcePolicyOfGroupToEPersonTest() throws Exception { .withName("My community") .build(); - ResourcePolicy resourcePolicyOfGroup = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfGroup = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group).build(); + .withAction(Constants.ADD).build(); context.restoreAuthSystemState(); @@ -3597,10 +3564,9 @@ public void updateResourcePolicyOfGroupWithEmptyTest() throws Exception { .build(); - ResourcePolicy resourcePolicyOfGroup = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfGroup = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group).build(); + .withAction(Constants.ADD).build(); context.restoreAuthSystemState(); String tokenAdmin = getAuthToken(admin.getEmail(), password); @@ -3622,10 +3588,9 @@ public void updateResourcePolicyOfGroupWithMultipleGroupsTest() throws Exception .withName("My community") .build(); - ResourcePolicy resourcePolicyOfGroup = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy resourcePolicyOfGroup = ResourcePolicyBuilder.createResourcePolicy(context, null, group1) .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withAction(Constants.ADD).build(); context.restoreAuthSystemState(); String tokenAdmin = getAuthToken(admin.getEmail(), password); @@ -3642,10 +3607,9 @@ public void updateResourcePolicyOfEPersonWithEmptyTest() throws Exception { Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy rpOfEPerson = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rpOfEPerson = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(community) .withAction(Constants.READ) - .withUser(eperson) .build(); context.restoreAuthSystemState(); @@ -3671,10 +3635,9 @@ public void updateResourcePolicyOfEPersonWithMultipleEPersonsTest() throws Excep Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy rpOfEPerson = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rpOfEPerson = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(community) .withAction(Constants.READ) - .withUser(eperson) .build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java index 5544ecdb032b..613075fe8f3a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java @@ -205,10 +205,9 @@ public void usagereport_loggedInUserReadRights() throws Exception { // ** WHEN ** context.turnOffAuthorisationSystem(); authorizeService.removeAllPolicies(context, itemNotVisitedWithBitstreams); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemNotVisitedWithBitstreams) - .withAction(Constants.READ) - .withUser(eperson).build(); + .withAction(Constants.READ).build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) .withEmail("eperson1@mail.com") @@ -239,10 +238,9 @@ public void usagereport_loggedInUserReadRights_and_usage_statistics_admin_is_fal configurationService.setProperty("usage-statistics.authorization.admin.usage", false); context.turnOffAuthorisationSystem(); authorizeService.removeAllPolicies(context, itemNotVisitedWithBitstreams); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemNotVisitedWithBitstreams) - .withAction(Constants.READ) - .withUser(eperson).build(); + .withAction(Constants.READ).build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) .withEmail("eperson1@mail.com") @@ -1150,10 +1148,9 @@ public void usagereportSearch_loggedInUserReadRights() throws Exception { // ** WHEN ** context.turnOffAuthorisationSystem(); authorizeService.removeAllPolicies(context, itemNotVisitedWithBitstreams); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemNotVisitedWithBitstreams) - .withAction(Constants.READ) - .withUser(eperson).build(); + .withAction(Constants.READ).build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) .withEmail("eperson1@mail.com") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 8b2f3f093a37..9c69e4715d77 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -121,6 +121,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration private Group embargoedGroup1; private Group embargoedGroup2; private Group anonymousGroup; + private Group adminGroup; @Before @Override @@ -144,6 +145,7 @@ public void setUp() throws Exception { .build(); anonymousGroup = EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS); + adminGroup = EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ADMIN); context.restoreAuthSystemState(); } @@ -6717,13 +6719,10 @@ public void patchAccesConditionDiscoverableTest() throws Exception { .build(); witem.getItem().setDiscoverable(false); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) - .withPolicyType(TYPE_CUSTOM) - .withName("administrator") +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -6777,7 +6776,7 @@ public void patchAccesConditionDiscoverableWrongValueTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -6824,7 +6823,7 @@ public void patcDiscoverableWithAccesConditionConfigurationDiscoverableDisabledT .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -6877,7 +6876,7 @@ public void patchAddAccesConditionTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -6935,7 +6934,7 @@ public void patchAddNotSupportedAccesConditionTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -7050,13 +7049,13 @@ public void patchRemoveAllAccesConditionsTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("administrator") @@ -7110,16 +7109,13 @@ public void patchRemoveSpecificAccesConditionsTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) - .withPolicyType(TYPE_CUSTOM) - .withName("administrator") +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .build(); Calendar calendar = Calendar.getInstance(); @@ -7130,7 +7126,7 @@ public void patchRemoveSpecificAccesConditionsTest() throws Exception { Date data = calendar.getTime(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, embargoedGroup1) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("embargoed") @@ -7192,16 +7188,13 @@ public void patchRemoveFirstAccesConditionsTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) - .withPolicyType(TYPE_CUSTOM) - .withName("administrator") +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .build(); context.restoreAuthSystemState(); @@ -7255,16 +7248,13 @@ public void patchRemoveAccesConditionsIdxNotSupportedTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) - .withPolicyType(TYPE_CUSTOM) - .withName("administrator") +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .build(); context.restoreAuthSystemState(); @@ -7320,16 +7310,13 @@ public void patchRemoveAccesConditionsUnprocessableEntityTest() throws Exception .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) - .withPolicyType(TYPE_CUSTOM) - .withName("administrator") +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .build(); context.restoreAuthSystemState(); @@ -7385,16 +7372,13 @@ public void patchReplaceAccesConditionTest() throws Exception { .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) - .withPolicyType(TYPE_CUSTOM) - .withName("administrator") +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .build(); context.restoreAuthSystemState(); @@ -7455,7 +7439,7 @@ public void patchReplaceAccesConditionsUpdateEmbargoStartDateTest() throws Excep .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -7469,7 +7453,7 @@ public void patchReplaceAccesConditionsUpdateEmbargoStartDateTest() throws Excep Date data = calendar.getTime(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, embargoedGroup1) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("embargo") @@ -7535,7 +7519,7 @@ public void patchReplaceAccesConditionsFromOpenaccessToAdministratorTest() throw .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CCLicenseFeatureRestIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CCLicenseFeatureRestIT.java index 38fc9a06fd8d..be8851cb5c06 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CCLicenseFeatureRestIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CCLicenseFeatureRestIT.java @@ -202,8 +202,10 @@ public void checkAuthorizationAsItemAdminTest() throws Exception { Community com = CommunityBuilder.createCommunity(context).withName("A community").build(); Collection col = CollectionBuilder.createCollection(context, com).withName("A collection").build(); Item item = ItemBuilder.createItem(context, col).withTitle("Item to withdraw").build(); - ResourcePolicy resource = ResourcePolicyBuilder.createResourcePolicy(context).withAction(Constants.ADMIN) - .withUser(eperson).withDspaceObject(item).build(); + ResourcePolicy resource = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withAction(Constants.ADMIN) + .withDspaceObject(item) + .build(); context.restoreAuthSystemState(); ItemRest itemRest = itemConverter.convert(item, Projection.DEFAULT); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageBitstreamBundlesFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageBitstreamBundlesFeatureIT.java index 6e29c5b9494b..8d900bfd7c0a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageBitstreamBundlesFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageBitstreamBundlesFeatureIT.java @@ -216,9 +216,8 @@ public void checkCanCreateVersionsFeatureTest() throws Exception { @SuppressWarnings("unchecked") public void itemAdminSetPropertyCreateBitstreamToFalseTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withAction(Constants.ADMIN) - .withUser(userA) .withDspaceObject(itemA).build(); configurationService.setProperty("core.authorization.item-admin.create-bitstream", false); @@ -260,9 +259,8 @@ public void itemAdminSetPropertyCreateBitstreamToFalseTest() throws Exception { @SuppressWarnings("unchecked") public void itemAdminSetPropertyDeleteBitstreamToFalseTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withAction(Constants.ADMIN) - .withUser(userA) .withDspaceObject(itemA).build(); configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false); @@ -304,9 +302,8 @@ public void itemAdminSetPropertyDeleteBitstreamToFalseTest() throws Exception { @SuppressWarnings("unchecked") public void itemAdminSetPropertyCollectionAdminCreateBitstreamToFalseTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withAction(Constants.ADMIN) - .withUser(userA) .withDspaceObject(itemA).build(); configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false); @@ -365,9 +362,8 @@ public void itemAdminSetPropertyCollectionAdminCreateBitstreamToFalseTest() thro @SuppressWarnings("unchecked") public void itemAdminSetPropertyCollectionAdminDeleteBitstreamToFalseTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withAction(Constants.ADMIN) - .withUser(userA) .withDspaceObject(itemA).build(); configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false); @@ -426,9 +422,8 @@ public void itemAdminSetPropertyCollectionAdminDeleteBitstreamToFalseTest() thro @SuppressWarnings("unchecked") public void itemAdminSetPropertyCommunityAdminCreateBitstreamToFalseTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withAction(Constants.ADMIN) - .withUser(userA) .withDspaceObject(itemA).build(); configurationService.setProperty("core.authorization.community-admin.item.create-bitstream", false); @@ -487,9 +482,8 @@ public void itemAdminSetPropertyCommunityAdminCreateBitstreamToFalseTest() throw @SuppressWarnings("unchecked") public void itemAdminSetPropertyCommunityAdminDeleteBitstreamToFalseTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withAction(Constants.ADMIN) - .withUser(userA) .withDspaceObject(itemA).build(); configurationService.setProperty("core.authorization.community-admin.item.delete-bitstream", false); @@ -544,4 +538,4 @@ public void itemAdminSetPropertyCommunityAdminDeleteBitstreamToFalseTest() throw } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageMappingsFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageMappingsFeatureIT.java index e85ca857b916..498983ef65b8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageMappingsFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanManageMappingsFeatureIT.java @@ -151,15 +151,13 @@ public void epersonCollectionNotFound() throws Exception { @Test public void addWriteEpersonCollectionSuccess() throws Exception { - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADD) - .withUser(eperson) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); String epersonToken = getAuthToken(eperson.getEmail(), password); @@ -175,10 +173,9 @@ public void addWriteEpersonCollectionSuccess() throws Exception { @Test public void adminEpersonCollectionSuccess() throws Exception { - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); String epersonToken = getAuthToken(eperson.getEmail(), password); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanSubscribeFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanSubscribeFeatureIT.java index 7eb0960566fc..224ec0472cd0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanSubscribeFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/CanSubscribeFeatureIT.java @@ -287,10 +287,9 @@ public void canNotSubscribeCommunityTest() throws Exception { private void setPermissions(DSpaceObject dSpaceObject, Group group, Integer permissions) { try { - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(dSpaceObject) .withAction(permissions) - .withGroup(group) .build(); } catch (SQLException | AuthorizeException sqlException) { log.error(sqlException.getMessage()); @@ -307,4 +306,4 @@ private void cleanUpPermissions(List resourcePolicies) { } } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditItemFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditItemFeatureIT.java index 4bdc7743b571..ec9020a761bb 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditItemFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditItemFeatureIT.java @@ -96,8 +96,7 @@ public void testNoRights() throws Exception { @Test public void testDirectEPersonWritePolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA1X) .withAction(Constants.WRITE) .build(); @@ -108,8 +107,7 @@ public void testDirectEPersonWritePolicy() throws Exception { @Test public void testDirectGroupWritePolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(itemA1X) .withAction(Constants.WRITE) .build(); @@ -120,8 +118,7 @@ public void testDirectGroupWritePolicy() throws Exception { @Test public void testDirectEPersonAdminPolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA1X) .withAction(Constants.ADMIN) .build(); @@ -132,8 +129,7 @@ public void testDirectEPersonAdminPolicy() throws Exception { @Test public void testDirectGroupAdminPolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(itemA1X) .withAction(Constants.ADMIN) .build(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java index 1d3b5b051605..8ddd24348c8c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java @@ -148,25 +148,21 @@ public void setUp() throws Exception { .withName("item1AdminGroup") .addMember(item1Admin) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, item1AdminGroup) .withDspaceObject(item1) .withAction(Constants.ADMIN) - .withGroup(item1AdminGroup) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, communityAWriter, null) .withDspaceObject(communityA) .withAction(Constants.WRITE) - .withUser(communityAWriter) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, collectionXWriter, null) .withDspaceObject(collectionX) .withAction(Constants.WRITE) - .withUser(collectionXWriter) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1Writer, null) .withDspaceObject(item1) .withAction(Constants.WRITE) - .withUser(item1Writer) .build(); communityB = CommunityBuilder.createCommunity(context) @@ -798,10 +794,9 @@ public void testCanMoveAdmin() throws Exception { // grant item 1 admin REMOVE permissions on the item’s owning collection // verify item 1 admin has this feature on item 1 context.turnOffAuthorisationSystem(); - ResourcePolicy removePermission = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy removePermission = ResourcePolicyBuilder.createResourcePolicy(context, item1Writer, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(item1Writer) .build(); context.restoreAuthSystemState(); @@ -820,10 +815,9 @@ public void testCanMoveWriter() throws Exception { // grant item 1 write REMOVE permissions on the item’s owning collection context.turnOffAuthorisationSystem(); - ResourcePolicy removePermission = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy removePermission = ResourcePolicyBuilder.createResourcePolicy(context, item1Writer, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(item1Writer) .build(); context.restoreAuthSystemState(); @@ -901,10 +895,9 @@ public void testCanDeleteAdmin() throws Exception { .withEmail("communityAAAdmin@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, communityAAAdmin, null) .withDspaceObject(communityAA) .withAction(Constants.ADMIN) - .withUser(communityAAAdmin) .build(); context.restoreAuthSystemState(); String communityAAAdminToken = getAuthToken(communityAAAdmin.getEmail(), password); @@ -1082,10 +1075,9 @@ public void testCanDeleteAdminParent() throws Exception { .withEmail("communityAAAdmin@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, communityAAAdmin, null) .withDspaceObject(communityA) .withAction(Constants.REMOVE) - .withUser(communityAAAdmin) .build(); context.restoreAuthSystemState(); String communityAAAdminToken = getAuthToken(communityAAAdmin.getEmail(), password); @@ -1098,10 +1090,9 @@ public void testCanDeleteAdminParent() throws Exception { // Grant REMOVE permissions on community AA for collection X admin context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, collectionXAdmin, null) .withDspaceObject(communityAA) .withAction(Constants.REMOVE) - .withUser(collectionXAdmin) .build(); context.restoreAuthSystemState(); // verify collection X admin has this feature on collection X @@ -1113,10 +1104,9 @@ public void testCanDeleteAdminParent() throws Exception { // Grant REMOVE permissions on collection X for item 1 admin context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1Admin, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(item1Admin) .build(); context.restoreAuthSystemState(); // verify item 1 admin has this feature on item 1 @@ -1143,10 +1133,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("communityADeleter@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, communityADeleter, null) .withDspaceObject(communityA) .withAction(Constants.DELETE) - .withUser(communityADeleter) .build(); context.restoreAuthSystemState(); String communityADeleterToken = getAuthToken(communityADeleter.getEmail(), password); @@ -1171,10 +1160,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("communityARemover@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, communityARemover, null) .withDspaceObject(communityA) .withAction(Constants.REMOVE) - .withUser(communityARemover) .build(); context.restoreAuthSystemState(); String communityARemoverToken = getAuthToken(communityARemover.getEmail(), password); @@ -1204,10 +1192,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("communityAARemover@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, communityAARemover, null) .withDspaceObject(communityAA) .withAction(Constants.REMOVE) - .withUser(communityAARemover) .build(); context.restoreAuthSystemState(); String communityAARemoverToken = getAuthToken(communityAARemover.getEmail(), password); @@ -1237,10 +1224,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("communityXRemover@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, collectionXRemover, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(collectionXRemover) .build(); context.restoreAuthSystemState(); String collectionXRemoverToken = getAuthToken(collectionXRemover.getEmail(), password); @@ -1258,10 +1244,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("item1Deleter@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1Deleter, null) .withDspaceObject(item1) .withAction(Constants.DELETE) - .withUser(item1Deleter) .build(); context.restoreAuthSystemState(); String item1DeleterToken = getAuthToken(item1Deleter.getEmail(), password); @@ -1279,15 +1264,13 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("collectionXDeleter@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, collectionXRemoverItem1Deleter, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(collectionXRemoverItem1Deleter) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, collectionXRemoverItem1Deleter, null) .withDspaceObject(item1) .withAction(Constants.DELETE) - .withUser(collectionXRemoverItem1Deleter) .build(); context.restoreAuthSystemState(); String collectionXRemoverItem1DeleterToken = getAuthToken(collectionXRemoverItem1Deleter.getEmail(), password); @@ -1320,10 +1303,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("item1Remover@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1Remover, null) .withDspaceObject(item1) .withAction(Constants.REMOVE) - .withUser(item1Remover) .build(); context.restoreAuthSystemState(); String item1RemoverToken = getAuthToken(item1Remover.getEmail(), password); @@ -1353,10 +1335,9 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("bundle1Remover@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1Remover, null) .withDspaceObject(bundle1) .withAction(Constants.REMOVE) - .withUser(bundle1Remover) .build(); context.restoreAuthSystemState(); String bundle1RemoverToken = getAuthToken(bundle1Remover.getEmail(), password); @@ -1375,15 +1356,13 @@ public void testCanDeleteMinimalPermissions() throws Exception { .withEmail("bundle1item1Remover@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1item1Remover, null) .withDspaceObject(bundle1) .withAction(Constants.REMOVE) - .withUser(bundle1item1Remover) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1item1Remover, null) .withDspaceObject(item1) .withAction(Constants.REMOVE) - .withUser(bundle1item1Remover) .build(); context.restoreAuthSystemState(); String bundle1item1RemoverToken = getAuthToken(bundle1item1Remover.getEmail(), password); @@ -1553,10 +1532,9 @@ public void testCanCreateBitstreamWriter() throws Exception { .withEmail("bundle1Writer@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1Writer, null) .withDspaceObject(bundle1) .withAction(Constants.WRITE) - .withUser(bundle1Writer) .build(); context.restoreAuthSystemState(); String bundle1WriterToken = getAuthToken(bundle1Writer.getEmail(), password); @@ -1574,10 +1552,9 @@ public void testCanCreateBitstreamWriter() throws Exception { .withEmail("bundle1Adder@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1Adder, null) .withDspaceObject(bundle1) .withAction(Constants.ADD) - .withUser(bundle1Adder) .build(); context.restoreAuthSystemState(); String bundle1AdderToken = getAuthToken(bundle1Adder.getEmail(), password); @@ -1596,25 +1573,21 @@ public void testCanCreateBitstreamWriter() throws Exception { .withEmail("bundle1WriterAdder@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1WriterAdder, null) .withDspaceObject(bundle1) .withAction(Constants.ADD) - .withUser(bundle1WriterAdder) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1WriterAdder, null) .withDspaceObject(bundle1) .withAction(Constants.WRITE) - .withUser(bundle1WriterAdder) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1WriterAdder, null) .withDspaceObject(item1) .withAction(Constants.ADD) - .withUser(bundle1WriterAdder) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, bundle1WriterAdder, null) .withDspaceObject(item1) .withAction(Constants.WRITE) - .withUser(bundle1WriterAdder) .build(); context.restoreAuthSystemState(); String bundle1WriterAdderToken = getAuthToken(bundle1WriterAdder.getEmail(), password); @@ -1666,15 +1639,13 @@ public void testCanCreateBundleWriter() throws Exception { .withEmail("item1AdderWriter@my.edu") .withPassword(password) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1AdderWriter, null) .withDspaceObject(item1) .withAction(Constants.ADD) - .withUser(item1AdderWriter) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1AdderWriter, null) .withDspaceObject(item1) .withAction(Constants.WRITE) - .withUser(item1AdderWriter) .build(); context.restoreAuthSystemState(); String item1AdderWriterToken = getAuthToken(item1AdderWriter.getEmail(), password); @@ -1685,4 +1656,4 @@ public void testCanCreateBundleWriter() throws Exception { .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageAdminGroupFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageAdminGroupFeatureIT.java index cadb3e1ebba9..49082a1613f1 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageAdminGroupFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageAdminGroupFeatureIT.java @@ -123,10 +123,9 @@ public void anonymousCommunityTestNotFound() throws Exception { @Test public void collectionAdminCollectionTestSuccess() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); @@ -144,10 +143,9 @@ public void collectionAdminCollectionTestSuccess() throws Exception { @Test public void collectionAdminCommunityTestNotFound() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageSubmitterGroupFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageSubmitterGroupFeatureIT.java index 42ad0c11055e..fbf20337ebbe 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageSubmitterGroupFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageSubmitterGroupFeatureIT.java @@ -123,10 +123,9 @@ public void anonymousCommunityTestNotFound() throws Exception { @Test public void collectionAdminCollectionTestSuccess() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); @@ -144,10 +143,9 @@ public void collectionAdminCollectionTestSuccess() throws Exception { @Test public void collectionAdminCommunityTestNotFound() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageTemplateItemFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageTemplateItemFeatureIT.java index 613dfe11513e..97e32e55be5b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageTemplateItemFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageTemplateItemFeatureIT.java @@ -123,10 +123,9 @@ public void anonymousCommunityTestNotFound() throws Exception { @Test public void collectionAdminCollectionTestSuccess() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); @@ -144,10 +143,9 @@ public void collectionAdminCollectionTestSuccess() throws Exception { @Test public void collectionAdminCommunityTestNotFound() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageWorkflowGroupFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageWorkflowGroupFeatureIT.java index ddf0bbac5c4d..411aa76a2352 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageWorkflowGroupFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageWorkflowGroupFeatureIT.java @@ -123,10 +123,9 @@ public void anonymousCommunityTestNotFound() throws Exception { @Test public void collectionAdminCollectionTestSuccess() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); @@ -144,10 +143,9 @@ public void collectionAdminCollectionTestSuccess() throws Exception { @Test public void collectionAdminCommunityTestNotFound() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA) .withAction(Constants.ADMIN) - .withUser(eperson) .build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/SubmitFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/SubmitFeatureIT.java index bba45ecd229f..2613cf8ad205 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/SubmitFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/SubmitFeatureIT.java @@ -89,8 +89,7 @@ public void testNoRights() throws Exception { @Test public void testDirectEPersonAddPolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA1) .withAction(Constants.ADD) .build(); @@ -99,8 +98,7 @@ public void testDirectEPersonAddPolicy() throws Exception { @Test public void testDirectGroupAddPolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(collectionA1) .withAction(Constants.ADD) .build(); @@ -109,8 +107,7 @@ public void testDirectGroupAddPolicy() throws Exception { @Test public void testDirectEPersonAdminPolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA1) .withAction(Constants.ADMIN) .build(); @@ -119,8 +116,7 @@ public void testDirectEPersonAdminPolicy() throws Exception { @Test public void testDirectGroupAdminPolicy() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(collectionA1) .withAction(Constants.ADMIN) .build(); @@ -199,8 +195,7 @@ public void testNoRightsOnCollection() throws Exception { @Test public void testDirectEPersonAddPolicyOnCollection() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA1) .withAction(Constants.ADD) .build(); @@ -210,8 +205,7 @@ public void testDirectEPersonAddPolicyOnCollection() throws Exception { @Test public void testDirectGroupAddPolicyOnCollection() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(collectionA1) .withAction(Constants.ADD) .build(); @@ -221,8 +215,7 @@ public void testDirectGroupAddPolicyOnCollection() throws Exception { @Test public void testDirectEPersonAdminPolicyOnCollection() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(collectionA1) .withAction(Constants.ADMIN) .build(); @@ -232,8 +225,7 @@ public void testDirectEPersonAdminPolicyOnCollection() throws Exception { @Test public void testDirectGroupAdminPolicyOnCollection() throws Exception { - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) - .withGroup(group) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, null, group) .withDspaceObject(collectionA1) .withAction(Constants.ADMIN) .build(); From b923e13726a744f36d628b015ae30c89582ab0ad Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 29 Sep 2023 10:36:43 +0200 Subject: [PATCH 002/479] 106812: Fix and add new tests --- .../rest/ResourcePolicyRestRepositoryIT.java | 94 ++++++++++++++++++- .../rest/WorkspaceItemRestRepositoryIT.java | 29 ++++-- 2 files changed, 116 insertions(+), 7 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index e7d2634525d3..5d2a05ab6440 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -998,7 +998,7 @@ public void createWithEPersonTest() throws Exception { AtomicReference idRef = new AtomicReference(); try { Community community = CommunityBuilder.createCommunity(context) - .withName("My commynity") + .withName("My community") .build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) @@ -1045,6 +1045,98 @@ public void createWithEPersonTest() throws Exception { ResourcePolicyBuilder.delete(idRef.get()); } } + @Test + public void createWithGroupTest() throws Exception { + context.turnOffAuthorisationSystem(); + + AtomicReference idRef = new AtomicReference(); + try { + Community community = CommunityBuilder.createCommunity(context) + .withName("My community") + .build(); + + EPerson eperson1 = EPersonBuilder.createEPerson(context) + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); + + Group group1 = GroupBuilder.createGroup(context) + .withName("Group 1") + .addMember(eperson1) + .build(); + + context.restoreAuthSystemState(); + + ObjectMapper mapper = new ObjectMapper(); + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_SUBMISSION); + resourcePolicyRest.setAction(Constants.actionText[Constants.READ]); + + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken) + .perform(post("/api/authz/resourcepolicies") + .content(mapper.writeValueAsBytes(resourcePolicyRest)) + .param("resource", community.getID().toString()) + .param("group", group1.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + String authToken1 = getAuthToken(eperson1.getEmail(), "qwerty01"); + getClient(authToken1).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + } + } + + @Test + public void createWithoutGroupOrPersonTest() throws Exception { + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context) + .withName("My commynity") + .build(); + + + context.restoreAuthSystemState(); + + ObjectMapper mapper = new ObjectMapper(); + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_SUBMISSION); + resourcePolicyRest.setAction(Constants.actionText[Constants.ADMIN]); + + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform(post("/api/authz/resourcepolicies") + .content(mapper.writeValueAsBytes(resourcePolicyRest)) + .param("resource", community.getID().toString()) + .contentType(contentType)) + .andExpect(status().isBadRequest()); + + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", community.getID().toString()) + .param("action", "ADMIN")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/resource"))) + .andExpect(jsonPath("$.page.totalElements", is(0))); + } @Test public void createOneUnAuthenticatedTest() throws Exception { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 9c69e4715d77..c403ec4236a8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -6719,7 +6719,9 @@ public void patchAccesConditionDiscoverableTest() throws Exception { .build(); witem.getItem().setDiscoverable(false); -ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup).withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") .build(); ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) @@ -7116,7 +7118,10 @@ public void patchRemoveSpecificAccesConditionsTest() throws Exception { .build(); ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) - .build(); + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); Calendar calendar = Calendar.getInstance(); @@ -7195,7 +7200,10 @@ public void patchRemoveFirstAccesConditionsTest() throws Exception { .build(); ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) - .build(); + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -7255,7 +7263,10 @@ public void patchRemoveAccesConditionsIdxNotSupportedTest() throws Exception { .build(); ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) - .build(); + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -7317,7 +7328,10 @@ public void patchRemoveAccesConditionsUnprocessableEntityTest() throws Exception .build(); ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) - .build(); + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -7379,7 +7393,10 @@ public void patchReplaceAccesConditionTest() throws Exception { .build(); ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) - .build(); + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); From 1cf13e3bfef7e3d1f5d09ae3086c7245fabd00df Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 29 Sep 2023 11:44:29 +0200 Subject: [PATCH 003/479] 106812: Fix old rest resource policy creations --- .../src/main/java/org/dspace/rest/BitstreamResource.java | 5 +++-- dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java b/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java index 3a6ad859603e..b71bfad592cf 100644 --- a/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java +++ b/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java @@ -729,9 +729,10 @@ static String getMimeType(String name) { private void addPolicyToBitstream(org.dspace.core.Context context, ResourcePolicy policy, org.dspace.content.Bitstream dspaceBitstream) throws SQLException, AuthorizeException { - org.dspace.authorize.ResourcePolicy dspacePolicy = resourcePolicyService.create(context); + org.dspace.authorize.ResourcePolicy dspacePolicy = + resourcePolicyService.create(context, null, + groupService.findByIdOrLegacyId(context, policy.getGroupId())); dspacePolicy.setAction(policy.getActionInt()); - dspacePolicy.setGroup(groupService.findByIdOrLegacyId(context, policy.getGroupId())); dspacePolicy.setdSpaceObject(dspaceBitstream); dspacePolicy.setStartDate(policy.getStartDate()); dspacePolicy.setEndDate(policy.getEndDate()); diff --git a/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java b/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java index 615aacac21cc..761d4dfc8ae3 100644 --- a/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java +++ b/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java @@ -516,9 +516,10 @@ public Bitstream addItemBitstream(@PathParam("item_id") String itemId, InputStre bitstreamsPolicies.remove(policy); } - org.dspace.authorize.ResourcePolicy dspacePolicy = resourcePolicyService.create(context); + org.dspace.authorize.ResourcePolicy dspacePolicy = + resourcePolicyService.create(context, + null, groupService.findByIdOrLegacyId(context, groupId)); dspacePolicy.setAction(org.dspace.core.Constants.READ); - dspacePolicy.setGroup(groupService.findByIdOrLegacyId(context, groupId)); dspacePolicy.setdSpaceObject(dspaceBitstream); if ((year != null) || (month != null) || (day != null)) { Date date = new Date(); From 9880e697b65c2284aea9d626008a96010af664de Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 29 Sep 2023 15:36:53 +0200 Subject: [PATCH 004/479] 106812: Add h2 sql file --- ...28__enforce_group_or_eperson_for_resourcepolicy.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql new file mode 100644 index 000000000000..de40d0415dc6 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql @@ -0,0 +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/ +-- + +ALTER TABLE ResourcePolicy ADD CONSTRAINT resourcepolicy_eperson_and_epersongroup_not_nullobject_chk + CHECK (eperson_id is not null or epersongroup_id is not null) ; From 9b1234fc85728e3def4940d5b25b9e49e3db422a Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 10 Nov 2023 14:05:38 +0100 Subject: [PATCH 005/479] 106812: Remove stray settting of group for an rp --- .../java/org/dspace/app/itemimport/ItemImportServiceImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index a77d854ade26..9532ecdb4708 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1814,7 +1814,6 @@ protected void setPermission(Context c, Group g, int actionID, Bitstream bs) rp.setdSpaceObject(bs); rp.setAction(actionID); - rp.setGroup(g); resourcePolicyService.update(c, rp); } else { From 76658e7c967813e789dd0b5a60ef3c6e9d3d7b0f Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 15 Nov 2023 14:16:40 -0600 Subject: [PATCH 006/479] [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 97ff29e5c583..2fddacc7e4b5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index eff53478ed9d..c8db035d864e 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 8aaad80861f3..c9d8285559c1 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index f425ea7d9086..f118ebddb378 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index c1ac8ac2302c..5688424a1c26 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.1 + 7.6.2-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 + 7.6.2-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index b23db1960cee..b3e903a47b4e 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index c539dac94f5c..98e0253a498d 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 244a661f0c56..900ede5fc507 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 4879d7a29136..8d618bdf5f81 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index fa7d75f82d88..84b53171bf37 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index eaf25a568b68..5819158505fd 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 66c5ea86f301..7797346c51db 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.1 + 7.6.2-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 195623f72dad..564f81593777 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 + 7.6.2-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index f61b27c1c9a0..f9dc6126e60b 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.1 + 7.6.2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 614bb39a1fe7..1c3479fc0f51 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.1 + 7.6.2-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 + 7.6.2-SNAPSHOT jar classes org.dspace dspace-rest - 7.6.1 + 7.6.2-SNAPSHOT war @@ -1030,69 +1030,69 @@ org.dspace dspace-api - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-api test-jar - 7.6.1 + 7.6.2-SNAPSHOT test org.dspace.modules additions - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-sword - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-swordv2 - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-oai - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-services - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6.1 + 7.6.2-SNAPSHOT test org.dspace dspace-rdf - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-iiif - 7.6.1 + 7.6.2-SNAPSHOT org.dspace dspace-server-webapp - 7.6.1 + 7.6.2-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6.1 + 7.6.2-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.1 + dspace-7_x From 722ad0275757b1859b1ebeb95d41b4c2792652ce Mon Sep 17 00:00:00 2001 From: Philipp Rumpf Date: Tue, 14 Nov 2023 09:38:35 +0000 Subject: [PATCH 007/479] CrossRefImport: ignore empty responses rather than generating empty phantom ImportRecords Fixes https://github.com/DSpace/DSpace/issues/9202 . --- .../CrossRefImportMetadataSourceServiceImpl.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java index 7dde330b27ec..71b088ff162b 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java @@ -162,7 +162,9 @@ public List call() throws Exception { Iterator nodes = jsonNode.at("/message/items").iterator(); while (nodes.hasNext()) { JsonNode node = nodes.next(); - results.add(transformSourceRecords(node.toString())); + if (!node.isMissingNode()) { + results.add(transformSourceRecords(node.toString())); + } } return results; } @@ -196,7 +198,9 @@ public List call() throws Exception { String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); JsonNode jsonNode = convertStringJsonToJsonNode(responseString); JsonNode messageNode = jsonNode.at("/message"); - results.add(transformSourceRecords(messageNode.toString())); + if (!messageNode.isMissingNode()) { + results.add(transformSourceRecords(messageNode.toString())); + } return results; } } @@ -250,7 +254,9 @@ public List call() throws Exception { Iterator nodes = jsonNode.at("/message/items").iterator(); while (nodes.hasNext()) { JsonNode node = nodes.next(); - results.add(transformSourceRecords(node.toString())); + if (!node.isMissingNode()) { + results.add(transformSourceRecords(node.toString())); + } } return results; } @@ -333,4 +339,4 @@ public void setUrl(String url) { this.url = url; } -} \ No newline at end of file +} From 21a7f251186f8f450f7bf9187dc6670514d0a51f Mon Sep 17 00:00:00 2001 From: Philipp Rumpf Date: Thu, 16 Nov 2023 14:15:32 +0000 Subject: [PATCH 008/479] CrossRefImportMetadataSourceServiceIT: Test empty responses don't result in ... results. --- .../CrossRefImportMetadataSourceServiceIT.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index 72524709ec65..31c22692f008 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -48,6 +48,24 @@ public class CrossRefImportMetadataSourceServiceIT extends AbstractLiveImportInt @Autowired private CrossRefImportMetadataSourceServiceImpl crossRefServiceImpl; + @Test + public void crossRefImportMetadataGetNoRecordsTest() throws Exception { + context.turnOffAuthorisationSystem(); + CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient(); + CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class); + try { + liveImportClientImpl.setHttpClient(httpClient); + CloseableHttpResponse response = mockResponse("" , 404, "Not Found"); + when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response); + + context.restoreAuthSystemState(); + Collection recordsImported = crossRefServiceImpl.getRecords("test query", 0, 2); + assertEquals(0, recordsImported.size()); + } finally { + liveImportClientImpl.setHttpClient(originalHttpClient); + } + } + @Test public void crossRefImportMetadataGetRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); From c9c57faa21b165784c4c0d99c932b1dc56e30b7d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 22 Nov 2023 14:06:05 -0600 Subject: [PATCH 009/479] Update GitHub action plugin versions. Minor fixes including using built-in Maven caching & fix to CodeCov action (cherry picked from commit 2aae4cd78de318e816591932187049eb135fa60d) --- .github/workflows/build.yml | 22 ++--- .github/workflows/codescan.yml | 2 +- .github/workflows/docker.yml | 84 +++++++++---------- .../workflows/port_merged_pull_request.yml | 4 +- .github/workflows/pull_request_opened.yml | 2 +- 5 files changed, 54 insertions(+), 60 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99c9efe0190f..d6913078e471 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/actions/setup-java - name: Install JDK ${{ matrix.java }} @@ -53,16 +53,7 @@ jobs: with: java-version: ${{ matrix.java }} distribution: 'temurin' - - # https://github.com/actions/cache - - name: Cache Maven dependencies - uses: actions/cache@v3 - with: - # Cache entire ~/.m2/repository - path: ~/.m2/repository - # Cache key is hash of all pom.xml files. Therefore any changes to POMs will invalidate cache - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-maven- + cache: 'maven' # Run parallel Maven builds based on the above 'strategy.matrix' - name: Run Maven ${{ matrix.type }} @@ -96,7 +87,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Download artifacts from previous 'tests' job - name: Download coverage artifacts @@ -108,10 +99,13 @@ jobs: # Retry action: https://github.com/marketplace/actions/retry-action # Codecov action: https://github.com/codecov/codecov-action - name: Upload coverage to Codecov.io - uses: Wandalen/wretry.action@v1.0.36 + uses: Wandalen/wretry.action@v1.3.0 with: action: codecov/codecov-action@v3 - # Try upload 5 times max + # Ensure codecov-action throws an error when it fails to upload + with: | + fail_ci_if_error: true + # Try re-running action 5 times max attempt_limit: 5 # Run again in 30 seconds attempt_delay: 30000 diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml index 9e6dcc0b23af..13bb0d2278ad 100644 --- a/.github/workflows/codescan.yml +++ b/.github/workflows/codescan.yml @@ -35,7 +35,7 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/actions/setup-java - name: Install JDK diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f1ae184fd5c0..c538d324cdea 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -39,7 +39,7 @@ env: jobs: #################################################### # Build/Push the 'dspace/dspace-dependencies' image. - # This image is used by all other jobs. + # This image is used by all other DSpace build jobs. #################################################### dspace-dependencies: # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' @@ -49,21 +49,21 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -72,7 +72,7 @@ jobs: # Get Metadata for docker_build_deps step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-dependencies' image id: meta_build_deps - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: dspace/dspace-dependencies tags: ${{ env.IMAGE_TAGS }} @@ -81,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@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.dependencies @@ -106,21 +106,21 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -128,7 +128,7 @@ jobs: # Get Metadata for docker_build step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace' image id: meta_build - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: dspace/dspace tags: ${{ env.IMAGE_TAGS }} @@ -136,7 +136,7 @@ jobs: - name: Build and push 'dspace' image id: docker_build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile @@ -161,21 +161,21 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -183,7 +183,7 @@ jobs: # 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 - uses: docker/metadata-action@v4 + uses: docker/metadata-action@5 with: images: dspace/dspace tags: ${{ env.IMAGE_TAGS }} @@ -194,7 +194,7 @@ jobs: - name: Build and push 'dspace-test' image id: docker_build_test - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.test @@ -219,21 +219,21 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -241,7 +241,7 @@ jobs: # 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 - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: dspace/dspace-cli tags: ${{ env.IMAGE_TAGS }} @@ -249,7 +249,7 @@ jobs: - name: Build and push 'dspace-cli' image id: docker_build_cli - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.cli @@ -276,17 +276,17 @@ jobs: # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -294,7 +294,7 @@ jobs: # 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 - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: dspace/dspace-solr tags: ${{ env.IMAGE_TAGS }} @@ -302,7 +302,7 @@ jobs: - name: Build and push 'dspace-solr' image id: docker_build_solr - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./dspace/src/main/docker/dspace-solr/Dockerfile @@ -325,21 +325,21 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -347,7 +347,7 @@ jobs: # 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 - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: dspace/dspace-postgres-pgcrypto tags: ${{ env.IMAGE_TAGS }} @@ -355,7 +355,7 @@ jobs: - name: Build and push 'dspace-postgres-pgcrypto' image id: docker_build_postgres - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ @@ -379,21 +379,21 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 # 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 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -401,7 +401,7 @@ jobs: # 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 - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: dspace/dspace-postgres-pgcrypto tags: ${{ env.IMAGE_TAGS }} @@ -412,7 +412,7 @@ jobs: - name: Build and push 'dspace-postgres-pgcrypto-loadsql' image id: docker_build_postgres_loadsql - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/ diff --git a/.github/workflows/port_merged_pull_request.yml b/.github/workflows/port_merged_pull_request.yml index 109835d14d3c..857f22755e49 100644 --- a/.github/workflows/port_merged_pull_request.yml +++ b/.github/workflows/port_merged_pull_request.yml @@ -23,11 +23,11 @@ jobs: if: github.event.pull_request.merged steps: # Checkout code - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # 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 + uses: korthout/backport-action@v2 with: # Trigger based on a "port to [branch]" label on PR # (This label must specify the branch name to port to) diff --git a/.github/workflows/pull_request_opened.yml b/.github/workflows/pull_request_opened.yml index 9b61af72d187..f16e81c9fd25 100644 --- a/.github/workflows/pull_request_opened.yml +++ b/.github/workflows/pull_request_opened.yml @@ -21,4 +21,4 @@ jobs: # 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 + uses: toshimaru/auto-author-assign@v2.0.1 From 68f29c45b6cfadc3ad11e078d4fe7340d5c6f04c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 22 Nov 2023 14:07:05 -0600 Subject: [PATCH 010/479] Minor fixes to Dockerfiles. No longer need 'git'. Use Maven flags to slightly speed up build/install steps. (cherry picked from commit 538833f8a8573e55b49cb28ee6bfbc5330ad6bc4) --- .github/workflows/docker.yml | 2 +- Dockerfile | 5 ++++- Dockerfile.dependencies | 10 ++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c538d324cdea..8be8ac13fe6b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -183,7 +183,7 @@ jobs: # 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 - uses: docker/metadata-action@5 + uses: docker/metadata-action@v5 with: images: dspace/dspace tags: ${{ env.IMAGE_TAGS }} diff --git a/Dockerfile b/Dockerfile index 9c32ecb50cd4..3c536b317629 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,10 @@ USER dspace ADD --chown=dspace . /app/ # Build DSpace (note: this build doesn't include the optional, deprecated "dspace-rest" webapp) # Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small -RUN mvn --no-transfer-progress package && \ +# Maven flags here ensure that we skip building test environment and skip all code verification checks. +# These flags speed up this compilation as much as reasonably possible. +ENV MAVEN_FLAGS="-P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true" +RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ mv /app/dspace/target/${TARGET_DIR}/* /install && \ mvn clean diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index a55b323339dc..6f72ab058536 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -15,11 +15,6 @@ RUN useradd dspace \ && mkdir -p /home/dspace \ && chown -Rv dspace: /home/dspace RUN chown -Rv dspace: /app -# Need git to support buildnumber-maven-plugin, which lets us know what version of DSpace is being run. -RUN apt-get update \ - && apt-get install -y --no-install-recommends git \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Switch to dspace user & run below commands as that user USER dspace @@ -28,7 +23,10 @@ USER dspace ADD --chown=dspace . /app/ # Trigger the installation of all maven dependencies (hide download progress messages) -RUN mvn --no-transfer-progress package +# Maven flags here ensure that we skip final assembly, skip building test environment and skip all code verification checks. +# These flags speed up this installation as much as reasonably possible. +ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true" +RUN mvn --no-transfer-progress install ${MAVEN_FLAGS} # Clear the contents of the /app directory (including all maven builds), so no artifacts remain. # This ensures when dspace:dspace is built, it will use the Maven local cache (~/.m2) for dependencies From 93fc3ce8d09e4fa03055000447f6957c655ecb12 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 22 Nov 2023 15:54:28 -0600 Subject: [PATCH 011/479] Refactor docker.yml to use a separate reusable-docker-build.yml script for each image build. (cherry picked from commit 0e88bfdae7844ea2313238a96975b3d8c36f0a68) --- .github/workflows/docker.yml | 408 ++++---------------- .github/workflows/reusable-docker-build.yml | 217 +++++++++++ 2 files changed, 288 insertions(+), 337 deletions(-) create mode 100644 .github/workflows/reusable-docker-build.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8be8ac13fe6b..2adceaa4d3c3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -3,6 +3,7 @@ name: Docker images # Run this Build for all pushes to 'main' or maintenance branches, or tagged releases. # Also run for PRs to ensure PR doesn't break Docker build process +# NOTE: uses "reusable-docker-build.yml" to actually build each of the Docker images. on: push: branches: @@ -30,11 +31,6 @@ env: # 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 - # 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: #################################################### @@ -44,54 +40,14 @@ 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 - - steps: - # https://github.com/actions/checkout - - name: Checkout codebase - uses: actions/checkout@v4 - - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - - # 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 - id: meta_build_deps - uses: docker/metadata-action@v5 - with: - images: dspace/dspace-dependencies - tags: ${{ env.IMAGE_TAGS }} - flavor: ${{ env.TAGS_FLAVOR }} - - # https://github.com/docker/build-push-action - - name: Build and push 'dspace-dependencies' image - id: docker_build_deps - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile.dependencies - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build_deps.outputs.tags }} - labels: ${{ steps.meta_build_deps.outputs.labels }} + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace-dependencies + image_name: dspace/dspace-dependencies + dockerfile_path: ./Dockerfile.dependencies + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} ####################################### # Build/Push the 'dspace/dspace' image @@ -101,52 +57,18 @@ jobs: 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@v4 - - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - 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 - uses: docker/metadata-action@v5 - with: - images: dspace/dspace - tags: ${{ env.IMAGE_TAGS }} - flavor: ${{ env.TAGS_FLAVOR }} - - - name: Build and push 'dspace' image - id: docker_build - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build.outputs.tags }} - labels: ${{ steps.meta_build.outputs.labels }} + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace + image_name: dspace/dspace + dockerfile_path: ./Dockerfile + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Enable redeploy of sandbox & demo if the branch for this image matches the deployment branch of + # these sites as specified in reusable-docker-build.xml + REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }} + REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} ############################################################# # Build/Push the 'dspace/dspace' image ('-test' tag) @@ -156,55 +78,17 @@ jobs: 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@v4 - - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - 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 - uses: docker/metadata-action@v5 - with: - images: dspace/dspace - tags: ${{ env.IMAGE_TAGS }} - # As this is a test/development image, its tags are all suffixed with "-test". Otherwise, it uses the same - # tagging logic as the primary 'dspace/dspace' image above. - flavor: ${{ env.TAGS_FLAVOR }} - suffix=-test - - - name: Build and push 'dspace-test' image - id: docker_build_test - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile.test - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build_test.outputs.tags }} - labels: ${{ steps.meta_build_test.outputs.labels }} + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace-test + image_name: dspace/dspace + dockerfile_path: ./Dockerfile.test + # As this is a test/development image, its tags are all suffixed with "-test". Otherwise, it uses the same + # tagging logic as the primary 'dspace/dspace' image above. + tags_flavor: suffix=-test + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} ########################################### # Build/Push the 'dspace/dspace-cli' image @@ -214,52 +98,14 @@ jobs: 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@v4 - - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - 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 - uses: docker/metadata-action@v5 - with: - images: dspace/dspace-cli - tags: ${{ env.IMAGE_TAGS }} - flavor: ${{ env.TAGS_FLAVOR }} - - - name: Build and push 'dspace-cli' image - id: docker_build_cli - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile.cli - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build_cli.outputs.tags }} - labels: ${{ steps.meta_build_cli.outputs.labels }} + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace-cli + image_name: dspace/dspace-cli + dockerfile_path: ./Dockerfile.cli + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} ########################################### # Build/Push the 'dspace/dspace-solr' image @@ -267,52 +113,14 @@ jobs: 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@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - 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 - uses: docker/metadata-action@v5 - with: - images: dspace/dspace-solr - tags: ${{ env.IMAGE_TAGS }} - flavor: ${{ env.TAGS_FLAVOR }} - - - name: Build and push 'dspace-solr' image - id: docker_build_solr - uses: docker/build-push-action@v5 - with: - context: . - file: ./dspace/src/main/docker/dspace-solr/Dockerfile - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build_solr.outputs.tags }} - labels: ${{ steps.meta_build_solr.outputs.labels }} + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace-solr + image_name: dspace/dspace-solr + dockerfile_path: ./dspace/src/main/docker/dspace-solr/Dockerfile + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} ########################################################### # Build/Push the 'dspace/dspace-postgres-pgcrypto' image @@ -320,53 +128,16 @@ jobs: 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@v4 - - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - 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 - uses: docker/metadata-action@v5 - with: - images: dspace/dspace-postgres-pgcrypto - tags: ${{ env.IMAGE_TAGS }} - flavor: ${{ env.TAGS_FLAVOR }} - - - name: Build and push 'dspace-postgres-pgcrypto' image - id: docker_build_postgres - uses: docker/build-push-action@v5 - with: - # Must build out of subdirectory to have access to install script for pgcrypto - context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ - dockerfile: Dockerfile - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build_postgres.outputs.tags }} - labels: ${{ steps.meta_build_postgres.outputs.labels }} + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace-postgres-pgcrypto + image_name: dspace/dspace-postgres-pgcrypto + # Must build out of subdirectory to have access to install script for pgcrypto. + # NOTE: this context will build the image based on the Dockerfile in the specified directory + dockerfile_context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} ######################################################################## # Build/Push the 'dspace/dspace-postgres-pgcrypto' image (-loadsql tag) @@ -374,53 +145,16 @@ jobs: 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@v4 - - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - - # 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@v3 - 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 - uses: docker/metadata-action@v5 - with: - images: dspace/dspace-postgres-pgcrypto - tags: ${{ env.IMAGE_TAGS }} - # Suffix all tags with "-loadsql". Otherwise, it uses the same - # tagging logic as the primary 'dspace/dspace-postgres-pgcrypto' image above. - flavor: ${{ env.TAGS_FLAVOR }} - suffix=-loadsql - - - name: Build and push 'dspace-postgres-pgcrypto-loadsql' image - id: docker_build_postgres_loadsql - uses: docker/build-push-action@v5 - with: - # Must build out of subdirectory to have access to install script for pgcrypto - context: ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/ - dockerfile: Dockerfile - platforms: ${{ env.PLATFORMS }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ github.event_name != 'pull_request' }} - # Use tags / labels provided by 'docker/metadata-action' above - tags: ${{ steps.meta_build_postgres_loadsql.outputs.tags }} - labels: ${{ steps.meta_build_postgres_loadsql.outputs.labels }} \ No newline at end of file + uses: ./.github/workflows/reusable-docker-build.yml + with: + build_id: dspace-postgres-pgcrypto-loadsql + image_name: dspace/dspace-postgres-pgcrypto + # Must build out of subdirectory to have access to install script for pgcrypto. + # NOTE: this context will build the image based on the Dockerfile in the specified directory + dockerfile_context: ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/ + # Suffix all tags with "-loadsql". Otherwise, it uses the same + # tagging logic as the primary 'dspace/dspace-postgres-pgcrypto' image above. + tags_flavor: suffix=-loadsql + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml new file mode 100644 index 000000000000..46bdab3b6827 --- /dev/null +++ b/.github/workflows/reusable-docker-build.yml @@ -0,0 +1,217 @@ +# +# DSpace's reusable Docker build/push workflow. +# +# This is used by docker.yml for all Docker image builds +name: Reusable DSpace Docker Build + +on: + workflow_call: + # Possible Inputs to this reusable job + inputs: + # Build name/id for this Docker build. Used for digest storage to avoid digest overlap between builds. + build_id: + required: true + type: string + # Requires the image name to build (e.g dspace/dspace-test) + image_name: + required: true + type: string + # Optionally the path to the Dockerfile to use for the build. (Default is [dockerfile_context]/Dockerfile) + dockerfile_path: + required: false + type: string + # Optionally the context directory to build the Dockerfile within. Defaults to "." (current directory) + dockerfile_context: + required: false + type: string + # If Docker image should have additional tag flavor details (e.g. a suffix), it may be passed in. + tags_flavor: + required: false + type: string + secrets: + # Requires that Docker login info be passed in as secrets. + DOCKER_USERNAME: + required: true + DOCKER_ACCESS_TOKEN: + required: true + # These URL secrets are optional. When specified & branch checks match, the redeployment code below will trigger. + # Therefore builds which need to trigger redeployment MUST specify these URLs. All others should leave them empty. + REDEPLOY_SANDBOX_URL: + required: false + REDEPLOY_DEMO_URL: + required: false + +# Define shared default settings as environment variables +env: + IMAGE_NAME: ${{ inputs.image_name }} + # 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 '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=latest,enable=${{ github.ref_name == github.event.repository.default_branch }} + type=ref,event=branch,enable=${{ github.ref_name != 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 manage the 'latest' tag ourselves to the 'main' branch (see settings above) + TAGS_FLAVOR: | + latest=false + ${{ inputs.tags_flavor }} + # When these URL variables are specified & required branch matches, then the sandbox or demo site will be redeployed. + # See "Redeploy" steps below for more details. + REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }} + REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} + # Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org + # (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch) + DEPLOY_DEMO_BRANCH: 'dspace-7_x' + DEPLOY_ARCH: 'linux/amd64' + +jobs: + docker-build: + + strategy: + matrix: + # Architectures / Platforms for which we will build Docker images + arch: [ 'linux/amd64', 'linux/arm64' ] + os: [ ubuntu-latest ] + isPr: + - ${{ github.event_name == 'pull_request' }} + # If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work. + # The below exclude therefore ensures we do NOT build ARM64 for PRs. + exclude: + - isPr: true + os: ubuntu-latest + arch: linux/arm64 + + runs-on: ${{ matrix.os }} + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v4 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU emulation to build for multiple architectures + uses: docker/setup-qemu-action@v3 + + # 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: ${{ ! matrix.isPr }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + # https://github.com/docker/metadata-action + # Get Metadata for docker_build_deps step below + - name: Sync metadata (tags, labels) from GitHub to Docker for image + id: meta_build + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} + + # https://github.com/docker/build-push-action + - name: Build and push image + id: docker_build + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.dockerfile_context || '.' }} + file: ${{ inputs.dockerfile_path }} + platforms: ${{ matrix.arch }} + # For pull requests, we run the Docker build (to ensure no PR changes break the build), + # but we ONLY do an image push to DockerHub if it's NOT a PR + push: ${{ ! matrix.isPr }} + # Use tags / labels provided by 'docker/metadata-action' above + tags: ${{ steps.meta_build.outputs.tags }} + labels: ${{ steps.meta_build.outputs.labels }} + + # Export the digest of Docker build locally (for non PRs only) + - name: Export Docker build digest + if: ${{ ! matrix.isPr }} + run: | + mkdir -p /tmp/digests + digest="${{ steps.docker_build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + # Upload digest to an artifact, so that it can be used in manifest below + - name: Upload Docker build digest to artifact + if: ${{ ! matrix.isPr }} + uses: actions/upload-artifact@v3 + with: + name: digests-${{ inputs.build_id }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + # If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret, + # Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch. + - name: Redeploy sandbox.dspace.org (based on main branch) + if: | + !matrix.isPR && + env.REDEPLOY_SANDBOX_URL != '' && + matrix.arch == env.DEPLOY_ARCH && + github.ref_name == github.event.repository.default_branch + run: | + curl -X POST $REDEPLOY_SANDBOX_URL + + # If this build is NOT a PR and passed in a REDEPLOY_DEMO_URL secret, + # Then redeploy https://demo.dspace.org if this build is for our deployment architecture and demo branch. + - name: Redeploy demo.dspace.org (based on maintenace branch) + if: | + !matrix.isPR && + env.REDEPLOY_DEMO_URL != '' && + matrix.arch == env.DEPLOY_ARCH && + github.ref_name == env.DEPLOY_DEMO_BRANCH + run: | + curl -X POST $REDEPLOY_DEMO_URL + + # Merge Docker digests (from various architectures) into a manifest. + # This runs after all Docker builds complete above, and it tells hub.docker.com + # that these builds should be all included in the manifest for this tag. + # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) + docker-build_manifest: + if: ${{ github.event_name != 'pull_request' }} + runs-on: ubuntu-latest + needs: + - docker-build + steps: + - name: Download Docker build digests + uses: actions/download-artifact@v3 + with: + name: digests-${{ inputs.build_id }} + path: /tmp/digests + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Add Docker metadata for image + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + - name: Create manifest list from digests and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} From af1550d415c72540bb79c80344c706797f27fd2d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 28 Nov 2023 14:28:14 -0600 Subject: [PATCH 012/479] Ensure dspace-solr redeploys the Solr instances for Demo/Sandbox --- .github/workflows/docker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 2adceaa4d3c3..dd44e92f0470 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -121,6 +121,10 @@ jobs: secrets: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} + # Enable redeploy of sandbox & demo SOLR instance whenever dspace-solr image changes for deployed branch. + # These URLs MUST use different secrets than 'dspace/dspace' image build above as they are deployed separately. + REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_SOLR_URL }} + REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_SOLR_URL }} ########################################################### # Build/Push the 'dspace/dspace-postgres-pgcrypto' image From 7524d169f5782eab26967b3eba90c3247576ab72 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 28 Nov 2023 16:59:41 -0600 Subject: [PATCH 013/479] Remove unused env variables from docker.yml build script --- .github/workflows/docker.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index dd44e92f0470..338c7371f61a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -16,22 +16,6 @@ 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 '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=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 manage the 'latest' tag ourselves to the 'main' branch (see settings above) - TAGS_FLAVOR: | - latest=false - jobs: #################################################### # Build/Push the 'dspace/dspace-dependencies' image. From 16112a44d8f8ba5c097c2f0f383da53b644553c1 Mon Sep 17 00:00:00 2001 From: Shankeerthan Kasilingam Date: Tue, 5 Dec 2023 16:22:01 +0530 Subject: [PATCH 014/479] fix: Failure of org.dspace.app.rest.SitemapRestControllerIT when running locally (cherry picked from commit 44fc15f74baf6aa23c70f3b73975f3c82401e499) --- .../dspace/app/rest/SitemapRestControllerIT.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java index 175fb34e6cac..04d22718e846 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java @@ -216,7 +216,12 @@ public void testSitemap_sitemapIndexXml() throws Exception { //** THEN ** .andExpect(status().isOk()) //We expect the content type to match - .andExpect(content().contentType("application/xml;charset=UTF-8")) + .andExpect(res -> { + String actual = res.getResponse().getContentType(); + assertTrue("Content Type", + "text/xml;charset=UTF-8".equals(actual) || + "application/xml;charset=UTF-8".equals(actual)); + }) .andReturn(); String response = result.getResponse().getContentAsString(); @@ -232,7 +237,12 @@ public void testSitemap_sitemap0Xml() throws Exception { //** THEN ** .andExpect(status().isOk()) //We expect the content type to match - .andExpect(content().contentType("application/xml;charset=UTF-8")) + .andExpect(res -> { + String actual = res.getResponse().getContentType(); + assertTrue("Content Type", + "text/xml;charset=UTF-8".equals(actual) || + "application/xml;charset=UTF-8".equals(actual)); + }) .andReturn(); String response = result.getResponse().getContentAsString(); From 75365372067140d6407aeb4d9fb301722c3d1e72 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Fri, 29 Dec 2023 05:55:48 +0100 Subject: [PATCH 015/479] 90830/104654: issue PR#8267 - Only first 10 predb records resynced (default size) - IT proving issue --- .../SolrDatabaseResyncIT.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java b/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java index 4fa881257e0f..fd7c3c4934f0 100644 --- a/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java +++ b/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java @@ -35,6 +35,9 @@ import org.junit.Before; import org.junit.Test; +/** + * IT for {@link org.dspace.app.solrdatabaseresync.SolrDatabaseResyncIT} + */ public class SolrDatabaseResyncIT extends AbstractIntegrationTestWithDatabase { private final ConfigurationService configurationService = @@ -48,6 +51,15 @@ public class SolrDatabaseResyncIT extends AbstractIntegrationTestWithDatabase { private Collection col; private Item item1; private Item item2; + private Item item3; + private Item item4; + private Item item5; + private Item item6; + private Item item7; + private Item item8; + private Item item9; + private Item item10; + private Item item11; @Before public void setUp() throws Exception { @@ -75,6 +87,16 @@ public void setUp() throws Exception { .withSubject("TestingForMore") .build(); + item3 = ItemBuilder.createItem(context, col).withTitle("Public item 3").build(); + item4 = ItemBuilder.createItem(context, col).withTitle("Public item 4").build(); + item5 = ItemBuilder.createItem(context, col).withTitle("Public item 5").build(); + item6 = ItemBuilder.createItem(context, col).withTitle("Public item 6").build(); + item7 = ItemBuilder.createItem(context, col).withTitle("Public item 7").build(); + item8 = ItemBuilder.createItem(context, col).withTitle("Public item 8").build(); + item9 = ItemBuilder.createItem(context, col).withTitle("Public item 9").build(); + item10 = ItemBuilder.createItem(context, col).withTitle("Public item 10").build(); + item11 = ItemBuilder.createItem(context, col).withTitle("Public item 11").build(); + context.setDispatcher("noindex"); } @@ -83,12 +105,30 @@ public void solrPreDBStatusExistingItemTest() throws Exception { // Items were created, they should contain a predb status in solr assertHasPreDBStatus(item1); assertHasPreDBStatus(item2); + assertHasPreDBStatus(item3); + assertHasPreDBStatus(item4); + assertHasPreDBStatus(item5); + assertHasPreDBStatus(item6); + assertHasPreDBStatus(item7); + assertHasPreDBStatus(item8); + assertHasPreDBStatus(item9); + assertHasPreDBStatus(item10); + assertHasPreDBStatus(item11); performSolrDatabaseResyncScript(); // Database status script was performed, their predb status should be removed assertHasNoPreDBStatus(item1); assertHasNoPreDBStatus(item2); + assertHasNoPreDBStatus(item3); + assertHasNoPreDBStatus(item4); + assertHasNoPreDBStatus(item5); + assertHasNoPreDBStatus(item6); + assertHasNoPreDBStatus(item7); + assertHasNoPreDBStatus(item8); + assertHasNoPreDBStatus(item9); + assertHasNoPreDBStatus(item10); + assertHasNoPreDBStatus(item11); context.restoreAuthSystemState(); } @@ -98,22 +138,50 @@ public void solrPreDBStatusRemovedItemTest() throws Exception { // Items were created, they should contain a predb status in solr assertHasPreDBStatus(item1); assertHasPreDBStatus(item2); + assertHasPreDBStatus(item3); + assertHasPreDBStatus(item4); + assertHasPreDBStatus(item5); + assertHasPreDBStatus(item6); + assertHasPreDBStatus(item7); + assertHasPreDBStatus(item8); + assertHasPreDBStatus(item9); + assertHasPreDBStatus(item10); + assertHasPreDBStatus(item11); collectionService.delete(context, col); // Items were deleted, they should still contain a predb status in solr for now assertHasPreDBStatus(item1); assertHasPreDBStatus(item2); + assertHasPreDBStatus(item3); + assertHasPreDBStatus(item4); + assertHasPreDBStatus(item5); + assertHasPreDBStatus(item6); + assertHasPreDBStatus(item7); + assertHasPreDBStatus(item8); + assertHasPreDBStatus(item9); + assertHasPreDBStatus(item10); + assertHasPreDBStatus(item11); performSolrDatabaseResyncScript(); // Database status script was performed, their solr document should have been removed assertNoSolrDocument(item1); assertNoSolrDocument(item2); + assertNoSolrDocument(item3); + assertNoSolrDocument(item4); + assertNoSolrDocument(item5); + assertNoSolrDocument(item6); + assertNoSolrDocument(item7); + assertNoSolrDocument(item8); + assertNoSolrDocument(item9); + assertNoSolrDocument(item10); + assertNoSolrDocument(item11); context.restoreAuthSystemState(); } + public void assertHasNoPreDBStatus(Item item) throws Exception { assertNotEquals(STATUS_FIELD_PREDB, getStatus(item)); } From 91f53c22870bcb28719ded08bf642d269d13f4a2 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Fri, 29 Dec 2023 05:58:25 +0100 Subject: [PATCH 016/479] 90830/104654: issue PR#8267 - Only first 10 predb records resynced (default size) - Fix: - Query with 0 rows to get total number of records to process - Process them in batches of 100 --- .../SolrDatabaseResyncCli.java | 70 +++++++++++-------- .../SolrDatabaseResyncIT.java | 1 + 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java index f901c9ca569e..aac42ce1acf9 100644 --- a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java +++ b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java @@ -105,35 +105,17 @@ private void performStatusUpdate(Context context) throws SearchServiceException, solrQuery.addFilterQuery(dateRangeFilter); solrQuery.addField(SearchUtils.RESOURCE_ID_FIELD); solrQuery.addField(SearchUtils.RESOURCE_UNIQUE_ID); + solrQuery.setRows(0); QueryResponse response = solrSearchCore.getSolr().query(solrQuery, solrSearchCore.REQUEST_METHOD); - - if (response != null) { - logInfoAndOut(response.getResults().size() + " items found to process"); - - for (SolrDocument doc : response.getResults()) { - String uuid = (String) doc.getFirstValue(SearchUtils.RESOURCE_ID_FIELD); - String uniqueId = (String) doc.getFirstValue(SearchUtils.RESOURCE_UNIQUE_ID); - logDebugAndOut("Processing item with UUID: " + uuid); - - Optional indexableObject = Optional.empty(); - try { - indexableObject = indexObjectServiceFactory - .getIndexableObjectFactory(uniqueId).findIndexableObject(context, uuid); - } catch (SQLException e) { - log.warn("An exception occurred when attempting to retrieve item with UUID \"" + uuid + - "\" from the database, removing related solr document", e); - } - - try { - if (indexableObject.isPresent()) { - logDebugAndOut("Item exists in DB, updating solr document"); - updateItem(context, indexableObject.get()); - } else { - logDebugAndOut("Item doesn't exist in DB, removing solr document"); - removeItem(context, uniqueId); - } - } catch (SQLException | IOException e) { - log.error(e.getMessage(), e); + if (response != null && response.getResults() != null) { + long nrOfPreDBResults = response.getResults().getNumFound(); + if (nrOfPreDBResults > 0) { + logInfoAndOut(nrOfPreDBResults + " items found to process"); + int batchSize = configurationService.getIntProperty("script.solr-database-resync.batch-size", 100); + for (int start = 0; start < nrOfPreDBResults; start += batchSize) { + solrQuery.setStart(start); + solrQuery.setRows(batchSize); + performStatusUpdateOnNextBatch(context, solrQuery); } } } @@ -141,6 +123,38 @@ private void performStatusUpdate(Context context) throws SearchServiceException, indexingService.commit(); } + private void performStatusUpdateOnNextBatch(Context context, SolrQuery solrQuery) + throws SolrServerException, IOException { + QueryResponse response = solrSearchCore.getSolr().query(solrQuery, solrSearchCore.REQUEST_METHOD); + + for (SolrDocument doc : response.getResults()) { + String uuid = (String) doc.getFirstValue(SearchUtils.RESOURCE_ID_FIELD); + String uniqueId = (String) doc.getFirstValue(SearchUtils.RESOURCE_UNIQUE_ID); + logDebugAndOut("Processing item with UUID: " + uuid); + + Optional indexableObject = Optional.empty(); + try { + indexableObject = indexObjectServiceFactory + .getIndexableObjectFactory(uniqueId).findIndexableObject(context, uuid); + } catch (SQLException e) { + log.warn("An exception occurred when attempting to retrieve item with UUID \"" + uuid + + "\" from the database, removing related solr document", e); + } + + try { + if (indexableObject.isPresent()) { + logDebugAndOut("Item exists in DB, updating solr document"); + updateItem(context, indexableObject.get()); + } else { + logDebugAndOut("Item doesn't exist in DB, removing solr document"); + removeItem(context, uniqueId); + } + } catch (SQLException | IOException e) { + log.error(e.getMessage(), e); + } + } + } + private void updateItem(Context context, IndexableObject indexableObject) throws SolrServerException, IOException { Map fieldModifier = new HashMap<>(1); fieldModifier.put("remove", STATUS_FIELD_PREDB); diff --git a/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java b/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java index fd7c3c4934f0..d1fa0db9089b 100644 --- a/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java +++ b/dspace-api/src/test/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncIT.java @@ -65,6 +65,7 @@ public class SolrDatabaseResyncIT extends AbstractIntegrationTestWithDatabase { public void setUp() throws Exception { super.setUp(); configurationService.setProperty("solr-database-resync.time-until-reindex", 1); + configurationService.setProperty("script.solr-database-resync.batch-size", 5); ServiceManager serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); searchService = serviceManager.getServiceByName(null, MockSolrSearchCore.class); From c7113e5d2450f7d7d8f4c8a26e83c43a0477aa4e Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 27 Oct 2023 14:10:49 +0200 Subject: [PATCH 017/479] Correct response of byMetadataAndCollection operation of Controlled Vocabularies Endpoint when no controlled vocabulary is available for the specified metadata and collection (cherry picked from commit eb2e4f1155864d190a3458210419c02978519ec0) --- .../dspace/app/rest/repository/VocabularyRestRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyRestRepository.java index fcc37d13160d..783ed418233d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyRestRepository.java @@ -100,6 +100,9 @@ public VocabularyRest findByMetadataAndCollection( } String authorityName = cas.getChoiceAuthorityName(tokens[0], tokens[1], tokens[2], collection); + if (authorityName == null) { + return null; + } ChoiceAuthority source = cas.getChoiceAuthorityByAuthorityName(authorityName); return authorityUtils.convertAuthority(source, authorityName, utils.obtainProjection()); } From 14c6ccce21d0cb563b9d9ba88a73309d910e28ce Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 27 Oct 2023 14:26:37 +0200 Subject: [PATCH 018/479] Add missing test for byMetadataAndCollection operation of Controlled Vocabularies endpoint (cherry picked from commit a294f996cf8c6169d6414609363997e118bc41d0) --- .../app/rest/VocabularyRestRepositoryIT.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java index f9b31fb06de9..81d2dba67911 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java @@ -394,6 +394,20 @@ public void findByMetadataAndCollectionTest() throws Exception { ))); } + @Test + public void findByMetadataAndCollectionWithMetadataWithoutVocabularyTest() throws Exception { + context.turnOffAuthorisationSystem(); + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test collection") + .build(); + context.restoreAuthSystemState(); + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/submission/vocabularies/search/byMetadataAndCollection") + .param("metadata", "dc.title") + .param("collection", collection.getID().toString())) + .andExpect(status().isNoContent()); + } + @Test public void findByMetadataAndCollectionUnprocessableEntityTest() throws Exception { context.turnOffAuthorisationSystem(); From 37ed65850fcba45ddf9dbbe615bd5f2792e406ac Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Tue, 9 Jan 2024 11:34:52 +0300 Subject: [PATCH 019/479] dspace-api: fix typo in AuthorizeServiceImpl log (cherry picked from commit 127b1ae868534b318f8fc5c0c21d300502183961) --- .../main/java/org/dspace/authorize/AuthorizeServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java index 5dffe5fdfc1f..beff6fdc48e8 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -895,7 +895,7 @@ private boolean performCheck(Context context, String query) throws SQLException return true; } } catch (SearchServiceException e) { - log.error("Failed getting getting community/collection admin status for " + log.error("Failed getting community/collection admin status for " + context.getCurrentUser().getEmail() + " The search error is: " + e.getMessage() + " The search resourceType filter was: " + query); } From b8d00afd121404446a5021020e9f834951b9dfc3 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 8 Jan 2024 10:22:52 -0600 Subject: [PATCH 020/479] Return headers for HEAD request (cherry picked from commit 64ae49a29ff4edb87053e8319b23a6a60696f65a) --- .../dspace/app/rest/BitstreamRestController.java | 6 ++++++ .../app/rest/utils/HttpHeadersInitializer.java | 15 ++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index ebd84270f285..063e4760fa40 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -167,6 +167,12 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp //Send the data if (httpHeadersInitializer.isValid()) { HttpHeaders httpHeaders = httpHeadersInitializer.initialiseHeaders(); + + if (RequestMethod.HEAD.name().equals(request.getMethod())) { + log.debug("HEAD request - no response body"); + return ResponseEntity.ok().headers(httpHeaders).build(); + } + return ResponseEntity.ok().headers(httpHeaders).body(bitstreamResource); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java index a69da4c5e86b..854aec8868a3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java @@ -33,7 +33,6 @@ public class HttpHeadersInitializer { protected final Logger log = LoggerFactory.getLogger(this.getClass()); - private static final String METHOD_HEAD = "HEAD"; private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES"; private static final String CONTENT_TYPE_MULTITYPE_WITH_BOUNDARY = "multipart/byteranges; boundary=" + MULTIPART_BOUNDARY; @@ -165,16 +164,14 @@ public HttpHeaders initialiseHeaders() throws IOException { httpHeaders.put(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, Collections.singletonList(HttpHeaders.ACCEPT_RANGES)); - httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(String.format(CONTENT_DISPOSITION_FORMAT, - disposition, - encodeText(fileName)))); - log.debug("Content-Disposition : {}", disposition); - // Content phase - if (METHOD_HEAD.equals(request.getMethod())) { - log.debug("HEAD request - skipping content"); - return null; + // distposition may be null here if contentType is null + if (!isNullOrEmpty(disposition)) { + httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(String.format(CONTENT_DISPOSITION_FORMAT, + disposition, + encodeText(fileName)))); } + log.debug("Content-Disposition : {}", disposition); return httpHeaders; From ebbae391d74775fb6c406d56ee0038e115ec069a Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 10 Jan 2024 13:51:11 -0600 Subject: [PATCH 021/479] Add content-length to bitstream (cherry picked from commit 6be7e4e37047c3f33d4aec371240a3bf2f02a3ef) --- .../dspace/app/rest/BitstreamRestController.java | 1 + .../app/rest/utils/HttpHeadersInitializer.java | 4 ++++ .../dspace/app/rest/BitstreamRestControllerIT.java | 14 +++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index 063e4760fa40..9b5ede37c85f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -138,6 +138,7 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp .withBufferSize(BUFFER_SIZE) .withFileName(name) .withChecksum(bit.getChecksum()) + .withLength(bit.getSizeBytes()) .withMimetype(mimetype) .with(request) .with(response); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java index 854aec8868a3..d68c710a3c7a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.Objects; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -143,6 +144,9 @@ public HttpHeaders initialiseHeaders() throws IOException { if (checksum != null) { httpHeaders.put(ETAG, Collections.singletonList(checksum)); } + if (Objects.nonNull((Long.valueOf(this.length)))) { + httpHeaders.put(HttpHeaders.CONTENT_LENGTH, Collections.singletonList(String.valueOf(this.length))); + } httpHeaders.put(LAST_MODIFIED, Collections.singletonList(FastHttpDateFormat.formatDate(lastModified))); httpHeaders.put(EXPIRES, Collections.singletonList(FastHttpDateFormat.formatDate( System.currentTimeMillis() + DEFAULT_EXPIRE_TIME))); 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 4813cc659694..47c48846ad26 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 @@ -205,6 +205,18 @@ public void retrieveFullBitstream() throws Exception { } context.restoreAuthSystemState(); + //** WHEN ** + // we want to know what we are downloading before we download it + getClient().perform(head("/api/core/bitstreams/" + bitstream.getID() + "/content")) + //** THEN ** + .andExpect(status().isOk()) + + //The Content Length must match the full length + .andExpect(header().longValue("Content-Length", bitstreamContent.getBytes().length)) + .andExpect(header().string("Content-Type", "text/plain;charset=UTF-8")) + .andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\"")) + .andExpect(content().bytes(new byte[] {})); + //** WHEN ** //We download the bitstream getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) @@ -231,7 +243,7 @@ public void retrieveFullBitstream() throws Exception { .andExpect(status().isNotModified()); //The download and head request should also be logged as a statistics record - checkNumberOfStatsRecords(bitstream, 2); + checkNumberOfStatsRecords(bitstream, 3); } @Test From 9a0db2a642025423dcc8d3c890350057b79c821e Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 10 Jan 2024 13:08:02 +0000 Subject: [PATCH 022/479] Skip recording usage event if administrator (cherry picked from commit 73e5c43f7c565166a7b4b920a8958be0cc0c6c01) --- .../dspace/statistics/SolrLoggerServiceImpl.java | 13 +++++++++++++ 1 file changed, 13 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 97585f5a47cb..5f976bbfd94b 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -81,6 +81,7 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ShardParams; import org.apache.solr.common.util.NamedList; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.Collection; @@ -146,6 +147,8 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea private SolrStatisticsCore solrStatisticsCore; @Autowired private GeoIpService geoIpService; + @Autowired + private AuthorizeService authorizeService; /** URL to the current-year statistics core. Prior-year shards will have a year suffixed. */ private String statisticsCoreURL; @@ -219,6 +222,16 @@ public void postView(DSpaceObject dspaceObject, HttpServletRequest request, @Override public void postView(DSpaceObject dspaceObject, HttpServletRequest request, EPerson currentUser, String referrer) { + Context context = new Context(); + // Do not record statistics for Admin users + try { + if (authorizeService.isAdmin(context, currentUser)) { + return; + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + if (solr == null) { return; } From dc57aceeaf74204326862a4d0efb781789c2b9aa Mon Sep 17 00:00:00 2001 From: nwoodward Date: Tue, 16 Jan 2024 13:28:40 -0600 Subject: [PATCH 023/479] catch exceptions stemming from invalid id's (cherry picked from commit 848df25984b8d6cabb8b6b0c831070ffed66919a) --- .../org/dspace/content/BitstreamServiceImpl.java | 13 +++++++++---- .../java/org/dspace/content/BundleServiceImpl.java | 13 +++++++++---- .../org/dspace/content/CollectionServiceImpl.java | 13 +++++++++---- .../org/dspace/content/CommunityServiceImpl.java | 13 +++++++++---- .../java/org/dspace/content/ItemServiceImpl.java | 13 +++++++++---- .../java/org/dspace/eperson/EPersonServiceImpl.java | 13 +++++++++---- .../java/org/dspace/eperson/GroupServiceImpl.java | 13 +++++++++---- 7 files changed, 63 insertions(+), 28 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 691d38f03039..e23e5ce2c825 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -458,10 +458,15 @@ public int countTotal(Context context) throws SQLException { @Override public Bitstream findByIdOrLegacyId(Context context, String id) throws SQLException { - if (StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUID.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return null; } } diff --git a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java index 546d48d4306b..3ba90c8cc2ae 100644 --- a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java @@ -562,10 +562,15 @@ public int getSupportsTypeConstant() { @Override public Bundle findByIdOrLegacyId(Context context, String id) throws SQLException { - if (StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUID.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return null; } } diff --git a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java index 84ca1692ccf2..0d9507e3aa35 100644 --- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java @@ -895,10 +895,15 @@ public void updateLastModified(Context context, Collection collection) throws SQ @Override public Collection findByIdOrLegacyId(Context context, String id) throws SQLException { - if (StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUID.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return null; } } diff --git a/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java index 15ac1c58a690..045adc229e79 100644 --- a/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java @@ -694,10 +694,15 @@ public void updateLastModified(Context context, Community community) { @Override public Community findByIdOrLegacyId(Context context, String id) throws SQLException { - if (StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUID.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return null; } } 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 ebea2aa5b820..26a61877fe7f 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -1609,10 +1609,15 @@ protected void getAuthoritiesAndConfidences(String fieldKey, Collection collecti @Override public Item findByIdOrLegacyId(Context context, String id) throws SQLException { - if (StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUID.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return 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 66fe6562ea25..453d5d0726be 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java @@ -142,10 +142,15 @@ public EPerson getSystemEPerson(Context c) @Override public EPerson findByIdOrLegacyId(Context context, String id) throws SQLException { - if (StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUID.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return null; } } diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index b8d8c75d0f2e..730053e42ce2 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -872,10 +872,15 @@ protected Set getChildren(Map> parents, UUID parent) { @Override public Group findByIdOrLegacyId(Context context, String id) throws SQLException { - if (org.apache.commons.lang3.StringUtils.isNumeric(id)) { - return findByLegacyId(context, Integer.parseInt(id)); - } else { - return find(context, UUIDUtils.fromString(id)); + try { + if (StringUtils.isNumeric(id)) { + return findByLegacyId(context, Integer.parseInt(id)); + } else { + return find(context, UUID.fromString(id)); + } + } catch (IllegalArgumentException e) { + // Not a valid legacy ID or valid UUID + return null; } } From a22a01aa5c26dc49316523c089326d9c7a3568eb Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 22 Jan 2024 15:14:54 +0100 Subject: [PATCH 024/479] search on labels, not values on DCInputs (cherry picked from commit 5ebe1a9402d8d0be85284d722f65bc71836113df) --- .../java/org/dspace/content/authority/DCInputAuthority.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java index b1d8cf36a5d3..902bded33ef7 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java @@ -156,7 +156,8 @@ public Choices getMatches(String query, int start, int limit, String locale) { int found = 0; List v = new ArrayList(); for (int i = 0; i < valuesLocale.length; ++i) { - if (query == null || StringUtils.containsIgnoreCase(valuesLocale[i], query)) { + // In a DCInputAuthority context, a user will want to query the labels, not the values + if (query == null || StringUtils.containsIgnoreCase(labelsLocale[i], query)) { if (found >= start && v.size() < limit) { v.add(new Choice(null, valuesLocale[i], labelsLocale[i])); if (valuesLocale[i].equalsIgnoreCase(query)) { From 25496630810e535c17588a965adc33740d8b6384 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Sat, 27 Jan 2024 13:38:22 +0100 Subject: [PATCH 025/479] The cas.init() method is called in the VocabularyRestRepositoryIT#setup() method (cherry picked from commit 85b5e06fb0946a15f71abaa1d27ef5914f7b8df2) --- .../java/org/dspace/app/rest/VocabularyRestRepositoryIT.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java index 81d2dba67911..30890d7ef838 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java @@ -92,6 +92,11 @@ public void setup() throws Exception { // the properties that we're altering above and this is only used within the tests DCInputAuthority.reset(); pluginService.clearNamedPluginClasses(); + + // The following line is needed to call init() method in the ChoiceAuthorityServiceImpl class, without it + // the `submissionConfigService` will be null what will cause a NPE in the clearCache() method + // https://github.com/DSpace/DSpace/issues/9292 + cas.getChoiceAuthoritiesNames(); cas.clearCache(); context.turnOffAuthorisationSystem(); From a0ba470197613873bc18031daad9d760a689b107 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 Feb 2024 09:02:19 -0600 Subject: [PATCH 026/479] Fix "cannot override networks.dspacenet" by updating all compose files to reference 'dspacenet' network --- docker-compose-cli.yml | 12 ++++++------ docker-compose.yml | 2 +- dspace/src/main/docker-compose/README.md | 1 + .../main/docker-compose/docker-compose-angular.yml | 8 +++++--- .../src/main/docker-compose/docker-compose-iiif.yml | 8 +++++--- .../docker-compose/docker-compose-shibboleth.yml | 8 +++++--- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index 9c66fed6835b..4f4d4a189161 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -1,5 +1,10 @@ version: "3.7" - +networks: + # Default to using network named 'dspacenet' from docker-compose.yml. + # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") + default: + name: ${COMPOSE_PROJECT_NAME}_dspacenet + external: true services: dspace-cli: image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" @@ -26,13 +31,8 @@ services: - ./dspace/config:/dspace/config entrypoint: /dspace/bin/dspace command: help - networks: - - dspacenet tty: true stdin_open: true volumes: assetstore: - -networks: - dspacenet: diff --git a/docker-compose.yml b/docker-compose.yml index 6c1615040722..7801a681031a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,7 @@ services: depends_on: - dspacedb networks: - dspacenet: + - dspacenet ports: - published: 8080 target: 8080 diff --git a/dspace/src/main/docker-compose/README.md b/dspace/src/main/docker-compose/README.md index 35a6e6055433..1c9acea30cc5 100644 --- a/dspace/src/main/docker-compose/README.md +++ b/dspace/src/main/docker-compose/README.md @@ -78,6 +78,7 @@ docker-compose -p d7 up -d ``` docker-compose -p d7 -f docker-compose.yml -f dspace/src/main/docker-compose/docker-compose-angular.yml up -d ``` +NOTE: This starts the UI in development mode. It will take a few minutes to see the UI as the Angular code needs to be compiled. ## Run DSpace REST and DSpace Angular from local branches diff --git a/dspace/src/main/docker-compose/docker-compose-angular.yml b/dspace/src/main/docker-compose/docker-compose-angular.yml index 00dde2e83187..8f9bdf94b38b 100644 --- a/dspace/src/main/docker-compose/docker-compose-angular.yml +++ b/dspace/src/main/docker-compose/docker-compose-angular.yml @@ -8,7 +8,11 @@ version: '3.7' networks: - dspacenet: + # Default to using network named 'dspacenet' from docker-compose.yml. + # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") + default: + name: ${COMPOSE_PROJECT_NAME}_dspacenet + external: true services: dspace-angular: container_name: dspace-angular @@ -24,8 +28,6 @@ services: DSPACE_REST_PORT: 8080 DSPACE_REST_NAMESPACE: /server image: dspace/dspace-angular:dspace-7_x - networks: - dspacenet: ports: - published: 4000 target: 4000 diff --git a/dspace/src/main/docker-compose/docker-compose-iiif.yml b/dspace/src/main/docker-compose/docker-compose-iiif.yml index 2ab58d9014f0..d795192f13c4 100644 --- a/dspace/src/main/docker-compose/docker-compose-iiif.yml +++ b/dspace/src/main/docker-compose/docker-compose-iiif.yml @@ -12,7 +12,11 @@ # version: '3.7' networks: - dspacenet: + # Default to using network named 'dspacenet' from docker-compose.yml. + # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") + default: + name: ${COMPOSE_PROJECT_NAME}_dspacenet + external: true services: dspace-iiif: container_name: dspace-iiif @@ -21,8 +25,6 @@ services: # Using UCLA Library image as it seems to be most maintained at this time. There is no official image. # https://hub.docker.com/r/uclalibrary/cantaloupe image: uclalibrary/cantaloupe:5.0.4-0 - networks: - dspacenet: ports: - '8182:8182' # For a guide of environment variables that can be used, see diff --git a/dspace/src/main/docker-compose/docker-compose-shibboleth.yml b/dspace/src/main/docker-compose/docker-compose-shibboleth.yml index 58f1527d6ccb..33eadcb142d7 100644 --- a/dspace/src/main/docker-compose/docker-compose-shibboleth.yml +++ b/dspace/src/main/docker-compose/docker-compose-shibboleth.yml @@ -12,7 +12,11 @@ # version: '3.7' networks: - dspacenet: + # Default to using network named 'dspacenet' from docker-compose.yml. + # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") + default: + name: ${COMPOSE_PROJECT_NAME}_dspacenet + external: true services: dspace-shibboleth: container_name: dspace-shibboleth @@ -22,8 +26,6 @@ services: build: # Must be relative to root, so that it can be built alongside [src]/docker-compose.yml context: ./dspace/src/main/docker/dspace-shibboleth - networks: - dspacenet: ports: - published: 80 target: 80 From 250e82af9d2b1c812341dc4af2294010a603f36d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 Feb 2024 09:13:16 -0600 Subject: [PATCH 027/479] Fix build issues with dockersolr by using "additional_contexts" to pass solr config path to Dockerfile. --- docker-compose.yml | 6 ++++-- dspace/src/main/docker/dspace-solr/Dockerfile | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7801a681031a..a7894135c6ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -89,8 +89,10 @@ services: container_name: dspacesolr image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}" build: - context: . - dockerfile: ./dspace/src/main/docker/dspace-solr/Dockerfile + context: ./dspace/src/main/docker/dspace-solr/ + # Provide path to Solr configs necessary to build Docker image + additional_contexts: + solrconfigs: ./dspace/solr/ args: SOLR_VERSION: "${SOLR_VER:-8.11}" networks: diff --git a/dspace/src/main/docker/dspace-solr/Dockerfile b/dspace/src/main/docker/dspace-solr/Dockerfile index 9fe9adf9440f..eb8e93493fa8 100644 --- a/dspace/src/main/docker/dspace-solr/Dockerfile +++ b/dspace/src/main/docker/dspace-solr/Dockerfile @@ -26,10 +26,12 @@ RUN mkdir -p $AUTHORITY_CONFIGSET_PATH && \ mkdir -p $SEARCH_CONFIGSET_PATH && \ mkdir -p $STATISTICS_CONFIGSET_PATH -COPY dspace/solr/authority/conf/* $AUTHORITY_CONFIGSET_PATH/ -COPY dspace/solr/oai/conf/* $OAI_CONFIGSET_PATH/ -COPY dspace/solr/search/conf/* $SEARCH_CONFIGSET_PATH/ -COPY dspace/solr/statistics/conf/* $STATISTICS_CONFIGSET_PATH/ +# NOTE: "solrconfigs" MUST be passed in by docker-compose via "additional_contexts" +# OR via "docker build --build-context solrconfigs=[path-to-dspace/solr]" +COPY --from=solrconfigs authority/conf/* $AUTHORITY_CONFIGSET_PATH/ +COPY --from=solrconfigs oai/conf/* $OAI_CONFIGSET_PATH/ +COPY --from=solrconfigs search/conf/* $SEARCH_CONFIGSET_PATH/ +COPY --from=solrconfigs statistics/conf/* $STATISTICS_CONFIGSET_PATH/ RUN chown -R solr:solr /opt/solr/server/solr/configsets From 414837a12a2634eb2d5217d11776f2cdf241bd42 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 Feb 2024 10:09:08 -0600 Subject: [PATCH 028/479] Fix 'dspacesolr' build issues in GitHub actions by adding 'solrconfig' as an additional_context --- .github/workflows/docker.yml | 2 ++ .github/workflows/reusable-docker-build.yml | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 338c7371f61a..9f1e407cff4b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -102,6 +102,8 @@ jobs: build_id: dspace-solr image_name: dspace/dspace-solr dockerfile_path: ./dspace/src/main/docker/dspace-solr/Dockerfile + # Must pass solrconfigs to the Dockerfile so that it can find the required Solr config files + dockerfile_additional_contexts: 'solrconfigs=./dspace/solr/' secrets: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 46bdab3b6827..aa8327f4d11b 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -24,6 +24,12 @@ on: dockerfile_context: required: false type: string + default: '.' + # Optionally a list of "additional_contexts" to pass to Dockerfile. Defaults to empty + dockerfile_additional_contexts: + required: false + type: string + default: '' # If Docker image should have additional tag flavor details (e.g. a suffix), it may be passed in. tags_flavor: required: false @@ -123,7 +129,9 @@ jobs: id: docker_build uses: docker/build-push-action@v5 with: - context: ${{ inputs.dockerfile_context || '.' }} + build-contexts: | + ${{ inputs.dockerfile_additional_contexts }} + context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} # For pull requests, we run the Docker build (to ensure no PR changes break the build), From 3bfcf829772b535c6bb90007d211f853869e5145 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 029/479] 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 b3e903a47b4e..3df2f2d5e221 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 774f3bbcd5c490b1619c6e9fdd5a837949eb5913 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 030/479] 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 c8db035d864e..8cba90552481 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 5893c7768e22ad784cdb07698ad3624d71d8f1b1 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 031/479] 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 2fddacc7e4b5..7b4cc0b6f596 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -635,7 +635,7 @@ dnsjava dnsjava - 2.1.7 + 2.1.9 From bd7214ad1f515c266355be7a646174d3c14cca92 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 2 Jun 2023 19:36:59 +0300 Subject: [PATCH 032/479] 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 1c3479fc0f51..08fd32f7202d 100644 --- a/pom.xml +++ b/pom.xml @@ -1738,7 +1738,7 @@ com.google.code.findbugs jsr305 - 3.0.1 + 3.0.2 provided From aeb5300c6ff81d26cb48d3f9ee8f103288f79b18 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 033/479] 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 08fd32f7202d..e1b7afc8cad6 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.53.v20231009 2.20.0 From 84452a97d8c477a04407f8abeb4f05d68cbacdc5 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 034/479] 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 e1b7afc8cad6..711d9e439b8f 100644 --- a/pom.xml +++ b/pom.xml @@ -1358,7 +1358,7 @@ net.handle handle - 9.3.0 + 9.3.1 From be7bdcf3980784fa820355e0ee4cfc0930fcd38a 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 035/479] 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 711d9e439b8f..52308f07784c 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 969bee48715c9870aedb9e401078f22ef6ee5e0a 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 036/479] 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 52308f07784c..1f6f108f7b88 100644 --- a/pom.xml +++ b/pom.xml @@ -1669,7 +1669,7 @@ junit junit - 4.13.1 + 4.13.2 test From 95ac7dea42e5494e3214d9590f85cb6ab129cd2d Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 2 Jun 2023 22:25:29 +0300 Subject: [PATCH 037/479] 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 1f6f108f7b88..233a70e43c72 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 bbe05833a98f844d98e69e6295f766b042b83b64 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 038/479] 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 233a70e43c72..5da3aef26215 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 e2d809ffb4fadae00a42c46ca7fc6ca042ee4a7b 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 039/479] 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 7b4cc0b6f596..17479e3c1410 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -671,7 +671,7 @@ org.flywaydb flyway-core - 8.4.4 + 8.5.13 From 91f9d8c4dd7c67e7cb3595431354fbe26c5d30cc 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 040/479] 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 17479e3c1410..62964053961a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -794,7 +794,7 @@ com.opencsv opencsv - 5.6 + 5.7.1 From ba95c1852f199dc6d95855e399dd820aa6e5b54f 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 041/479] 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 5da3aef26215..e51bc02cf577 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.53.v20231009 From e14f267229371eaac186ad66ee21561f684c1e47 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 042/479] 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 e51bc02cf577..8af14a95e277 100644 --- a/pom.xml +++ b/pom.xml @@ -1302,7 +1302,7 @@ org.apache.james apache-mime4j-core - 0.8.4 + 0.8.9 From 53c4e18159db21bdac2d32d1ab56a4bc4d11560f 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 043/479] 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 8af14a95e277..ac9985276455 100644 --- a/pom.xml +++ b/pom.xml @@ -1489,7 +1489,7 @@ commons-io commons-io - 2.7 + 2.12.0 org.apache.commons From 8895aca3eb2f1c3ea645595b3ef9d14fd8b272fb 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 044/479] 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 ac9985276455..93ad8008e668 100644 --- a/pom.xml +++ b/pom.xml @@ -1516,7 +1516,7 @@ commons-validator commons-validator - 1.5.0 + 1.7 joda-time From 167033d8305bb521c71ab8192451875a0462ee0e 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 045/479] 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 93ad8008e668..44e032e2462d 100644 --- a/pom.xml +++ b/pom.xml @@ -1456,7 +1456,7 @@ commons-codec commons-codec - 1.10 + 1.15 org.apache.commons From a9a8021cf658c151aa38579a211658219248a561 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 046/479] 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 44e032e2462d..7b0edc065c5d 100644 --- a/pom.xml +++ b/pom.xml @@ -1451,7 +1451,7 @@ commons-cli commons-cli - 1.4 + 1.5.0 commons-codec From b5a59c76e8fc6e202d0362a0c46858a6070334c6 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 047/479] 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 7b0edc065c5d..6e47dc59ecfc 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 8e66812dbbd85eddc51bfc5036bd249ddd67cf96 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 4 Jun 2023 22:25:40 +0300 Subject: [PATCH 048/479] 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 6e47dc59ecfc..054a04dbd741 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 f9469c9b6f97c2031d76b4671f357fb93ec5b4c5 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 4 Jun 2023 22:26:54 +0300 Subject: [PATCH 049/479] 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 62964053961a..a90dc0e1d92b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -821,6 +821,13 @@ eu.openaire funders-model 2.0.0 + + + + org.javassist + javassist + + From 36c042bc98fc932f21bdd938e205f99fa30e4dc9 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 050/479] 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 3df2f2d5e221..4b3e79431e97 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 fe8c3ef38843a576c5e75accbf51b1e4c0616943 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 051/479] 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 054a04dbd741..1ff8a92524cf 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 7b0b21f84de121713fcb080171feac6064117e95 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 052/479] 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 4b3e79431e97..40b61139ee72 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 69e1ff98a0de01995ac10a1d69d33ceca064e39b Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Mon, 5 Jun 2023 10:33:46 +0300 Subject: [PATCH 053/479] 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 900ede5fc507..52783e3ec571 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 1ff8a92524cf..31fcf8f75a57 100644 --- a/pom.xml +++ b/pom.xml @@ -1784,7 +1784,7 @@ xom xom - 1.2.5 + 1.3.9 From 096fb3fb74e29b8c6eee8155c6940084a00f8039 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Mon, 5 Jun 2023 10:36:44 +0300 Subject: [PATCH 054/479] 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 a90dc0e1d92b..0fdf045c10a6 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -495,12 +495,6 @@ jaxen jaxen - - - xom - xom - - org.jdom diff --git a/pom.xml b/pom.xml index 31fcf8f75a57..2d7edd8763d9 100644 --- a/pom.xml +++ b/pom.xml @@ -1538,13 +1538,7 @@ jaxen jaxen - 1.1.6 - - - xom - xom - - + 2.0.0 org.jdom From 6b4e5ed9c8236490157bde819d570c64b3a2aca1 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 055/479] 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 0fdf045c10a6..bb9bf675584a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -806,7 +806,7 @@ org.apache.bcel bcel - 6.6.0 + 6.7.0 test From e92e5ee6d847974da9f2f530393af9a7a52818b6 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 056/479] 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 bb9bf675584a..57362fa75390 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -926,7 +926,7 @@ org.scala-lang scala-library - 2.13.9 + 2.13.11 test From af29486965dafd017bcc471d45898c4a1e9330c9 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 057/479] 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 2d7edd8763d9..8b53afa9e5c1 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 5152e99bda9f3ef9ac531f9bd460d60d9c8ba6fb 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 058/479] 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 40b61139ee72..7f48c5dde2a4 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 972c2d69c8ba890b13d9365f5809d1e14d20be17 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 059/479] 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 8b53afa9e5c1..1d3fed2d51fb 100644 --- a/pom.xml +++ b/pom.xml @@ -1456,7 +1456,7 @@ commons-codec commons-codec - 1.15 + 1.16.0 org.apache.commons From ad061c962ba7c5ac27e9649f9dac6b0750daeada Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 30 Jun 2023 21:38:32 +0300 Subject: [PATCH 060/479] 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 1d3fed2d51fb..15039783cee9 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 16b1104b0164eb397e2b2f8915282adb2a59ab50 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 30 Jun 2023 21:41:41 +0300 Subject: [PATCH 061/479] 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 15039783cee9..a071ae2c7a00 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 79178e3b62cf83e4dcfca1bdab8c483da89561a4 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 062/479] 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 a071ae2c7a00..36f6a880e9cf 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 9.4.53.v20231009 2.20.0 - 2.0.28 + 2.0.29 1.19.0 1.7.36 2.5.0 From 0d1dd13c842d625bd8ab6661281905963cde2cc0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:24:34 +0000 Subject: [PATCH 063/479] Update dependency com.h2database:h2 to v2.2.224 (cherry picked from commit 0d4c1ea63ab7cdd5ea79511228a5486a46c2a515) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36f6a880e9cf..5f349232886d 100644 --- a/pom.xml +++ b/pom.xml @@ -1688,7 +1688,7 @@ com.h2database h2 - 2.2.220 + 2.2.224 test From 7a38f0097d57907555b434f83492e80bcc065cc5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:38:10 +0000 Subject: [PATCH 064/479] Update dependency org.glassfish.jaxb:jaxb-runtime to v2.3.9 (cherry picked from commit 8d6f6e37b3beed98b9d20327cbb67ce683da9b3c) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f349232886d..df57ea816569 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ 2.13.4.2 1.3.2 2.3.1 - 2.3.8 + 2.3.9 1.1.1 9.4.53.v20231009 From a50534d617a3f14264a1750cc5d7ebbe3d737557 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:44:02 +0000 Subject: [PATCH 065/479] Update pdfbox-version to v2.0.30 (cherry picked from commit fe7800ab5a7d9b77e9783d09b000aff19be38c96) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index df57ea816569..f63ac3d053cd 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 9.4.53.v20231009 2.20.0 - 2.0.29 + 2.0.30 1.19.0 1.7.36 2.5.0 From 602e9e854f23932a60be6be9ddafa0f6362738f7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:51:40 +0000 Subject: [PATCH 066/479] Update dependency commons-cli:commons-cli to v1.6.0 (cherry picked from commit f67e0f65091b85d191d3694ea80339d42833ed9f) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f63ac3d053cd..be0c732165e5 100644 --- a/pom.xml +++ b/pom.xml @@ -1451,7 +1451,7 @@ commons-cli commons-cli - 1.5.0 + 1.6.0 commons-codec From f7c7abc36201a38154b2d3ec3246aa8bb9e9bf22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:52:16 +0000 Subject: [PATCH 067/479] Update dependency com.opencsv:opencsv to v5.9 (cherry picked from commit ab9279cbb88e8511f28fa01f7d2a005b61eca1d2) --- 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 57362fa75390..b79a088dc90c 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -788,7 +788,7 @@ com.opencsv opencsv - 5.7.1 + 5.9 From ed74d3198cf277b73fc1516e26f99b0e9ac74690 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:52:20 +0000 Subject: [PATCH 068/479] Update dependency commons-io:commons-io to v2.15.1 (cherry picked from commit 70646a30de182a23b3f7ee675ff74a9a79c76b1f) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be0c732165e5..e3b05649ef6c 100644 --- a/pom.xml +++ b/pom.xml @@ -1489,7 +1489,7 @@ commons-io commons-io - 2.13.0 + 2.15.1 org.apache.commons From 279a315301c7bf4df56bb95fde959c3256bd1506 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 06:24:36 +0000 Subject: [PATCH 069/479] Update log4j.version to v2.22.1 (cherry picked from commit a6978137203c6c8112b9d31c095bf8c7823fe8db) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e3b05649ef6c..66f243304b1c 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 1.1.1 9.4.53.v20231009 - 2.20.0 + 2.22.1 2.0.30 1.19.0 1.7.36 From 8edd8bc09bfe002f9e2840854a15bde7aab9d8ec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 06:33:06 +0000 Subject: [PATCH 070/479] Update dependency com.fasterxml:classmate to v1.6.0 (cherry picked from commit 07ec54832a7a8380a0d1c7b361e57938ed88360f) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 66f243304b1c..b69554a72d8d 100644 --- a/pom.xml +++ b/pom.xml @@ -1746,7 +1746,7 @@ com.fasterxml classmate - 1.5.1 + 1.6.0 com.fasterxml.jackson.core From 28d051e88d7f03a2b51261622d6b0b0fb4d49758 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:13:14 +0000 Subject: [PATCH 071/479] Update dependency org.xmlunit:xmlunit-core to v2.9.1 (cherry picked from commit 0958a98b1f4d339cfdca50ff65081ae0730b0d74) --- 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 b79a088dc90c..1b539a5cc394 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -900,7 +900,7 @@ org.xmlunit xmlunit-core - 2.8.0 + 2.9.1 test From a77628bd1e2e55b47492938ab68b0be7fb2b2ef9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:16:05 +0000 Subject: [PATCH 072/479] Update dependency com.maxmind.geoip2:geoip2 to v2.17.0 (cherry picked from commit 68caa1dcf8e7c5687fc250ed62704832d93aa362) --- 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 1b539a5cc394..920cdfcf4627 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -620,7 +620,7 @@ com.maxmind.geoip2 geoip2 - 2.11.0 + 2.17.0 org.apache.ant From 4840b1de834a00d8eebe7f3e2461ff77c2839482 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 09:06:03 +0000 Subject: [PATCH 073/479] Update netty monorepo to v4.1.106.Final (cherry picked from commit 71ea2a752665bd6eaeb27575bd3fdf3ac05eacd4) --- dspace-api/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 920cdfcf4627..b1aa3f246cfa 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -865,32 +865,32 @@ io.netty netty-buffer - 4.1.94.Final + 4.1.106.Final io.netty netty-transport - 4.1.94.Final + 4.1.106.Final io.netty netty-transport-native-unix-common - 4.1.94.Final + 4.1.106.Final io.netty netty-common - 4.1.94.Final + 4.1.106.Final io.netty netty-handler - 4.1.94.Final + 4.1.106.Final io.netty netty-codec - 4.1.94.Final + 4.1.106.Final org.apache.velocity From b603776a08c7367c15eef94f03e7eee3a34de79f Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 29 Nov 2023 08:33:39 +0300 Subject: [PATCH 074/479] pom.xml: update Spring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to keep Spring Framework, Spring Boot, and Spring Security versions updated together: - Spring Framework: 5.3.28→5.3.31 - Spring Boot: 2.7.13→2.7.18 - Spring Security: 5.7.9→5.7.11 (cherry picked from commit f42a981d1bfc588c5a42d918036110f2b92b1b8a) --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b69554a72d8d..1502c0f7c509 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 11 - 5.3.28 - 2.7.13 - 5.7.9 + 5.3.31 + 2.7.18 + 5.7.11 5.6.15.Final 6.2.5.Final 42.6.0 From 487202e246d7afe10e614da12e4afbb998cd4e50 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 29 Nov 2023 10:01:27 +0300 Subject: [PATCH 075/479] pom.xml: update spotbugs and spotbugs-maven-plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update to latest versions: - spotbugs v4.1.2→v4.8.2 - spotbugs-maven-plugin v4.0.4→v4.8.2.0 These are not run in CI and seem to only run manually when asked, ie via maven: $ mvn spotbugs:spotbugs (cherry picked from commit fc6a9ca5cb2dec321c2ad67f722dcc6a2e894bc1) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1502c0f7c509..fc20c0059ac4 100644 --- a/pom.xml +++ b/pom.xml @@ -294,7 +294,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.0.4 + 4.8.2.0 Max Low @@ -304,7 +304,7 @@ com.github.spotbugs spotbugs - 4.1.2 + 4.8.2 From 5342d540751c6a0837fcf2636222d40adeea3b4c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 07:07:13 +0000 Subject: [PATCH 076/479] Update dependency org.apache.ant:ant to v1.10.14 (cherry picked from commit 4e071b2428d66d7350abc451598f75ba647e3394) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc20c0059ac4..e89b5c9dae2a 100644 --- a/pom.xml +++ b/pom.xml @@ -1336,7 +1336,7 @@ org.apache.ant ant - 1.10.13 + 1.10.14 org.apache.jena From b2494c6be2a942f1b89d4b80b5b934db633cda34 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 07:24:40 +0000 Subject: [PATCH 077/479] Update json-path.version to v2.8.0 (cherry picked from commit 8809e98a18ba1980e99584b21ffb427ed70e028e) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e89b5c9dae2a..da087bdc9feb 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ - 2.6.0 + 2.8.0 7.9 From c7c8062f014fbf8cbc4609b676c31f6cc8c07e68 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 29 Nov 2023 12:46:35 +0300 Subject: [PATCH 078/479] Update hamcrest to v2.2 Due to changes in hamcrest packaging we only need the main artifact now, but we add hamcrest-core (which is an empty pom) so it doesn't get pulled in by other deps. Last, the hamcrest docs recommend that we put hamcrest first so that we don't have dependency convergence issues from junit. See: https://hamcrest.org/JavaHamcrest/distributables (cherry picked from commit 710feb798d2ad2192f2941fd5beeede30b7d8498) --- dspace-api/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- pom.xml | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index b1aa3f246cfa..5b578fa49d5a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -528,7 +528,7 @@ org.hamcrest - hamcrest-all + hamcrest test diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index c9d8285559c1..748f6ad2deef 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -156,7 +156,7 @@ org.hamcrest - hamcrest-all + hamcrest compile diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 7f48c5dde2a4..05a24cf7d167 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -502,7 +502,7 @@ org.hamcrest - hamcrest-all + hamcrest test diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 84b53171bf37..7e60e982ec45 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -273,7 +273,7 @@ org.hamcrest - hamcrest-core + hamcrest test diff --git a/pom.xml b/pom.xml index da087bdc9feb..7e078fa21f90 100644 --- a/pom.xml +++ b/pom.xml @@ -1661,21 +1661,21 @@ - junit - junit - 4.13.2 + org.hamcrest + hamcrest + 2.2 test org.hamcrest - hamcrest-all - 1.3 + hamcrest-core + 2.2 test - org.hamcrest - hamcrest-core - 1.3 + junit + junit + 4.13.2 test From 752d94efb4a32d4da6d019261f8ce25779843bd8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 07:37:42 +0000 Subject: [PATCH 079/479] Update dependency org.apache.maven.plugins:maven-checkstyle-plugin to v3.3.1 (cherry picked from commit ae12f1865fd18b5ba642bdd1852934c57dfbb49d) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e078fa21f90..9770e2085e53 100644 --- a/pom.xml +++ b/pom.xml @@ -257,7 +257,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 + 3.3.1 verify-style From 88edfbbfdfde82498a4e492e274bd5c0eabab485 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:53:22 +0000 Subject: [PATCH 080/479] Update dependency org.apache.maven.plugins:maven-assembly-plugin to v3.6.0 (cherry picked from commit 275757e6d46b9175b81748722114fcef9a62e1b4) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9770e2085e53..fe6a9fd93630 100644 --- a/pom.xml +++ b/pom.xml @@ -334,7 +334,7 @@ maven-assembly-plugin - 3.2.0 + 3.6.0 org.apache.maven.plugins From 9cb4eac8027c9cddbf8c3c3a7f6573b15fa4da87 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 06:31:33 +0000 Subject: [PATCH 081/479] Update dependency org.webjars.bowergithub.twbs:bootstrap to v4.6.2 (cherry picked from commit 2c1a45bc8840614690bacfbeb8fa4dfbe5b42d70) --- 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 05a24cf7d167..5f2a256f3e0a 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -354,7 +354,7 @@ org.webjars.bowergithub.twbs bootstrap - 4.6.1 + 4.6.2 From 4dcbe16e24fa7903a90bea99517ab5c63c226091 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 06:32:41 +0000 Subject: [PATCH 082/479] Update dependency org.webjars.bowergithub.jquery:jquery-dist to v3.7.1 (cherry picked from commit 6d0b5deb8e2553be8afc1f4892f5e74353b5fdc2) --- 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 5f2a256f3e0a..2165d546caf4 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -308,7 +308,7 @@ org.webjars.bowergithub.jquery jquery-dist - 3.7.0 + 3.7.1 From 4682db506ec5e7c89cfb2c8cdf8ba30dc3192f9a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 06:31:39 +0000 Subject: [PATCH 083/479] Update dependency org.apache.commons:commons-lang3 to v3.14.0 (cherry picked from commit 3f675d9cd5bddea7d31de39db44183b36941492c) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fe6a9fd93630..50a76f9cd0ea 100644 --- a/pom.xml +++ b/pom.xml @@ -1494,7 +1494,7 @@ org.apache.commons commons-lang3 - 3.12.0 + 3.14.0 From 186453eb3f472951fbfe7a6e869bd8ebe04f2482 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 06:30:59 +0000 Subject: [PATCH 084/479] Update dependency commons-logging:commons-logging to v1.3.0 (cherry picked from commit e76132b4d8c1984994bfb150e3a3abdcff6f69a8) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50a76f9cd0ea..44cde8d4c640 100644 --- a/pom.xml +++ b/pom.xml @@ -1501,7 +1501,7 @@ commons-logging commons-logging - 1.2 + 1.3.0 org.apache.commons From 1f67a2bffeaade8813a9f5b9b41d9c30235ffc75 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:42:09 +0000 Subject: [PATCH 085/479] Update dependency org.apache.commons:commons-configuration2 to v2.9.0 (cherry picked from commit b583029a7d1e3b6a56f31ef2e086147ff005bab6) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44cde8d4c640..af6df1ae9330 100644 --- a/pom.xml +++ b/pom.xml @@ -1474,7 +1474,7 @@ org.apache.commons commons-configuration2 - 2.8.0 + 2.9.0 org.apache.commons From bdb19787bb3a1e6042a018b5c0b9ab7d20ce83c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:42:44 +0000 Subject: [PATCH 086/479] Update dependency org.apache.commons:commons-collections4 to v4.4 (cherry picked from commit 6de85adeb7afa0d91ee0bf3273b8494d5b5d33c1) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af6df1ae9330..63e130b08105 100644 --- a/pom.xml +++ b/pom.xml @@ -1461,7 +1461,7 @@ org.apache.commons commons-collections4 - 4.1 + 4.4 - 2.13.4 - 2.13.4.2 + 2.16.0 + 2.16.0 1.3.2 2.3.1 2.3.9 From 762c0397fe252061b382adf5e3304f8eb29d1dd8 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Mon, 5 Feb 2024 17:41:40 +0300 Subject: [PATCH 088/479] pom.xml: update commons-dbcp2 and commons-pool2 Update commons-dbcp2 and commons-pool2 to latest stable versions. (cherry picked from commit bafb1b56570df3b4bfea045ccc913af0f0994cce) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8626865f2dc8..d3a0128ce341 100644 --- a/pom.xml +++ b/pom.xml @@ -1479,7 +1479,7 @@ org.apache.commons commons-dbcp2 - 2.9.0 + 2.11.0 commons-fileupload @@ -1506,7 +1506,7 @@ org.apache.commons commons-pool2 - 2.11.1 + 2.12.0 org.apache.commons From 9274a117597d85ad4e485091a6452e935d31b0de Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Mon, 5 Feb 2024 22:21:50 +0300 Subject: [PATCH 089/479] dspace-server-webapp/pom.xml: upgrade zjsonpatch Upgrade zjsonpatch from v0.4.6 to v0.4.16. (cherry picked from commit 20fc8e4fb7c5deb81f66d926712da7fdbe25a687) --- 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 2165d546caf4..5395169950bc 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -288,7 +288,7 @@ com.flipkart.zjsonpatch zjsonpatch - 0.4.14 + 0.4.16 From 6d5949729d0c7c68d01dfe277d5aadc51005cf77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:30:28 +0000 Subject: [PATCH 090/479] Bump com.jayway.jsonpath:json-path from 2.6.0 to 2.9.0 Bumps [com.jayway.jsonpath:json-path](https://github.com/jayway/JsonPath) from 2.6.0 to 2.9.0. - [Release notes](https://github.com/jayway/JsonPath/releases) - [Changelog](https://github.com/json-path/JsonPath/blob/master/changelog.md) - [Commits](https://github.com/jayway/JsonPath/compare/json-path-2.6.0...json-path-2.9.0) --- updated-dependencies: - dependency-name: com.jayway.jsonpath:json-path dependency-type: direct:development ... Signed-off-by: dependabot[bot] (cherry picked from commit db56de3887109404bb849413f5a02023ebb9bcae) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3a0128ce341..f940dfb97a37 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ - 2.8.0 + 2.9.0 7.9 From 1dada28c89b8ad514cdf0d538476ec16a82926ae Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Wed, 7 Feb 2024 14:35:46 +0100 Subject: [PATCH 091/479] 111719 Fix bug where old style of retrieving config value was being used --- .../src/main/java/org/dspace/sword/CollectionDepositor.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dspace-sword/src/main/java/org/dspace/sword/CollectionDepositor.java b/dspace-sword/src/main/java/org/dspace/sword/CollectionDepositor.java index 2550d947d325..3fe47cfaa62b 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/CollectionDepositor.java +++ b/dspace-sword/src/main/java/org/dspace/sword/CollectionDepositor.java @@ -150,11 +150,7 @@ public DepositResult doDeposit(Deposit deposit) // for a moment context.turnOffAuthorisationSystem(); - String bundleName = configurationService.getProperty( - "sword-server", "bundle.name"); - if (bundleName == null || "".equals(bundleName)) { - bundleName = "SWORD"; - } + String bundleName = configurationService.getProperty("sword-server.bundle.name", "SWORD"); Item item = result.getItem(); List bundles = item.getBundles(); Bundle swordBundle = null; From 6624248c7affed8f8b801f113d70c0912f26f074 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 7 Feb 2024 12:11:06 +1300 Subject: [PATCH 092/479] Improve default identifiers.cfg properties (Related to dspace-angular#2765) (cherry picked from commit 40b7427d884002a0916ec4a3e687280e9426d081) --- dspace/config/modules/identifiers.cfg | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dspace/config/modules/identifiers.cfg b/dspace/config/modules/identifiers.cfg index 63a9cda30f17..2660646af394 100644 --- a/dspace/config/modules/identifiers.cfg +++ b/dspace/config/modules/identifiers.cfg @@ -41,9 +41,7 @@ # Show Register DOI button in item status page? # Default: false -# This configuration property is exposed over rest. For dspace-angular to work, -# this property must always have a true or false value. Do not comment it out! -identifiers.item-status.register-doi = false +#identifiers.item-status.register-doi = true # Which identifier types to show in submission step? # Default: handle, doi (currently the only supported identifier 'types') From 09357b133e033f9ba9da3593ad650830a7d81143 Mon Sep 17 00:00:00 2001 From: MajoBerger Date: Fri, 9 Jun 2023 11:22:50 +0200 Subject: [PATCH 093/479] added failsafe while creating admin when db is not connected (cherry picked from commit efcf9dba20ac4c49516d8486a33e14f06db86080) --- .../org/dspace/administer/CreateAdministrator.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java b/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java index 81250e9c8259..58b85493915a 100644 --- a/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java +++ b/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java @@ -116,6 +116,17 @@ public static void main(String[] argv) protected CreateAdministrator() throws Exception { context = new Context(); + try { + context.getDBConfig(); + } catch (NullPointerException npr) { + // if database is null, there is no point in continuing. Prior to this exception and catch, + // NullPointerException was thrown, that wasn't very helpful. + throw new IllegalStateException("Problem connecting to database. This " + + "indicates issue with either network or version (or possibly some other). " + + "If you are running this in docker-compose, please make sure dspace-cli was " + + "built from the same sources as running dspace container AND that they are in " + + "the same project/network."); + } groupService = EPersonServiceFactory.getInstance().getGroupService(); ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); } From 4942fd272ce2259e3199f07f821236c05f9dae36 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 15 Feb 2024 10:31:04 +0100 Subject: [PATCH 094/479] 106812: Fix compile issue due to new test --- .../test/java/org/dspace/app/rest/ItemRestRepositoryIT.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 73bae1219d56..714ad0b419a1 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 @@ -2936,7 +2936,7 @@ public void specificEmbedTestMultipleLevelOfLinksWithData() throws Exception { BitstreamMatcher.matchBitstreamEntryWithoutEmbed(bitstream2.getID(), bitstream2.getSizeBytes()) ))) .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems." + - "_embedded.mappedItems[0]_embedded.relationships").doesNotExist()) + "_embedded.mappedItems[0]._embedded.relationships").doesNotExist()) .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems" + "._embedded.mappedItems[0]._embedded.bundles._embedded.bundles[0]." + "_embedded.primaryBitstream").doesNotExist()) @@ -3045,8 +3045,7 @@ public void testHiddenMetadataForUserWithReadRights() throws Exception { context.restoreAuthSystemState(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(READ) .withDspaceObject(item) .build(); From 2cbcf9006dcc07a6197f55be79db7602e90e37fd Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 15 Feb 2024 18:36:45 +0100 Subject: [PATCH 095/479] SWORD config fixes: Instances where old style module/fileName, configName params are used from long-deprecated ConfigurationManager instead of configName, defaultValue --- .../src/main/java/org/dspace/sword/SWORDUrlManager.java | 3 +-- .../src/main/java/org/dspace/sword2/SwordUrlManager.java | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java b/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java index c145c9d3671f..6672027003e9 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java +++ b/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java @@ -431,8 +431,7 @@ public String getBitstreamUrl(Bitstream bitstream) */ public String getBaseMediaLinkUrl() throws DSpaceSWORDException { - String mlUrl = configurationService.getProperty( - "sword-server", "media-link.url"); + String mlUrl = configurationService.getProperty("sword-server.media-link.url"); if (StringUtils.isBlank(mlUrl)) { if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSWORDException( diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java index f3b2cf439657..eee3627c4045 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java @@ -458,10 +458,9 @@ public String getSplashUrl(Item item) throws DSpaceSwordException { WorkflowTools wft = new WorkflowTools(); - // if the item is in the workspace, we need to give it it's own special identifier + // if the item is in the workspace, we need to give it its own special identifier if (wft.isItemInWorkspace(context, item)) { - String urlTemplate = configurationService - .getProperty("swordv2-server", "workspace.url-template"); + String urlTemplate = configurationService.getProperty("swordv2-server.workspace.url-template"); if (urlTemplate != null) { return urlTemplate.replace("#wsid#", Integer.toString( wft.getWorkspaceItem(context, item).getID())); From b97b093d6c97d47599ea7ed00a6b93fd2d3fd1b6 Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Wed, 17 Jan 2024 10:39:18 +0200 Subject: [PATCH 096/479] [DURACOM-143] Fix indexing errors & further improvements (cherry picked from commit d645939baf07d10d0f9f0b5e7b2722556400ff97) --- .../indexobject/IndexFactoryImpl.java | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index 55c99b168e7a..4b012a072f33 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -2,7 +2,7 @@ * 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.discovery.indexobject; @@ -64,7 +64,14 @@ public SolrInputDocument buildDocument(Context context, T indexableObject) throw //Do any additional indexing, depends on the plugins for (SolrServiceIndexPlugin solrServiceIndexPlugin : ListUtils.emptyIfNull(solrServiceIndexPlugins)) { - solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc); + try { + solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc); + } catch (Exception e) { + log.error("An error occurred while indexing additional fields. " + + "Could not fully index item with UUID: {}. Plugin: {}", + indexableObject.getUniqueIndexID(), solrServiceIndexPlugin.getClass().getSimpleName()); + + } } return doc; @@ -82,7 +89,7 @@ public void writeDocument(Context context, T indexableObject, SolrInputDocument writeDocument(solrInputDocument, null); } catch (Exception e) { log.error("Error occurred while writing SOLR document for {} object {}", - indexableObject.getType(), indexableObject.getID(), e); + indexableObject.getType(), indexableObject.getID(), e); } } @@ -101,8 +108,8 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea if (streams != null && !streams.isEmpty()) { // limit full text indexing to first 100,000 characters unless configured otherwise final int charLimit = DSpaceServicesFactory.getInstance().getConfigurationService() - .getIntProperty("discovery.solr.fulltext.charLimit", - 100000); + .getIntProperty("discovery.solr.fulltext.charLimit", + 100000); // Use Tika's Text parser as the streams are always from the TEXT bundle (i.e. already extracted text) TextAndCSVParser tikaParser = new TextAndCSVParser(); @@ -113,6 +120,18 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea // Use Apache Tika to parse the full text stream(s) try (InputStream fullTextStreams = streams.getStream()) { tikaParser.parse(fullTextStreams, tikaHandler, tikaMetadata, tikaContext); + + // Write Tika metadata to "tika_meta_*" fields. + // This metadata is not very useful right now, + // but we'll keep it just in case it becomes more useful. + for (String name : tikaMetadata.names()) { + for (String value : tikaMetadata.getValues(name)) { + doc.addField("tika_meta_" + name, value); + } + } + + // Save (parsed) full text to "fulltext" field + doc.addField("fulltext", tikaHandler.toString()); } catch (SAXException saxe) { // Check if this SAXException is just a notice that this file was longer than the character limit. // Unfortunately there is not a unique, public exception type to catch here. This error is thrown @@ -121,30 +140,23 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea if (saxe.getMessage().contains("limit has been reached")) { // log that we only indexed up to that configured limit log.info("Full text is larger than the configured limit (discovery.solr.fulltext.charLimit)." - + " Only the first {} characters were indexed.", charLimit); + + " Only the first {} characters were indexed.", charLimit); } else { log.error("Tika parsing error. Could not index full text.", saxe); throw new IOException("Tika parsing error. Could not index full text.", saxe); } - } catch (TikaException ex) { + } catch (TikaException | IOException ex) { log.error("Tika parsing error. Could not index full text.", ex); throw new IOException("Tika parsing error. Could not index full text.", ex); + } finally { + // Add document to index + solr.add(doc); } - - // Write Tika metadata to "tika_meta_*" fields. - // This metadata is not very useful right now, but we'll keep it just in case it becomes more useful. - for (String name : tikaMetadata.names()) { - for (String value : tikaMetadata.getValues(name)) { - doc.addField("tika_meta_" + name, value); - } - } - - // Save (parsed) full text to "fulltext" field - doc.addField("fulltext", tikaHandler.toString()); + return; } - // Add document to index solr.add(doc); + } } From 14f593e097d8947549b446e0f21923ec0c13e79c Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Wed, 17 Jan 2024 10:56:08 +0200 Subject: [PATCH 097/479] [DURACOM-143] Fix license (cherry picked from commit 324d2e3184dd35d2980e599f173adf18a7e31d64) --- .../java/org/dspace/discovery/indexobject/IndexFactoryImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index 4b012a072f33..f1ae137b9163 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -2,7 +2,7 @@ * 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.discovery.indexobject; From adec8e2e608f7e290c504c8d675cadf84a3102e6 Mon Sep 17 00:00:00 2001 From: haoueclf Date: Fri, 16 Feb 2024 16:51:41 +0100 Subject: [PATCH 098/479] [DS-9345] Correct the package name of the IIIF search plugin (cherry picked from commit 03fe58668114db529413d13d98af57c6e045e9df) --- dspace/config/modules/iiif.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/modules/iiif.cfg b/dspace/config/modules/iiif.cfg index fc1e9bdf9f8d..9a2a3bb0d13a 100644 --- a/dspace/config/modules/iiif.cfg +++ b/dspace/config/modules/iiif.cfg @@ -15,7 +15,7 @@ iiif.image.server = http://localhost:8182/iiif/2/ # The search plugin used to support (experimental) IIIF Search. # This is the class used with https://dbmdz.github.io/solr-ocrhighlighting/ # It is currently the only supported option. -# iiif.search.plugin = org.dspace.app.rest.iiif.service.WordHighlightSolrSearch +# iiif.search.plugin = org.dspace.app.iiif.service.WordHighlightSolrSearch # Sets the viewing hint. Possible values: "paged" or "individuals". # Typically "paged" is preferred for multi-age documents. Use "individuals" From dddd500bd19de851aa82773b685afe4967f8e6cd Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Mon, 19 Feb 2024 10:06:33 +0100 Subject: [PATCH 099/479] 106812: Add flyway migration to remove faulty rp entries --- ..._2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql | 2 ++ ..._2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql index de40d0415dc6..2e352e2ee7cf 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql @@ -8,3 +8,5 @@ ALTER TABLE ResourcePolicy ADD CONSTRAINT resourcepolicy_eperson_and_epersongroup_not_nullobject_chk CHECK (eperson_id is not null or epersongroup_id is not null) ; + +DELETE FROM ResourcePolicy WHERE eperson_id is null and epersongroup_id is null; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql index de40d0415dc6..2e352e2ee7cf 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql @@ -8,3 +8,5 @@ ALTER TABLE ResourcePolicy ADD CONSTRAINT resourcepolicy_eperson_and_epersongroup_not_nullobject_chk CHECK (eperson_id is not null or epersongroup_id is not null) ; + +DELETE FROM ResourcePolicy WHERE eperson_id is null and epersongroup_id is null; From d172865f520d3ad38dffb95372a2cf7a80f2dfd1 Mon Sep 17 00:00:00 2001 From: eskander Date: Fri, 9 Feb 2024 18:27:43 +0200 Subject: [PATCH 100/479] [DURACOM-232] solved Community/Collection admins can't edit logo for communities/collections (cherry picked from commit d1812866a6593c9efea88d555d64b057f7db3cad) --- .../java/org/dspace/content/Bitstream.java | 8 +++ .../java/org/dspace/content/Collection.java | 3 + .../java/org/dspace/content/Community.java | 3 + .../app/rest/CollectionLogoControllerIT.java | 67 +++++++++++++++++++ .../app/rest/CommunityLogoControllerIT.java | 58 ++++++++++++++++ 5 files changed, 139 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/content/Bitstream.java b/dspace-api/src/main/java/org/dspace/content/Bitstream.java index 451a3b75784d..5485735a2816 100644 --- a/dspace-api/src/main/java/org/dspace/content/Bitstream.java +++ b/dspace-api/src/main/java/org/dspace/content/Bitstream.java @@ -307,10 +307,18 @@ public Collection getCollection() { return collection; } + public void setCollection(Collection collection) { + this.collection = collection; + } + public Community getCommunity() { return community; } + public void setCommunity(Community community) { + this.community = community; + } + /** * Get the asset store number where this bitstream is stored * diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java index 53b63dbef1fa..a13c19d46cf8 100644 --- a/dspace-api/src/main/java/org/dspace/content/Collection.java +++ b/dspace-api/src/main/java/org/dspace/content/Collection.java @@ -135,6 +135,9 @@ public Bitstream getLogo() { protected void setLogo(Bitstream logo) { this.logo = logo; + if (logo != null) { + logo.setCollection(this); + } setModified(); } diff --git a/dspace-api/src/main/java/org/dspace/content/Community.java b/dspace-api/src/main/java/org/dspace/content/Community.java index dd6d978936df..d82e08bab72e 100644 --- a/dspace-api/src/main/java/org/dspace/content/Community.java +++ b/dspace-api/src/main/java/org/dspace/content/Community.java @@ -123,6 +123,9 @@ public Bitstream getLogo() { void setLogo(Bitstream logo) { this.logo = logo; + if (logo != null) { + logo.setCommunity(this); + } setModified(); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionLogoControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionLogoControllerIT.java index fa0732f6b774..8fd7f58d9cd6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionLogoControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionLogoControllerIT.java @@ -17,7 +17,10 @@ import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.eperson.EPerson; import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; @@ -93,6 +96,34 @@ public void createLogoNoRights() throws Exception { .andExpect(status().isForbidden()); } + @Test + public void createLogoByCollectionAdmin() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson collectionAdmin = EPersonBuilder.createEPerson(context) + .withEmail("test4@mail.com") + .withPassword(password) + .withCanLogin(true) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("New Community") + .build(); + + childCollection = CollectionBuilder.createCollection(context, community) + .withName("name of collection") + .withAdminGroup(collectionAdmin) + .build(); + + context.restoreAuthSystemState(); + + String userToken = getAuthToken(collectionAdmin.getEmail(), password); + getClient(userToken).perform( + MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString())) + .file(bitstreamFile)) + .andExpect(status().isCreated()); + } + @Test public void createDuplicateLogo() throws Exception { getClient(adminAuthToken).perform( @@ -142,6 +173,42 @@ public void deleteLogoNoRights() throws Exception { .andExpect(status().isForbidden()); } + @Test + public void deleteLogoByCollectionAdmin() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson collectionAdmin = EPersonBuilder.createEPerson(context) + .withEmail("test4@mail.com") + .withPassword(password) + .withCanLogin(true) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("New Community") + .build(); + + childCollection = CollectionBuilder.createCollection(context, community) + .withName("name of collection") + .withAdminGroup(collectionAdmin) + .build(); + + context.restoreAuthSystemState(); + + String userToken = getAuthToken(collectionAdmin.getEmail(), password); + MvcResult mvcPostResult = getClient(userToken).perform( + MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString())) + .file(bitstreamFile)) + .andExpect(status().isCreated()) + .andReturn(); + + String postContent = mvcPostResult.getResponse().getContentAsString(); + Map mapPostResult = mapper.readValue(postContent, Map.class); + + getClient(userToken) + .perform(delete(getBitstreamUrlTemplate(String.valueOf(mapPostResult.get("uuid"))))) + .andExpect(status().isNoContent()); + } + private String getLogoUrlTemplate(String uuid) { return "/api/core/collections/" + uuid + "/logo"; } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityLogoControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityLogoControllerIT.java index 3a0edc931049..f03e27b67749 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityLogoControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityLogoControllerIT.java @@ -16,6 +16,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EPersonBuilder; +import org.dspace.content.Community; +import org.dspace.eperson.EPerson; import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; @@ -88,6 +91,29 @@ public void createLogoNoRights() throws Exception { .andExpect(status().isForbidden()); } + @Test + public void createLogoBYCommunityAdmin() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson communityAdmin = EPersonBuilder.createEPerson(context) + .withEmail("test4@mail.com") + .withPassword(password) + .withCanLogin(true) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("New Community") + .withAdminGroup(communityAdmin) + .build(); + + context.restoreAuthSystemState(); + String userToken = getAuthToken(communityAdmin.getEmail(), password); + getClient(userToken).perform( + MockMvcRequestBuilders.multipart(getLogoUrlTemplate(community.getID().toString())) + .file(bitstreamFile)) + .andExpect(status().isCreated()); + } + @Test public void createDuplicateLogo() throws Exception { getClient(adminAuthToken).perform( @@ -137,6 +163,38 @@ public void deleteLogoNoRights() throws Exception { .andExpect(status().isForbidden()); } + @Test + public void deleteLogoByCommunityAdmin() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson communityAdmin = EPersonBuilder.createEPerson(context) + .withEmail("test4@mail.com") + .withPassword(password) + .withCanLogin(true) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("New Community") + .withAdminGroup(communityAdmin) + .build(); + + context.restoreAuthSystemState(); + + String userToken = getAuthToken(communityAdmin.getEmail(), password); + MvcResult mvcPostResult = getClient(userToken).perform( + MockMvcRequestBuilders.multipart(getLogoUrlTemplate(community.getID().toString())) + .file(bitstreamFile)) + .andExpect(status().isCreated()) + .andReturn(); + + String postContent = mvcPostResult.getResponse().getContentAsString(); + Map mapPostResult = mapper.readValue(postContent, Map.class); + + getClient(userToken) + .perform(delete(getBitstreamUrlTemplate(String.valueOf(mapPostResult.get("uuid"))))) + .andExpect(status().isNoContent()); + } + private String getLogoUrlTemplate(String uuid) { return "/api/core/communities/" + uuid + "/logo"; } From 61053cab8df8a05881a4290b2fcbd5b4539fecf5 Mon Sep 17 00:00:00 2001 From: Yannick Paulsen Date: Fri, 16 Feb 2024 12:08:00 +0100 Subject: [PATCH 101/479] Update of DataCite crosswalk to metadata schema 4.5 (cherry picked from commit 5db110a19f30f49a2e22c48d0a19044adfc008e1) --- dspace/config/crosswalks/DIM2DataCite.xsl | 111 ++++++++++++++-------- dspace/config/dspace.cfg | 6 +- 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index e92c371d6a77..5cc3eecdda77 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -3,15 +3,14 @@ @@ -46,9 +45,9 @@ properties are in the metadata of the item to export. The classe named above respects this. --> - + - + - + + + + + + + + Other + Other + + + - + - + + + @@ -246,7 +264,7 @@ - @@ -255,6 +273,13 @@ + + - + + @@ -295,9 +328,14 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - + - + + + + + + @@ -313,16 +351,20 @@ + + AlternativeTitle + Subtitle + TranslatedTitle @@ -341,6 +383,7 @@ --> + @@ -436,7 +479,7 @@ dissertations. DataCite uses submitted for the "date the creator submits the resource to the publisher". --> - Issued + Submitted Updated @@ -473,9 +516,9 @@ Audiovisual - Text - Text - Text + JournalArticle + Book + BookChapter Dataset InteractiveResource Image @@ -483,14 +526,14 @@ Model Other Model - Text - Text + Preprint + Other Sound Sound Sound Software - Text - Text + Report + Dissertation Audiovisual Text Other @@ -512,7 +555,7 @@ resolveUrlToHandle(context, altId) until one is recognized or all have been tested. --> - + @@ -561,20 +604,9 @@ Adds Rights information --> - - - - - - - - - - - - - - + + + + Abstract diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index d38ffd64339a..b7cc13e508dc 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -603,13 +603,13 @@ crosswalk.dissemination.DataCite.stylesheet = crosswalks/DIM2DataCite.xsl ## For DataCite via EZID, comment above and uncomment this: #crosswalk.dissemination.DataCite.stylesheet = crosswalks/DIM2EZID.xsl crosswalk.dissemination.DataCite.schemaLocation = \ - http://datacite.org/schema/kernel-3 \ - http://schema.datacite.org/meta/kernel-3/metadata.xsd + http://datacite.org/schema/kernel-4 \ + http://schema.datacite.org/meta/kernel-4/metadata.xsd crosswalk.dissemination.DataCite.preferList = false crosswalk.dissemination.DataCite.publisher = My University #crosswalk.dissemination.DataCite.dataManager = # defaults to publisher #crosswalk.dissemination.DataCite.hostingInstitution = # defaults to publisher -crosswalk.dissemination.DataCite.namespace = http://datacite.org/schema/kernel-3 +crosswalk.dissemination.DataCite.namespace = http://datacite.org/schema/kernel-4 # Crosswalk Plugin Configuration: # The purpose of Crosswalks is to translate an external metadata format to/from From ab56ad09ab78966b4572121c88dc66d0ce75233b Mon Sep 17 00:00:00 2001 From: Pascal-Nicolas Becker Date: Tue, 20 Feb 2024 12:38:08 +0100 Subject: [PATCH 102/479] Changing a comment on DIM2DataCite.xsl (cherry picked from commit 1a567827df61cff9931d84e8732886457304a7d5) --- dspace/config/crosswalks/DIM2DataCite.xsl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index 5cc3eecdda77..a97d127694ef 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -16,10 +16,15 @@ - + 10.5072/dspace- From 2c57c68a8129970b4abdaab50b750b63a34018e0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 20 Dec 2023 12:25:02 -0600 Subject: [PATCH 103/479] Add more ITs to SWORDv2 to verify basic upload, edit, delete functionality. These all pass prior to any SWORDv2 refactoring (cherry picked from commit eeee0295107ccb2a84e0b2f9adf92536155efb5e) --- .../AbstractWebClientIntegrationTest.java | 14 +- .../java/org/dspace/app/sword2/Swordv2IT.java | 333 +++++++++++++++--- .../org/dspace/app/sword2/example.zip | Bin 0 -> 33777 bytes dspace/config/modules/swordv2-server.cfg | 1 + 4 files changed, 304 insertions(+), 44 deletions(-) create mode 100644 dspace-server-webapp/src/test/resources/org/dspace/app/sword2/example.zip diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index 6556624c6b11..75b0143e3e65 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -19,6 +19,7 @@ import org.springframework.boot.web.server.LocalServerPort; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpEntity; +import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; @@ -94,6 +95,15 @@ public ResponseEntity getResponseAsString(String path) { return getClient().getForEntity(getURL(path), String.class); } + /** + * Perform a request (defined by RequestEntity) and return response as a String + * @param request RequestEntity object which defines the GET request + * @return ResponseEntity with a String body + */ + public ResponseEntity responseAsString(RequestEntity request) { + return getClient().exchange(request, String.class); + } + /** * Perform an authenticated (via Basic Auth) GET request and return response as a String * @param path path to perform GET against @@ -107,10 +117,10 @@ public ResponseEntity getResponseAsString(String path, String username, /** * Perform an authenticated (via Basic Auth) POST request and return response as a String. - * @param path path to perform GET against + * @param path path to perform POST against * @param username Username (may be null to perform an unauthenticated POST) * @param password Password - * @param requestEntity unknown -- not used. + * @param requestEntity HttpEntity to specify content/headers to POST * @return ResponseEntity with a String body */ public ResponseEntity postResponseAsString(String path, String username, String password, diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java index 95ec76251415..64e3db7dfc1a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java @@ -9,19 +9,36 @@ package org.dspace.app.sword2; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.nio.file.Path; +import java.util.List; import org.dspace.app.rest.test.AbstractWebClientIntegrationTest; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Item; import org.dspace.services.ConfigurationService; import org.junit.Assume; import org.junit.Before; -import org.junit.Ignore; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.TestPropertySource; +import org.springframework.util.LinkedMultiValueMap; /** * Integration test to verify the /swordv2 endpoint is responding as a valid SWORDv2 endpoint. @@ -41,11 +58,24 @@ public class Swordv2IT extends AbstractWebClientIntegrationTest { private ConfigurationService configurationService; // All SWORD v2 paths that we test against - private final String SERVICE_DOC_PATH = "/swordv2/servicedocument"; - private final String COLLECTION_PATH = "/swordv2/collection"; - private final String MEDIA_RESOURCE_PATH = "/swordv2/edit-media"; - private final String CONTAINER_PATH = "/swordv2/edit"; - private final String STATEMENT_PATH = "/swordv2/statement"; + private final String SWORD_PATH = "/swordv2"; + private final String SERVICE_DOC_PATH = SWORD_PATH + "/servicedocument"; + private final String COLLECTION_PATH = SWORD_PATH + "/collection"; + private final String MEDIA_RESOURCE_PATH = SWORD_PATH + "/edit-media"; + private final String EDIT_PATH = SWORD_PATH + "/edit"; + private final String STATEMENT_PATH = SWORD_PATH + "/statement"; + + // Content Types used + private final String ATOM_SERVICE_CONTENT_TYPE = "application/atomserv+xml;charset=UTF-8"; + private final String ATOM_FEED_CONTENT_TYPE = "application/atom+xml;type=feed;charset=UTF-8"; + private final String ATOM_ENTRY_CONTENT_TYPE = "application/atom+xml;type=entry;charset=UTF-8"; + + /** + * Create a global temporary upload folder which will be cleaned up automatically by JUnit. + * NOTE: As a ClassRule, this temp folder is shared by ALL tests below. + **/ + @ClassRule + public static final TemporaryFolder uploadTempFolder = new TemporaryFolder(); @Before public void onlyRunIfConfigExists() { @@ -60,7 +90,18 @@ public void onlyRunIfConfigExists() { // Ensure SWORDv2 URL configurations are set correctly (based on our integration test server's paths) // SWORDv2 validates requests against these configs, and throws a 404 if they don't match the request path + configurationService.setProperty("swordv2-server.url", getURL(SWORD_PATH)); configurationService.setProperty("swordv2-server.servicedocument.url", getURL(SERVICE_DOC_PATH)); + configurationService.setProperty("swordv2-server.collection.url", getURL(COLLECTION_PATH)); + + // Override default value of SWORD upload directory to point at our JUnit TemporaryFolder (see above). + // This ensures uploaded files are saved in a place where JUnit can clean them up automatically. + configurationService.setProperty("swordv2-server.upload.tempdir", + uploadTempFolder.getRoot().getAbsolutePath()); + + // MUST be set to allow DELETE requests on Items which are in the archive. (This isn't enabled by default) + configurationService.setProperty("plugin.single.org.dspace.sword2.WorkflowManager", + "org.dspace.sword2.WorkflowManagerUnrestricted"); } @Test @@ -68,22 +109,20 @@ public void serviceDocumentUnauthorizedTest() throws Exception { // Attempt to GET the ServiceDocument without first authenticating ResponseEntity response = getResponseAsString(SERVICE_DOC_PATH); // Expect a 401 response code - assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); } @Test public void serviceDocumentTest() throws Exception { - // Attempt to GET the ServiceDocument as an Admin user. + // Attempt to GET the ServiceDocument as any user account ResponseEntity response = getResponseAsString(SERVICE_DOC_PATH, - admin.getEmail(), password); - // Expect a 200 response code, and an ATOM UTF-8 document - assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); - assertThat(response.getHeaders().getContentType().toString(), - equalTo("application/atomserv+xml;charset=UTF-8")); + eperson.getEmail(), password); + // Expect a 200 response code, and an ATOM service document + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(ATOM_SERVICE_CONTENT_TYPE, response.getHeaders().getContentType().toString()); // Check for correct SWORD version in response body - assertThat(response.getBody(), - containsString("2.0")); + assertThat(response.getBody(), containsString("2.0")); } @Test @@ -91,44 +130,204 @@ public void collectionUnauthorizedTest() throws Exception { // Attempt to POST to /collection endpoint without sending authentication information ResponseEntity response = postResponseAsString(COLLECTION_PATH, null, null, null); // Expect a 401 response code - assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); } + /** + * In DSpace the /collections/[handle-prefix]/[handle-suffix] endpoint gives a list of all Items + * which were deposited BY THE AUTHENTICATED USER into the given collection + */ @Test - @Ignore public void collectionTest() throws Exception { - // TODO: Actually test collection endpoint via SWORDv2. - // Currently, we are just ensuring the /collection endpoint exists (see above) and isn't throwing a 404 + context.turnOffAuthorisationSystem(); + // Create all content as the SAME EPERSON we will use to authenticate on this endpoint. + // THIS IS REQUIRED as the /collections endpoint will only show YOUR ITEM SUBMISSIONS. + context.setCurrentUser(eperson); + // Create a top level community and one Collection + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test SWORDv2 Collection") + .build(); + + // Add one Item into that Collection. + String itemTitle = "Test SWORDv2 Item"; + Item item = ItemBuilder.createItem(context, collection) + .withTitle(itemTitle) + .withAuthor("Smith, Sam") + .build(); + + // Above changes MUST be committed to the database for SWORDv2 to see them. + context.commit(); + context.restoreAuthSystemState(); + + // This Collection should exist on the /collection endpoint via its handle. + // Authenticate as the same user we used to create the test content above. + ResponseEntity response = getResponseAsString(COLLECTION_PATH + "/" + collection.getHandle(), + eperson.getEmail(), password); + + // Expect a 200 response code, and an ATOM feed document + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(ATOM_FEED_CONTENT_TYPE, response.getHeaders().getContentType().toString()); + + // Check for response body to include the Item edit link + // NOTE: This endpoint will only list items which were submitted by the authenticated EPerson. + assertThat(response.getBody(), containsString(EDIT_PATH + "/" + item.getID().toString())); + // Check for response body to include the Item title text + assertThat(response.getBody(), containsString("" + itemTitle + "")); } @Test public void mediaResourceUnauthorizedTest() throws Exception { - // Attempt to POST to /mediaresource endpoint without sending authentication information + // Attempt to POST to /edit-media endpoint without sending authentication information ResponseEntity response = postResponseAsString(MEDIA_RESOURCE_PATH, null, null, null); // Expect a 401 response code - assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); + assertEquals(response.getStatusCode(), HttpStatus.UNAUTHORIZED); } + /** + * This tests four different SWORDv2 actions, as these all require starting with a new deposit. + * 1. Depositing a new item via SWORD (via POST /collections/[collection-uuid]) + * 2. Reading the deposited item (via GET /edit/[item-uuid]) + * 3. Updating the deposited item's metadata (via PUT /edit/[item-uuid]) + * 4. Deleting the deposited item (via DELETE /edit/[item-uuid]). + */ @Test - @Ignore - public void mediaResourceTest() throws Exception { - // TODO: Actually test this endpoint via SWORDv2. - // Currently, we are just ensuring the /mediaresource endpoint exists (see above) and isn't throwing a 404 - } + public void depositAndEditViaSwordTest() throws Exception { + context.turnOffAuthorisationSystem(); + // Create a top level community and one Collection + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + // Make sure our Collection allows the "eperson" user to submit into it + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test SWORDv2 Collection") + .withSubmitterGroup(eperson) + .build(); + // Above changes MUST be committed to the database for SWORDv2 to see them. + context.commit(); + context.restoreAuthSystemState(); - @Test - public void containerUnauthorizedTest() throws Exception { - // Attempt to POST to /container endpoint without sending authentication information - ResponseEntity response = postResponseAsString(CONTAINER_PATH, null, null, null); - // Expect a 401 response code - assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); + // Add file + LinkedMultiValueMap multipart = new LinkedMultiValueMap<>(); + multipart.add("file", new FileSystemResource(Path.of("src", "test", "resources", + "org", "dspace", "app", "sword2", "example.zip"))); + // Add required headers + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.setContentDisposition(ContentDisposition.attachment().filename("example.zip").build()); + headers.set("Packaging", "http://purl.org/net/sword/package/METSDSpaceSIP"); + headers.setAccept(List.of(MediaType.APPLICATION_ATOM_XML)); + + //---- + // STEP 1: Verify upload/submit via SWORDv2 works + //---- + // Send POST to upload Zip file via SWORD + ResponseEntity response = postResponseAsString(COLLECTION_PATH + "/" + collection.getHandle(), + eperson.getEmail(), password, + new HttpEntity<>(multipart, headers)); + + // Expect a 201 CREATED response with ATOM "entry" content returned + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + assertEquals(ATOM_ENTRY_CONTENT_TYPE, response.getHeaders().getContentType().toString()); + // MUST return a "Location" header which is the "/swordv2/edit/[uuid]" URI of the created item + assertNotNull(response.getHeaders().getLocation()); + + String editLink = response.getHeaders().getLocation().toString(); + + // Body should include that link as the rel="edit" URL + assertThat(response.getBody(), containsString("")); + + //---- + // STEP 2: Verify uploaded content can be read via SWORDv2 + //---- + // Edit URI should work when requested by the EPerson who did the deposit + HttpHeaders authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + RequestEntity request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + response = responseAsString(request); + + // Expect a 200 response with ATOM feed content returned + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(ATOM_FEED_CONTENT_TYPE, response.getHeaders().getContentType().toString()); + // Body should include links to bitstreams from the zip. + // This just verifies at least one /swordv2/edit-media/bitstream/* link exists. + assertThat(response.getBody(), containsString(getURL(MEDIA_RESOURCE_PATH + "/bitstream"))); + // Verify Item title also is returned in the body + assertThat(response.getBody(), containsString("Attempts to detect retrotransposition")); + + //---- + // STEP 3: Verify uploaded content can be UPDATED via SWORDv2 (by an Admin ONLY) + //---- + // Edit URI can be used with PUT to update the metadata of the Item. + // Since we submitted to a collection WITHOUT a workflow, this item is in archive. That means DELETE + // must be done via a user with Admin privileges on the Item. + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(admin.getEmail(), password); + // This example simply changes the title. + String newTitle = "This is a new title updated via PUT"; + String newTitleEntry = "" + newTitle + ""; + request = RequestEntity.put(editLink) + .headers(authHeaders) + .contentType(MediaType.APPLICATION_ATOM_XML) + .body(newTitleEntry); + response = responseAsString(request); + // Expect a 200 OK response + assertEquals(HttpStatus.OK, response.getStatusCode()); + + //---- + // STEP 4: Verify content was successfully updated by reading content again + //---- + // Edit URI should work when requested by the EPerson who did the deposit + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + response = responseAsString(request); + assertEquals(HttpStatus.OK, response.getStatusCode()); + // Verify the new Item title is now included in the response body + assertThat(response.getBody(), containsString(newTitle)); + + //---- + // STEP 5: Verify uploaded content can be DELETED via SWORDv2 (by an Admin ONLY) + //---- + // Edit URI should also allow user to DELETE the uploaded content + // Since we submitted to a collection WITHOUT a workflow, this item is in archive. That means DELETE + // must be done via a user with Admin privileges on the Item. + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(admin.getEmail(), password); + request = RequestEntity.delete(editLink) + .headers(authHeaders) + .build(); + response = responseAsString(request); + + // Expect a 204 No Content response + assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + + // Verify that Edit URI now returns a 404 (using eperson login info) + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + response = responseAsString(request); + // Expect a 404 response as content was deleted + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); } @Test - @Ignore - public void containerTest() throws Exception { - // TODO: Actually test this endpoint via SWORDv2. - // Currently, we are just ensuring the /container endpoint exists (see above) and isn't throwing a 404 + public void editUnauthorizedTest() throws Exception { + // Attempt to POST to /edit endpoint without sending authentication information + ResponseEntity response = postResponseAsString(EDIT_PATH, null, null, null); + // Expect a 401 response code + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); } @Test @@ -136,14 +335,64 @@ public void statementUnauthorizedTest() throws Exception { // Attempt to GET /statement endpoint without sending authentication information ResponseEntity response = getResponseAsString(STATEMENT_PATH); // Expect a 401 response code - assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); } + /** + * Statements exist for Items in DSpace (/statements/[item-uuid]) + * https://swordapp.github.io/SWORDv2-Profile/SWORDProfile.html#statement + */ @Test - @Ignore public void statementTest() throws Exception { - // TODO: Actually test this endpoint via SWORDv2. - // Currently, we are just ensuring the /statement endpoint exists (see above) and isn't throwing a 404 + context.turnOffAuthorisationSystem(); + // Create all content as the SAME EPERSON we will use to authenticate on this endpoint. + // THIS IS REQUIRED as the /statements endpoint will only show YOUR ITEM SUBMISSIONS. + context.setCurrentUser(eperson); + // Create a top level community and one Collection + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test SWORDv2 Collection") + .build(); + + // Add one Item into that Collection. + String itemTitle = "Test SWORDv2 Item"; + String itemAuthor = "Smith, Samantha"; + Item item = ItemBuilder.createItem(context, collection) + .withTitle(itemTitle) + .withAuthor(itemAuthor) + .build(); + + // Above changes MUST be committed to the database for SWORDv2 to see them. + context.commit(); + context.restoreAuthSystemState(); + + HttpHeaders authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + // GET call to /statement MUST include an "Accept" header that matches one of the formats + // supported by 'SwordStatementDisseminator' (configured in swordv2-server.cfg) + RequestEntity request = RequestEntity.get(getURL(STATEMENT_PATH + "/" + item.getID().toString())) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + ResponseEntity response = responseAsString(request); + + // Expect a 200 response with ATOM feed content returned + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(ATOM_FEED_CONTENT_TYPE, response.getHeaders().getContentType().toString()); + + + // Body should include the statement path of the Item, as well as the title & author information + assertThat(response.getBody(), + containsString(STATEMENT_PATH + "/" + item.getID().toString())); + assertThat(response.getBody(), + containsString("" + itemTitle + "")); + assertThat(response.getBody(), + containsString("" + itemAuthor + "")); + // Also verify Item is in "archived" state + assertThat(response.getBody(), + containsString("f%iAVQZtR0tNJk@EuW{=)X|)00nvm zegvk2 z{Ku}UO~ugt;_)@Zdw74v3aAkdOp~#sFLtiKQ0E;XCZ>u%In^^VJLcyrfs3O=XEr+3 zBke`FjmIWp!FJs|%}bj~x!yGY92X9bU$yffL!&Df?mZcoyWO&mHuXIt(I$fFuYJCVL{n4TSLqiaNPvO{x z&fB${ti9}8c_R4y^u+4WtgShVAh|-FYIRq+i<1ln%6KQ zu@D%!6d83f;5GVvznbNB8y_b%m#eN)xKi;i$7SQr`vy40{dmffSm4A9I2eA#2tE^>-Vtx1o1g@ z5EbXD(2wU2Z|6k}q1{!#PYsJrJHQj&rymC1)i|tH7HEqrJkZI*CYadVsB+LqYQ;Ro zvdF_`5{u=~jJa#uM^Gb&a{tV%@gNautLFHpM5i{bnj)~h`=wfVl+Kr1r>}#@OARr| zOnnUwJ<(+i>)eA1PhDnO1Zkzz`>S&@rW23O02NmiGSP-p8JPx)#gx6h_{O-K5-P(X z+C|~HI&giYQ~Tjrnikp3!O_mzq+?yiz!V5R4TPq#Gx=gicradIBGw@EyweQA$p&b? zCxe;|?h0M_g#}sB43kNMsklUu^Kavl&-i{Ra*k($P@;~O<=_=*63Y^5q?nsh7MOA< z=P|Z`wV0s!4C7T0ToBQac@Cij8SZ_plSUM}WCRJQxkgw`00D0#e?E223-=tWOept& zGMpaaiD{e-e*@Za3?VT|LN0k&U^2}6lGxGQdJ0SgJnJ^TPFyZ6IDAauJ3Tm^UZG6{g|@awcTfW^*+E z280@I2Gt(e1uT4CM$n;Jg_fr>z%$xFUL*t2kH`y5TiDkrrYy1pB38qO)xBhf=xk4J z_u*j-T;*-Ww%Qc(h4azrZEKD5Na0>!jg)&^0}i2h3I_mr%p_eu^G*S=COEv>EuaSQ)C zjsJrV?rb_@3kM6YxXu4vTgzeAEP;@HK=5Dxk1A~g)bIe?Odgw2w&CB{OuwJ0koDTG zzjBFKcE|)M{GAAd$&I<&{z*JdO|+u3uI6|&-e7;r04nRTYc~BIppKvW39EKoL%G<` zJk_f%!TR+cv$$YpUl~)X0vGaoI5$Ki3}8EFMXL2VIlSJ4nor5|TSUA3hHH90a(#g; z-Rcf-jbLbED0MC%1fcb!8fy5buLsuT3Ef`Re4T5PL@&)PI+fqhmP6DM_{ZVth@60q z2Wix@PcY%JB}i&saM9gMg^uvp36J78AHxErg85x=Z8bX}EEWA>6Y37VHLZugZc=^0 zrNLBBk&#T&>TE%|b&@0!pDcB+$!*>`3{x-LTh3IzeJc0E>GUl1_I2d*5L0-XX)NVW%o)gc{ zbLdlgT!h-s*FiBaVg}FspqkH&O&c`C)xJ1Q8-^y&>LP0VF|@uQKe`b%hafw9!u&*K z@dTk_+=knkt>1rvA^oB`LKkp!+_Y!)Hb_Hmv&D7A`dXkjzF+oVjH}3ONfF9^n|coK zQtGt&j)E{I+nJ(ch1%YII0<3Q1A@#`SNpX*uaBpG->izt!QK6J4>U;H!CmNomIZ^`x(t&!w>j@-bG`0-^IF@_ZmO=X%iSO9 ziPO-~|5%tANfO;pT-aS#T*^oSfe8az^RSRHFckhSOsx%q4m<@-{0Wt%!-c&L7((xY z4+{$WSOE{?<&}7WcMI!Elc1+M`p%Ka7r=C|)nhIsB4MplA@FM4Pzc6gVWvP>D{g|ZMM zIar;UO|Td>ff|?a%r3t(^C2eq#;(nE&3HsjU74lz_YE!K}U5lL~6wC4Ui~Qt{LLHola1X*18@#36`1lEdCGptEbfEPu|#j&G9$ zDxpbFatCN&w=^hB4+=_3Y9|HgO%0%49G9qi=50ul(WBX}2l?rz zM^b(@t144+2XE5QDft8n23 z>)z|%CI#FLyHakRx`jI9hnKz=D}?d~-rUt|71SF0xKFSSjY&oB3%Nk%-yQioZCU6d zZdCR@@B%M?vJO&SgF3hexJqIhq;NCJKT!;gb2?CDH`W{K+LV7!d@1#{?C}*jB^phL z$=TEkvFjw1T(k2T#Q_aA)u-}o5!=>h$Z@KGMeD>=S72Lgc?^3h#9ima>7?^}&X^Yc zxBGdNa?hyw1{{+;*Udb`O~Z7f=f8UzQZ~L3NEMOQb+hghvBXVL+7d+-z`x6<;{)aG z&Oa|V{7i8@yOzN4&Ou5OV)H?`2N??E-Q(46HO)sV327gGLY)Ts-6B4uf;?860E(6G z7^cFEosY1sLX;EGE~>Rd;E#vMQ=+&!@88QJ$M18wFZgp0TU7YUDEJ)k7%v_WDo9D~ zf((fL%st5jb7Qze-j@-lNJC3M2=O}h0@%stxAxCjak+d%K@INM%Dl*O_9i zcKJX?zMMAxmPehZ2VLXZmvl}HFR6f28H;C+;FUrgZ-mRh!_Xm|k!;{s zNwO)-?}%~z>CAIYzRhGsrE}fJL5M1*LEQ{JHgnntD5qPSJ28y^O*@x- z9@Bn|qzS4a?oHeQZS=Wvvukmy>v69hk);Dza+v5KJ-?>to8=;j;=0M9fW6mNhD2!+ zmpdy+<8j<&6Y^L8q3t50MyNc{by#M30!qou6n?lkk$i#nd&% zPr@I{24`Wq?%bjtwpg{e;54cRR3q?gAm;4Vdv$EB^cy{ox=HWhdi2+|Mmj@VzCS#( zKkwfMKD&L2y_caeiKd+#TC;9TAsQso9@nn$(8=UcsX}~!_mS0;eew*6-;s|kq#<`$ETCv7(DW!Aaim2(<@P6JQO?h59ttqb&ny>JVd;Ok-i;pe@D*5s% zM1kI)P8vPw!b?mk0Lv(azf)SE)^BbvV|m@-n?))iUeUII-~RZzZf(^4D$2j`N)XJj z$u?JFvP3-e!Y&=5p5iT9#p;xG@!PfFXBk_+G*7r7^i*S$oI0&+y|)fDb*#<5%oH>) zl6am$`L4`PP|ozdUarsRtFk_F7NUR1*Jiv8-w+tGmBzMjt7-_R3}7t(I>W|eV?~W8Q)9q{Hu-l`-xk-p*sFgBMx_~-av`31@+SkmhQ9_ zeFK&u+f&?MV9J1n->EPXG*wM!by3xB_DgCL=Y^Vzj4(F?g)apmVp7yeWVt@k#;7`f zhYo)e0|}Mx$f!~tvQYsYX3ij}-=JahPgk2*3(00ZIV_q<9!FDK+5OKm21avP!1UlC z2-!p%sh0q-3@%?BN9z1S(mPQ%)wYS)p~61e!?BRPq$k}$w+sz^a?Mvf-NAO($K9Uv zv%JS%GLzMkjqgd(`srDSsPyShR^PZZ(|RjvTeKi=YErysb!`Z$bJSZF$Xy-(15ubw zZrLH4uv?uvs|>r5QAFSfqc5tiVG-V#yz~e39F#hhIn?LfIRUiP+lUfusK8ky@nY1{ z3_l|VWmAo?!zrPr-~r=}?l%fk#$3{5W@)n}&waG{vb23m9P_sGiH&-jh&cj0lB&{eZpg?sP%+0k@zSbJ+RdJD zn^7xewlP`utK+kdk?>0>TFHY>+|j6Yx^bO!fA7&6W=qOa*mw?-2n)Deq3zz%OI1n5 z?LA@?b5rSqa%N6Y zkbbZ-E@=gM;!=gBD|c1lxI8(S4QlsLogCaG$!iApQCO(97% zrUtYYOKVa8^diV@c7yL7iK`RGTI%i}P$d<`+)jh*KY=Z`DD4tO>Ss@1FJC3)Qs`(c zzTj5N(7N?xTr7)R#c5`EQGB~w>&LB?7x?~dmTqgz4FwG&GP(ZwITf8U+3$hwM;0bY zCFRC|?SUV9n!fLw)-~s#N`j$cRSI9BlRGCO=?Dk(7j`B!~?g_I~h<3X79w^m36R5%~l^~OwNj~9@b5bOxe*$tXvg(f5Z3?TrW2PVa~ zGxpZ?+MV9)$8Fw@15&q$Rrq9nWN%ZeS zaA=5!9d$}-$;4TMn$GRbWnG__v%Sz!{k0FecV>A!s=Y{qeF~{tT-#O#3+uQ54eSnT z#3^D2zTPe;{ny3~c>Rja-y&`HGdqOHfBj=i*u0E4`WRHAp0^&vKm4vVB{DR44cVo; zqVS@SpM6|n6mbQ@Z za>SXo7E;3F0Z7)AsEE3)73eQ%hGGZo0v7J+rW>VjIND$6KQ%1?1Af4A_e90tziM7R zz`wNlN}uDdrS4S7EW=Wy)zkMbAC8MR;0P!uO=PamJxS31|I~kkL&b4a9_^J+F@>q?;vBbP!8 zPwy!LXWUNI(~w=g)~6R4S@8Xidchs{?5pmrxlA(j|1=an^_wS;eJhUCQEC#OXS7rF z3$V+Oe{;QESAq)N9BCI&|HHc2KD=J)CjE;)$w57L2j(5={^{*yRjjp0CC2syi@it{ z8wvj2<*5IBLU!E%NB8f6fsSj`1zju&L}&+0hDrTF^WS6hUHIN)*c5{OyPk#5SHYL) zNi1{x=a|pIqy@sI^Vj%dcy4fJOcK^t?1^iykaTbDNd4Hfp0*<$HVV-yMHSZ-5^pPY z%o|TCz6apjny3s-x9a3&!=AFcOVq;d2EhwK9HXVZ)mlq3n=zk7-i1%8rQMta(rXAC z4~Jn}aeFufj2Hb_+@*w6nG&SL7}J|1_VaVP?L`_=RAdBiZCk+yFRHq$2u~b8upGZa^?0vqPs{u};avfEf z1RQv_%q}2oEYPy;yEyS8x)^d7nYauqPgJ8S4UW#kIvYmMYWL^63zZkvc1v7fH`ZcE zn(0{dltgt~H(<*{G>hLY0gNeG`#P_*k}k={`nf*P8Un7TT)WoY%xAr~g(R-(o$rcX zX=Q#7oPpgCUOlmyuUxw0{n}-K+Bw6Tb8lNA&CYsNn35fKZTLC$zBcOH1rG8FBS9Y$!j7yPH3jc*GXLLLNuV z{&g{KaeyX0bhM{UI42c}?00Yu>X7_5Y;d+M`QEwAP}yqsM%8Hql!8Y@F!{;b+@d`S z*VS%0)^A9c8Y|6=Qqjki7@$*7SY!q0jw;rLRC0 z>D|_oy{BjykD!&P?I2gSMJJc$+hDifK(an?30xw^u}+_kiubd*cZO+4G`xvVUcMdS zyZTo0;cw5A%w%U`=}01PGIhm?z2^&iG952#(V`-W*PhG%E!A`EMIrZ9a-cd=SJ z(1SZ(Ka@2GuLKrg0zQZl^T*m{?xoGBymBY77Ho%2^`vG4|`Y z%5d1vTuoCZ0l_cnmll1zFnhanBBE@rtjPbfoosk#-W*7%XR!K}X;)sLbw! zmTg+T+eqw0dIFsQb}Rbd#Nex|$8F3KDsqcQnxjyLqOPN&d3KQd(^4%YBp%bT@Q{00@eZ7+h^Y5BOgrMc2x-m%0}u1 zu~)$eU5(wy&?rVqb{iL`6=1C=3(wO3&k{1bUQUva;)eRV7eOd1$O>N}SCTVsLnWJP zsS^p&Hf(1cGb9%5N8rW*Sq^-n;Gy7DC!(y_SyoiY1C8;ALq#)1SQ< z++kw`dU^pKnTvHWQsAj1t~X_cpTV7$ASRY2;H|^rx?Q}Bij0oerUg@OW;r4$Jp3<$ zkuuGp2sMo!FK)vNZ^P12ID2_H+h6h$oC|)`9A*}R;*knwjFLv5kc4kZ&7I>%6xxly~Dtrh9BaUPlLc0VkF-_kT*Cw{XGcTyMka- zRhPfbrc+;hESUy?K5aV(IAbAY(J>Jl$%wi8k>DIqG1Zz^!$$prBzR@NB_p=Xz=wkRgUoNbY)PGvO=r z{`J_&nNZ@y}@kq9Z> zRWy?ArZJh!!phU{I{UAr;-po5w_pN}+ENHE&+lav-T!N#^Vdbb(*E8T?!$qTPC|K? zMtw8V%eL*4CqH3~eNZ(Zwqg`(OL&@Ms5x7pa79xTe1h=)nn^%W&0dx>S5;|5-5&U< zx1*vXqprC^aQI-T7Kd%NTvyGF=?{AyoyYYVY-rYe8EpE9ep#1W=P5q0P}4mk7JTAMX(CiSodpT;!7}*$NM4;sH{HAwR0mdq0c0 zIN1!hsqk`TcW*E&wOyM0t7akQkEpU}6LAB@Yg!Y*ezv`|gir&bKUx%1Ni{Qvg z(;rp+EJ2}%_VUlq5-er0EEwlMq4c#CX%y>Y%;5@g zQV^whT-7^ZCwG?G{|gx(Ki&>oh=N%=B0A_o3TMK95L+DQtecSWxB&$?l%Vz8A#75h zVvRMFi@^6??_bl161B{~hi^akj-=YsuAx0$%-)?3d}_Y$zKs~?gRCX0++-vfgYaF% zhYVrh;^DLqe%+Y>0!cU7LEkmoaN!$5>+qg=JyF^XgntHwpxhi>%X0?cE^F?NePGqTG&N&`9*_Q6+B#C-tjZof#{S(;-1a#p5iF}>+JmC zyE&9pUhuvq_FD`wz)pJy3GX+n!PbKi6{(G+kbVc`^4@N38AB#Rf2flr8jXT~cz)12 zFj`>G;C#0i3&DiEFB#FvAmnbyLq0elog+<8U802LZptJ*@&qqVx zO@u;+^#u*h*MyhpgX4XI6h&D=1_N^X)1>1cMFR1%yHaX?w-}IERVsJ$-Y`%)1IndIN<7?x;JkEWpTB#`NMxhzcKc&JV{vL?MAID@V3^u zTOmQKO#W?KK(XhyGfD%`%j@vD@=qtJ6JbCsr1`5e1Y_C#5}HilMWr9<9T~ehiWro?ha) z{sluv4Nc)Nlgl&NW}tiv717)izaRwHNEEXbOqMiUsy%lqk=_Zd^<>lc#`p3>muiY$3EVV+3Quj z+AXT=ThF!#ckcPS+mvw6a>XkgmChrIcQ#Vr2^Ns0qan}KS$rw;va+~y9JD&LcwmEM zcPo0}?qMIqX|8e-wOi2GB1hTjW&E+(E7Tp9?**Rai<&x0SwywZ?(( z@DkSey8;xtRhzQTa|+VUZ@EaiFuA!ZjE6vBnD|kZf9Z&FR3o>p-jHz1%x2Jswf(g{+Wc&^#BPmO{3eb?nt=rilrBwGDA zjVlwftNrnGFXTl$$g@Y$!Fjs8AH3%yjDnLmJ)EmyB2y+ko>P)N z46oTV<0eU%s!$$%?7G;#jE=lUkt~QtXzPf6sApXbh6F=si!PtnV@!F#T~0H|K*FMGL?MbRazRg?QBt*@Rl9B&>4b6Y-+ppWq!M*GM@j7(Cj;XA=r#;a6d zrV{=W*}V4y{8V-ZCT13!KC&Bjb!PH4iD6dM1!CuVeTW{SCmBG0Ju~iKnNsVxDe2&1 zAz{r8M=x?-eeeI-8Cq3c>p<5g9}eAl-nN$ro?=q%wz@`uN+}kZ<*d$;7Ao_jJt~eX z7Bke<1xwGTp&fnmHX-il=c-}~UD4T?bXZ#_?l}yF3MU>j(v?KjAZ)OzoI$8UPPCh_ zNP9m}d@*C`Sf0>HXfhf2C8l?V6GwQ^jIyhWMH*CX#neignLjv*d z79=YGaI%xT*5!$K`qgDL(@7DI_+Mko#b_ynngr*t(1{Ut;QvCXv>D;}mQBKQ%f64< zu4)r7llT72+YV+wa2ZWjI31+j!qS+5=#MW|2=eOIEKx%v&;vs8E|Q(JefD;npcF}a z0>t2qh@t$S0MkEi;fU)D?$KH}AFS9Vs+3CrLB@Gdxq)o`BG^bSquPhC^la_5v3aU@ z@(x_AZnPS^Fj-4Z>Adpu0`z>5bpRjJY_GLAON~OI0a|NgYFaG*m=;{lW`CBw+y(H1 zW=Rj*ywhpk!$;5M$bCekN{$yRo$vLEdvv41)w(20Bk#QJMg0UFo3;z(xPVMc-sHw- zagy120UKzY`j*f18UXiQxjxCB#gtW5QbK55!%$Q{lu>-3L&NVW?)6v$Kcn4|coGmw zBTc=SCMy9qS2=%>;`s!%QjZt>W4`|BtJ#*(0F)pJH3(uk8Oi~UBkAGeU~22TK#~?b z#n()7%1CAiFr1=1T-*X|R!u9AJ0*0(ZI#ddW?pVb>g&Q^H2KU084 zH+O)+=N+Df^U(iE$0wNheR>O6%lL(B`YE42Ev}9wQdrxV5?ol&+nrk)8{D{Qr>wQM zggiR^d|vQlZDnmqXLm_!Z(C|jQcd`Xc&bL4Si>KPdD2eL-`x<+%|ikKUI@n01d1U< zqzTAHs)0gvUT9@pK5b@hV+0LI2=ICN2vdUxVs{fG-K)R$gJM8OBrOQ2i>)ZEY>XUc z;2|vt3N9=~4uE?q_cm095oX*|H&5b4nD3t-c!`%IV`Q2BE$^0MY1*zq2|@O_3VkY6 zEZ_|bVkxiR0gFc&S_WEMd~@BV4L^jnpPKp#K9+pF2Kl#dpcXe@bqn_xjqystiZj zEPg1~j6uhHt9J!Y3$D;#YYPR5N`51LJ-wqvuiA;vSL~`0$QQXi7d2t~Oi(VIS|+JV zz=kJBIKi%iZ?$4`2A}f807DQMpPW~LWb&aexbdo%*?OmGWK?Tw$}4YgGR0UkLi?UkOkcRGi;WzgwJJyyKXvx3+)tKK>O1 zQReZ607lX6)*nq>`d-ZegQ~Ex+A}b!pPQP3HZTkX(iq&^CCLB-H2@9-%98Lm#@)jp zI!+8As70%l@kfIjc)@v-?!*%Ojr$nQLK^r%02}+oy-ar>u#d(lZ*p7tfDVJd5YQAf z+!GhZfSB#q`liEZ>Kf|@xb;^|esw>}?kwzVEXwUJ;NN*ecow7-23FoMjQu{$56yuX z82c4=MdY>?&qiZ}ro&!nt#5ug72ZOlDtH)nC0F(p_CWG0!0z28>Eh!T3`qMh@T~nA z@F!t=^oJV9KPO-!{HA~$&@XyR)qqm|>JOcWIVs7Mf|LN_mY)vzmo}Vq1@aJoq z4krT-*2oA<*g`NSg%FYKSzpM#k%d(V1Q;_jQQ+4P2oW8f96bPOPd)}|1k$tc?=2e5 zK>su5ZzNMw;BOdHpUAWOmHJYQ|M*&_hT!kW+Hd9f(AWn;%s0W0-p%SSGR)!h?5BH7 z=6!zK46Q#`c5v>^^s&`zbsKbxQPM zeElo)-?eA&XIs-q`T+iAh0{`Ot$hZJF$&trRI%}Ohl$jTWHG9DRe@rY-tXTGJkreXko1MDrs~Hg`D0sS`VtVwbEjewA(+p;RYprpJ&n}s=gE?NZ={3i zWK@+N)pKysIJLK;1s{FE+?A%V5K-HLv_)b{edI(8p=-EL^?dx<4{N+82 zzJ-vlM;rYHJWhOphBqrw69kbZ6%IiCV-7po8;l!8Egil7&z=4UNcr<7QV)}S>4eQ~ z#rhV_0q=fT_+-hzD;p?Tqc%srl3<+*lK9LqS=DrgX zE`N&i#jh{+@#Kkr^gtbNRYR#`F9Ntx+ce`U9dpXz-=YxLmgTA!LnJQc-B-efoWp;) zi_M!f%=W{F+UJWNOTMX!xLMms9p-r`F1Go z6h5)7Wwqb;G#G33H@pGoYoTiZX&lLw1$A1(3+aw(+fKJ1{usjXU;Oiv>>*nJ&MXO9|I%>**5YP*yFtqM zL1CL{I2^Pp_;{wdI^2`=23!$`2Z<#}Ed}5-ot(s0%3Rn$@72&S@i4SDgzhhiNCJ3u z3AE71dLV1;T{YGZlTa3-b;76a-n@Q7ssbz0B4;NZE_pU~{nXyYa+b?*1y*(0h6Y6Z zPfq(h@x7J+5y5YE;?S{&19gw@n+uOzw!P}(xLb3De``^Xp`-d+|nx4+qhgi9zRR^ z$Tg~O1uyB2eRBrD08_FY1Hh*5;CYyhS?Jta^V{1ylhUJiYE|&p#s67yIg#W;x~;^X zcy&=b-lt)1yMJnhyDJ{Lb)RhOR?pJx(@5Pe;{dNR8eGYA|l`TSBk(o(s}OOd)8{w zaS?x`EN^aGs@sES{*h|{nX1v5O9x7pSilu?9!0(R-%Z4EVDL736sNNrC>UX*(7tnW zYD_glQW^_9$AIJ(P3sneg<*nAAO*l0$N^Rv*<25%t7mrF2*_=w9d%ap1J9p!-Y|)R z4g0pe&Lky5wQ>cPptZuqB79UWxl>!kLor>!ydUCSRmaB<-DMTX0C2UI2sVU6T=RoM z!o;p^(}i4qy;OoxGDA{ez%c!xvP}Bz9CDAE=a_J9EPvv)op+xIU1p!uIOwwD8iEk_ z@;67sT#K$X=ambmtV>Lbeg)3uSvJlYj@5Z4JcW~1tU8O+V>6`V9rN4I%*VqL2$nQN z5sE5Rc8hP$Tmk!_rZe)-&FrcIn-+<>ZJVzs{#KtcD+9RK7WQ0S!-AQO>F~n!sc^f7 z0JT6Km?Lgw%R)_&QF^M+k!vTfpr603<6&}|%{!hExb@$%CpUZM&(r1?zKWB%UD-p4 z^Q_riXM;33&F6fk&(zHBH?$%@9T`NQ2o+y=k#|arp1eVcGU{F!;OOMxTjl0TTb^1C*4Gn8h{a7$pr%BBtsS_WGy;S zFHIRO|Mti8rYWi2?OY=0;SEEc*)l#U+K_F(K*wc|MC(aNg2>~EDk6mf)SCm13{+pE z8RF(F7PkA^>DKj=VLi++KC*i187)dJE!DrZ(A7+Bvx z)7pXw+)?zpd1Q=gSswTjM{GRcP`@Qw2Mrpgv8{yWR5t`T)j^E4IMr!y{a9L!^-H~okD1 z#pah=Cp4HpZ4=|oA1gcQE&u&iYLSaiyqks}pzcFe`HFSnKs7LIp8Dz|ultjAQ5>7K zq<9nevSmCewDqY}9J&xw)e%>Et&K{p?o7=#(=Oa*wJJ)m8Np0X@v~d;5M%kK=iP@9 z*;@{n2X;F6j}K0KX;jr%6uN<`f}XTDrb7>e^N|*fF;G!14YCT=ECWz{$i(b0?oI8x zu?Qm%u_i6M+b?uZBf6rV5`edI=KC=#@_ZN|#+UZFU-B!}JfnF?QMjdnxLM}Do1Xq% zbgU-CONl794^1&e>av)ZwDl~jZ4r#}#3*EhKUO9SZ-B5oQco(xTADsUWV`D^8R|3f zf#F1{LKTsB3D6-yOoo4*@(CNM5Yq|KqKH4o$w|WLiBs>G?UIwe4gxPMbhcN+Tbi{)*6x`&doy$NQ24uX*x3zegsrG+ zPWhgGoFgUFqf3r~nGTbymSSSH0m-PgICAhWL0Ogu@>ilYFx=WU6p^1-QTQWHc5w`N zaDEkPA7m!p=pi2tyUQYGKK9F<4S3~TRh8qWG|AHqxR~<$7g6&ks#o*U$JJg}mOK25 zDaWc7EmM^F8rrke#K>%&-b$Ef)Ta`C?Y5h9c*5R$ z25JmU)=&4O4Cm7D>W*?-v@E8X1Zmsuvb;#@{GNT96s)CfJa;o^x>XYPmfw^m2UqqV zV%i(Z${N6vl(aIRoz8TlBrm#AIWc%HA+3QQO?&YZ(bTCPSxt#L`Ho`6ZKDpe&yf@+ zrW@=&HSlIY%T!<5<|qmek_shz_fp$cAX(8F# zycQWF_SRd3hO*<5lkwaPm1%*DMV?v%jXxRI%)|!Oor_gwyV8y>WAjN*+r49N^&#vP zaEtL;uI|cY73rEZ+*zra&abE~!t$(9Ij=w=VwzIIdV8~fpDG;#--;psNupP5XeqSN z!dYTspkO%v7|}5)!5Yz^Od9=JKi5g@?DwkSrX=bWMteu{&*n01wN_hu>@tc^`HFmSBj6Mu(?ZC3jL|C`ZUt4Ihr* za)LW_Qv~we?KuYRRujly9sV7s3+{g4E(NDjbIDriizk5XW;~aU{l_1~N=`Ri<>C4I zq}AE22TI$2>V+?Q9VNl($g(Ix}z21 zPVUAgdTD5AxKgb35lFWO$U|xJH+J#$MnP)Xt}vvg=ep}6#OzF6wnw}qR3BaGFN9X$ z7|TRq^=6f|5ITn>2Ogg#b*)SYzI!Q;*#b zq30%7H%3YuBe~v5yS?GKrE`NylVq8jJ!@ys!FL^fH8T(VWx@1De-zLqT!ZXjV0n`1 z5I!Ahmtke=^jvik2lr#Dd{HjB8g#7JxNXc+!vn(aD1p^Bb_tE{+YB5mTIrqYOQM^f zp3kWB{giu~1S5e-EcN`!hno|jEkg6@O*N)ojj9;2j+TO2v1hjhlU%+UHJTVsmLeR+*2NMz7x%Ioiojg?%7= zwm0aFj@EKJJ2dycXF$VEv3>u|ZDL~Cu6%84@#sWVNP5|w;6W1Va$M9$5tP+02Y7|= z#jVrf!KGEKkBR-N=k;Sb9=B&*s>YOqC%IKnGryt~A0}Oq{rWO$QvB)5>ak$&^h?vO zy_r{apIO{Y-;UHCQoKqp88{{_0KR4_>8;Y1XVeYy#E8jd=gcJW(+&d1$VV5K6LYRh z6HT`luTv3COQM`VB8ml}1g z)_QuiKRibcAc?*|Pn^i<5dM-^U0D7KnxSrn!$oF(!#&Egmy543`4#f&oz z*!S)9TYwoS9g$+7%pU5Kyqd^0>R`InP?DKMx-VTl8+VEd8&#=c<(Sq@Ptvt*7q|MR=fg5gq) z>7mlC=gOaYFW=@}DO-VU2}wq9KwsUzk#6kxJF4Q#7D9yU>_T6voN}9ItOjZorM#2G zzs%X)UO;VF_9|I;dEjTIJ3wc&DN`igbCs!3xY9vbDhLCln-RRFgDQ>%h3MUpC+5oS9)CEKV#Z2)&pl=l=~Y5gHSH6@;bz!-;aUhGw@SvQS56wo0m}c< zCX*p6Qz>CvdP4DzjEf{&p27aZPjN0Oi?~(nBB*%eD^aWBGz>PcL!PBmv(f(Rk>Lw+ zorPOZYS4hkA-#iwU1+zgT<(r#GUn>kXqHl{wI<7Cq%gNNYg0F0mY&0_ZFws@BP@Fw zOK*;JCZ4N0(x+DX&b-Y&)!a{u$d7>JBcAxfDl5vfTy zwWx6<+eE2VdiF&PBEi3#n@9w^_|oTN4^f6QXqV$Fl@+-TYf#eOKsr?>d>wV^?s$4NQHQ@dzMR4~`&SIGwjF5*^~hi@)EE>46-{FG}J zw=^!C4H($BtRE4wGb>A$y(;%0bGIn!GE1-{QpmQqicvpa$z4~lQLloJCjk8ikV|NV&sy?2LNqx`B;R$lHP{@TL2jqp2+%G{7^s-QxMh zP|4rVDy{cRF>@#y@hzKX?X<8Ep9RQCA1<a>2d`lE5o|Eq#_T}(_=?he)2R1+uH++ zxrOX>!vRs6yrfx?xdN}%3B9AC!Pm!CO$72cMV}27OhmPx6@kv*D3M3ZMVvbqCNZ|r ziwUQIerXL3i3xEp{_IsM3QE=(iH&GpT}>0QjSMdkNLmURUCQm-OLeMha@lS!JmW96 z&Ozoy8d+j>t6&{qzE@)9jlNc@q6SC!P^Lr_arYcuv^Dv{e>j1ZIW{YhztF#nu*46U zqkq5LU-a#lwHp|$4BmW`Dk{tEW{j3}H@rmDh5SmHhxhWm&=znKnkxq7c-t}Kb3m<8 zm6PSqRCHbBKLnc$5>iQW70^#d}wrjr@1%+ zzg(qC-MKNym`@pfXCQ(p>2cP@I<}dQrk1W1Zpw5Om1Z7jci3;r6s_}34%PT1C3bi$ z&s^u5J<|xM<_ft_aDiE|P8-rED>F#rZgZsJ?y{dWg`@Skch#oZ=7;4aZlR^sPV>!7 z1H%J~vmhrq`xDgbmRFQV7s1l4GcTAS{dAW!wC^>|SD7>Tm1N(T?*cy+mz0*Wb{7>X z*&lj!N2R}RS5sTu!fklO;HaB#-ADD5zfQJ0s)f$eBioyH@BO0+oSkPMh^HO_b3M%`(wj4tMW~?+p!GKB*I) z#~V8LwtWJTPIC-(eguKgdW;^e+b5ERtE)jN&snC1X7Tea+N}4(8WK2{Xb&s>o$PO8 z4D11Y+371N4buTVtVH}l>tI<|P;X{# zVTNTR)m-WW(qEf>E>?PKAh}8#{kYRt{Cos>^o@$}YEfl5#^3~}kxis1RNbU)7Z5Hf zYDr7#Cb-mY>OVWEN0^4fKWX-)P+dl?8fCP6kH{$eMEvE`_e{7S$+`~92dV1Ru!8zx zSiZUONS;I)6%g{ZmG}TU`^JF;RC#J`B-Er*jiEnwC4a1BT6aZBG!$y%AEEoiYio^% zjtWU|jnm+M+j&4_gFauxDGr__*Rduod)(et(7lF$YrOGWkSClf3&9OZutmW_bTq)kdX`y z2+ugV^bvX!`H{}^c}qL*9xl5vzLUk{!16Us;V&D`!9@6Mm3uh9NRtN%PrNrkurcr| zZlJK%bY;6*pNX!qsGy)eB!|5c+bG`$$hXc#0~lYfdqx8|3oO_kqj|(fKe)BtYY5#r z(@?ckgjBoN)>cCxF159YCkqB$uBB1K6cBI(jvWNTUxxTP1`p-$s+vv0AqC*^a6g~? z*fW;IjbEw6ju##|pY?EN`_et6rwygW1^R4~nncJWrO%v+hHddPyzA;BX)EJ(*un9sN+NG@ ze#wBuL#~?AztJwC!`XJIB@(4>$F*)j>Mu35C9(L;f7$G35Jt<;F*njKpat}7<6;*R zJl1Ltidq}*_<+etU65)HfsnJrm z);gqmbT^XGhKv*&p=r4`-#ChRKgg}~tX1#zUq}7lvHwQgv6z%_UYuFrCXn6ce)n^2 zCqI94ARRAspg4KLgO@Qwl7R7EBBflW?&0n{s?ZqawV4*A{KLok5QXJeYbb65>d!=d zhI7qM-3RDw(MSweov|se+s5@}wQmT0#GdS((hLt2&-2biM3fup-R2+qZfXcNjm8dUDR!~y1@%f`Y}DK>k9UPh^n zOKGScYL9(H2a2y#rggbwv@Y6 z+-ySeS+`NQ_mug9(2eP^=P~FbefztycbTB=z8jp+hrTELy%iAipB8fw(yyv~@S-EG zS;|kIqY|TcJT&)vsEB8N-gb0wsaJ4v@Wl7p%6;)3gQYE)0MedI49+U>56)Q zYYRz4!f*x*S|NaLG??bx7sL`FpYvhSMCpu}Fr>NV30>P%XJw`LZcpgogYM{LP^hj_p#jf?F^N%`M_LiMLc3LVw}{~&mFsZF@n>~ zoPM0L|DX_b7&~g$`tbk7NlIe-$>(^5h zdPb{y_K+q7aHmSy9q7Zr_*za&OCCNQQssYt^x-~ag@F2aRg@@ZgSp7E$4r_+R>7y`SI(0VJv9zbWp9st=YS19f$H}RjYr0 z*2k=mfDG4BZQS92DO?`$?UcU-kW%wz5kAd;-d( zZLTR-?-MIG)?Ul2EU(lj977sr7(+TKUGwxms@A?x=U})&^grr zSCGIheoLZ3bcuLrszJNK)~HbB%f>d+HR80;D_c}Pqu}k#?x}ur=H^MJi7=JOe)+hV zu6_x)@(zPxOy9KpT{bxXdV^T@gtAA)5`Vd;#z9}1zR+Q8x?WJj!Y*Z6nqNj|exCkU zv|R%;=A{q+8U3Qe9kNc?@KM8QPz&O-NGWvB;+nGiKM>RK~r~~!gJ{zJ3#8VVS(Om z%U>DVv%l^$tlSF~b7*Lcen(N4HcYV%t^Zi)O^m~tN;BT3LJ{uvl=CSL=Q^vTLGRd@ zGS{$j9C5^(6Kr^{+?MDlFb5?_N7V6kwV+La%_2dcb@fp_(^H%qak>QI3_bc7OBqf9 ze9{WTa6Whqy+YXgETw5Ze6tDt&OE$fWksE!G{^Ev-;$h^eaSvbm3MCfj5AlAyt*oG z5PplrcbJw)t=sxR`8#*%vM+l*6BKhIJRivBrdYz`K4lc&eaXuFJSre(Fh_M+zwQ`b zm&5#0!_C-yUOpdI2q#D#PBV&A32B5Yxb+RkE`ISn`Q~A<@TS=@W2yM%H9=PoTZK%+ zOna@O(&zE*0GBdIgD%C=Zsr>8Lb1b|?ZosrxG-dDDQbp&U;P;!_1Z`N-o<9YrLERj z)LwSy@A=4RtXI7{Z%Tz?Sxciy%f|{Qf+x@fgU03aW4HWS)bn}7Y6eoBQ-yO>$X-IF z^>HA5_Gf&8W{vV z-8CP7g-GY@D&Br@hxjj)McPp5xe?-8up3yft$(srcca2MP`X1S3PwnbbB#XDZFUJx zXf?wM}x`DzeyNzKokO;B%O5TcIf+V}}Toz>$4HoODSa6(k?B_WsEHsJ3_i?n7 zW>uzid-)mHaG_r52<420OOVr|NY_6f;+&R zRbcKK2nRDqHBDtL!0)XlYiC+Mz#mx&3516M7YG8O<>CQ>X+c0BKdk_W$B0|r#mpXV zA?|2x52FPFxW%0ptTaSL|D6N$A8S`Ue0=|(RWYNOcH1u6C&34zLB0EC<8|_?;;aV5$Sp_s zJUUQNK1infgXGs1!MWP8fFqpaJ6^HQktOF%d-JmKU1|cVQeJ%GRv8?ZSN(z-n6@P< z=Kz0^;^%s2XA7~WKNhzZ63Ra>8fYwTt@K}@P-8SoKN@ccqZS?U{d5Hm4gL~J@Jsi2-1vgrd zp5}+YcmZSD#7Rs-MH-Qa?qBQG$q*O7V^kknFXzW|ZD5OS7aCg(_5ai%tq-ih^&2Jg zVSVPaoO9-u@?R!G;J-AVThrCt)bcOvvCCr>*e>ne-8Gs|5T_kPHkiUDYWcIIn zFo0VZZs}sZ4j-%KLkMklh$wRw0|`2jjACL7Md^@18y}b8E!3@hl|nQwf>_PYG&5I zzmUHtlkd+Kp9ahs;fl0?In(~}puw#SvxJ-d9{At(!2)1fDBu00%OVl3PWM(c?<;8L z=tKl$!Y@NZi5PsJ@=Enxp!j_>}u7yZMBNdHAcApg4Vbs@Ze z@p$fe5Sf43yjQqCfV@0^;k=-GyMNrn5sp&#&o--+AkS~JP#ysuATK`{%mDZe_$XW7zFu; z91jrmpO^rcAN)^D;2*jI`~v^t~s zz14(!!R|}Nt${$?4-C&=Vr)P00T)X*G9GY5Jy#b1OW8`^#O%?by$?$ z*0+F!NQ!hz3pmWc07FWHbT=}-+N!z zJ^Nn2m3ytV_x!Wy6hy^Yfp0j_>Dsc}8rrJb($GOvY*f|;X6Sr;02!E-3H$>U`y-+R z5I41e!|VX!7Em}$6lQ2`1QQTIw};!opqA*)$#g$?(Y@GkkXtQ9G|*la2e| z_ps;oQAcOI@^EG|+2xRkNYYtwqmL{=0tE$;_NgdwD~(W;*E8zRi2-eWPogpUj3|VZ ztflhQ=w{ZmcCsR93n$gH9H=^@w!p&&`4=Potoyx%kBtJwb7j3K6(vM}b*gH)UZzL~Dq|Yjw1D_e^kFp7t zN1x0PqTKqnReNlvc_5^FzI=;Eb{dXp(U8T9@mVEw^#G$5rru+1C5I@<{wiW!BuImu z{cGF3*)AalW?+#^!i_A!dE?aJ>2>mPoS)8Gnj`;tbilg#?V^mEcFXwUnrW^bHx{!_vcQ#0Im* zgx53aH=ev}PeeV#)>&B&`gDlzG|I`d6D)^EZ(q@RXs|v}t3kpNMo6ZUUN+@^rrzp4 zgqC8%Ti~O7h(`N^=n5B&N{F5BiJAc-mKL?|hb1d#%%XrN4Vd3Dh-Q}6Y4NWryeLO9 zIIrJK?Aa)v5-D|v;JIOk;{m3htfLigybH!uUm)avMyx<~Cv~d2>;8O!9(hh**&oml=PPE8u(7@$(@a0ZEVAH<1ODt;Yv2#d91h}ip~iYG!+D$9L1gF$bf;m zK%>vm!(5ro{tU>7R%@qpHP7f$eWH_7tVhTYanYCUG*Cg|9te6gA>B#~xU8RwLKniO zZkG86I{Djn6tGAueyG*pH-*hOBQN-1OCoBDH;G}bp7C8}ZK%c+lDC#TH{8+w-gvW2 z%`F}qm7{|a=RMQo^@McL*=K+mRVnzx8kxE@T7+2}z5G9lt&1?=6hdw(^_d{J~ z(phy^^5&>Pv|bhwZo(N+NuBlOh5z7MhB#BKU*i9|L%L+v^qDNK1=y_)%MPPvT4%Ai zvi(4>LFe?vc5G#ILoK}KxV65-y9x>s=}~UQ=BVc|$*Rt8Yk-sqwLLTSByLe zJf~%<-vim)2rCaMX~|)X)U%SG8-k+_HYwJlooN6^y_e!mAqVT$E7qG2DG#|gI9SgG zCo4>i`k^U&?4T333jTNOm@4{clp}nnux0{LBprobseP1_DVb7 zIZWsI0sgMW*2cEYV>yrH#jr_)d*D47;i*uN+3O>%!s-0l_ct`UrB;<1?}#(K3d@s#2SsbWw4FzP|aPW@JJ#1HoPde2TH_XrzN zUy3v-+S34~3N0i}v6(|H>7rJ18oi`$;|8M<`G7+bhWejz-41K6cFb2K_4}=j^U@Tw z5zA-pc4WnIKS|fP)|}S(u~On!hm#6*aMcIKSLBjvRCQ3RDT;*#0^`!2+wcwxU)W33 z7|t=@zFO#sI|4QPHYYt$Ja9a`c;NEJd*^SLp|?eV;=kjk+$e+vf*Cj#?a^@P_ zh55ewezh>k_R0cXN2_4;fxSRe@Mu}Xb>?S!oo_CvUN^*Jwhy9mc7jXAtHmMr%wwil zpSd_tYc_>m%Grl3VTS%9t>v{Hk%wSc6T14D^-C-XH41*OMLBavcEZzaZKfsjs&)y! zVTVZXw-g-V+2ts-xTogR1p-%7X|8MncAp4dk#KjfuEiOVpt*Q8rxh%}7}mlacx!gJ zb@R-c9&mItBn_kW*CE+n8?Yn2VyGBA>P7p(a)h}1iZA!=Vz9fh{!^MI$09U)t>?zk zgi>@ytts&bi)k)C#uokgiH%z(iyP8Vew7x2eF=rY(j!hi{zKSzztt^YE+J$mLLJh1 zS)KTcNrU?}{X`32yC|<|5E6{E>*Q5Rx&E8EPx<%wClA=bo$6@8Rz_T4nVfLxTL~*% zEjIQu=>2?1-192^gCv`z^Y0sCTU5kPumZD|KlSba^7C~S%iS~`RM$s7*?rW^wUm;w zRm`Y-TNW>-?4f5R=cr~Ze!U`=%hb6>D`Ft*maO+}{PFrpuMjia!=v$c$IVdCYOD)A zrz6Ha<`aD{Dyp(0oES9K)!ptNgOyFj9Wj!jz+=o5tXL3uU8Ynj_JT&xG`8Ke>?)7K z&)PE9wWFIDr5JYTfkKMInPlrT6$Ke%5tmGxdSN;b;TRs74Be&2TS>~McXHEYlIdii ziuY2W6*-oV3S~w^stJv)lfpVhdHFhu6kq}hHlxf%-L{UdN#Vi+_np^{$LA!IW5{+AnD&7x1v;*kUbOh{h zN`M?2zd$C%HgaY8xdtfvml?D`{IB-pWab#fv2@4TJ;!8r7WCF|mkSEr@d|4WS--cQ zW4-(Oqvl%jc{PtZA6>>_?a5iat%CDajaU^IOQ;^ z4#LyrkAjSRra9~BuZ<3Y8r)4qvINFg+GH2NN8&TTJAjxpkzZh*AYAMc`4U0=XC?=K zeNhO`=7a|ZZM{^CqIh;u;iI;X&$g`NZFIR!$IZMV{L%T9t*kp-+_H9t7SxkE{ z_LDt8%2k_iXn{e6+A8W>p#%x?wM}FI#ViBzW?;x>?I*pT+9U zwd3S6w;6`di2D)>F#Xcsqm`i8Ol`kmBzH1E=V;})Sj*yT-(j?gc^j*j5Aj|lJIsE% z((QQc(p^C;SRkn0JaFl8H)DS`0+EntKtE zYHAA)BPPnBtA&Dp`_gQ_%qSd>XB0_CD=Djw`-5eAgTs6ZG9DU-TAhQ>^_X2NFE5a1bOK}4 z3*2E)`aLaf5DyAn&=ZnEs)3_z>H84x% zK_KH=??OMcKS57LTBPwv=AD`4RV;gMWo2$9XOg9<*l0XWbvi8#*Ql0`MwFt47`k2Q z>mDs7u>yz^{*=YjZHtfRZ?z}m+v2T(N!h(mrJJ0Im!N3v8rB8X-j>+YrO1v?C7)D% zX61}3LHp7~`T zq3)nfxcRcM@Rg_QuK!)}!_COs)v_}e5vQW2ru@9fA9iT`2mI9(twxUfn@$ zw(8N*;=;VW$<3>ZZ+?0l0p~JZo0ZDpgLG@OYwpvR;MHF7e!10w4la@dEx6N6Ib?97 z(P+%%YxbLg-c1M7wa(q7<648pi5Mi~kbxLOHfiDw7G+zdDDPh35HEr1CYmpA^MI-{ zg^~{0W=gZPQkmn)7)|e<0@A8-3Uwv zvIbd=Ls70--izy()wm_D@tPGBt=kIgxMe3d_d3pyId(QFe4)fj#zW ztaO^S%eCv}tL2L)JGxkx;T!Gkrb=6ETlR35ugMtYV&)s%pO}W%J^;~1`jwiPz74TV zzYAGC3-$?E4T|}|W)$PCg4WS#P~@EWaTv<_`t{lf`>9yfN?^L*TPd;NtoHY>VsYDDEhf5$go+KfnBS z*Y3Q5t!}Bg^Yj^1`Ftjs;*b{AUzW%(q^lLiVM+8vdZx{Om#O+>%BvrC8G6rg7wd(c%U-E~OqSH;jav?* z8Cgi43g+jT=_y8XeZf3vo!MI6y7ZY7bFxynN~r>0v^N8f2ldZ7sAXL;AJy3GCI z9jp4X^S+JQTYtQCPFCyl*_FHsD}2twq){uiW!e&xrr;#v7zZbe-il8ZP^YLd;c( zEMv@7A8D(4^sF$uG<9f2WRBz`E4KcTWfYvpHuHTA&3ARXDVCO<+j}1-h2@J!tQScg z`~!@^Inu~tYQV<7t{kV+vD6XT`E!Y*v#VWtQkgUd3UkT{cTEiy{x6CUmb=GELY_f2NdANRtRhVNaP$h=KW z)0$~)MHK<|TIFX+7bXvdZ(TFHHlMWzIlTOfZ#i#H)EkMPV&^H5s26xGkyNn=Iyq|xhYz$?_T@1U zdv+ehXmi`!(JPM#&9Iqvf5SGJ^Sz)frMrtgrdP3MZNB*d@oOp%ug;ldCD-e4>0T7d9gS@-*a84o5s;4n?w5M>X`QeZP1#~bPv*>g@G}Jk@xeN|S{y;?`_P!5 zzuFUzF@d3d&b37HBv2X|EHi47q!?q#oM#44PuaEi-bxPp#%&i-ew5pAvZ9|1n!)Nj z2~wo@C3I2NYhw`*eyz?hDuYvuKmc3eOX5i|UUPwnM6!Ox?J)C|kq0I1XzAZjX=@*` zL{4{gq_d4CCT*{ex@Sm^e-1pd83+mT5Wm~@$P{hyKTj%#ib>GqWz&>%CyKSQ>;#+9 z+qHpNeu>9dI)YLuz*zX?Ox%J=q@#H&V!WE?^AJ)zl}QALy_N$1s=@2ItD~H)UZ8B}>-`HmB%ZwRT#UKGVfWcno6c+6W zwiL#rUCpKbcj07)Q7+S=RI|D2XLZ!Z5}LYP=^MOt^;G3<&O)~erl##3M%fEgpkkF> zp^H7E+5uj3XmO&%O{sGN-0)X;8_VSDWbx^4oYjps>7NO5lkRcG)>5mv5s5jhL%sj0Z{LDlgpfrjHicj zbSZMoo39y9MlJh_932M&KR00=Q1&RIo|cst02gWY%qEyjPTRU0$#!shpQ^l>dETnl zsU6HiCx~J^D0#En_b`nwkw9kvv9-2^XYH*q1Qrl8&=3>Fg?pdYmIG{!`-t)=2qim7rWO6D| z?p)7fzewLBHtc6s`{-cjfg1R@P(~l^F?+x37|XGy#hbh)qTW^ClZl+#whoTMWSSoS@V<&vC%!3lR8eXi+zkhw(5R*o6Ks=!_cnB%i4{P7bS^w_;<{T z>*{xgZ<5}eX2{PDCwo?uHopH*JEi%3ZW?)WPaI;J+?PkQIZb7g+O)|%dUrL|m@LgN zz2zQw?DwJx6&V>Yy=uiz z{&lC_Jd*^7)aHLC;(XrDJ+%2vTp?BE6BPLdGwp?+`QcA}WCLa#WpBc}uSPT#`7!w1 zbuT6IRBKSusLiGd)=Mzb7(ZmpJ(QO3H+zCQJ9`F47j|8yz#C53-E!5J=~Lh}g2`)f zH)_#f*(R?=!p|ZUi$Pl#JbMP^WKj=TiVzsR)uBt4>rPA3|j4Q+y63CKWgr7p8>K9#+RH`0fvDlkNE8xrW zbuR`SjNr1xgIHB!CWz&BRZQ3_#pI>9PE8i>#fKrl&Ahjg?Gtq(@?-8mQF%$SFTR<* z1cCh=bex--6I+lANp z{_rk|3#R8mvijOaWtZi%G=aiTQ>G4Qs>lA6^_|6*sGll8LTA6_N{dn0n-8HnnmE&Q z*DWO-!T91ETIh=~s!?2YWVhuX)D?@9q3LP)K3N+&nRw&mjsc$;$+Fpb=MkED zU&baB!(G=SKcnvLDvpIxKCuv$`31BKrD5~q!HzH^GRBhl-f)qC$W(5#7dK}q zj1Vz@;e5}Ow z7rw7MJ{}%cyeY|(=W*RZVnI+7UU(ue4&OrS;e`XW>(z3zK+zEVL=9JaoL z{qsAtVgV8Qfu6LZ1%IAm!~0WjRwRl7tG!)Dl}M9N^-A_wS^3EjT80;@?o-XIcr$n> z{XH!a9(tCM8h&{+Zfr+N8CRduCo+sWkrR{IjxeG~A|}I9-KEqqnIJjF5TWMTZ6U&1 zo6T2dk=K`0NzC;+6eL|l_Xv*kF-mZ+?b!9G6Tf5E0RFsh>^vlKKR!oA=WYVCtzY87 zYZiB&VGTQcj?3JK!lhy;W!EY3@VQjM6*kmgBdWY>`-d6ZjR=i;^@q6Uy>T5-5mc?j z$;giVff`8rDHMDP-?4RS+}{Pp!VQ!2N+q|AFMWKl8?r2u*rLQ&X1O zZzm4IfvpZ}3gmQCH5s_rUsmwP!E^@wBls}RQ2}jCJ1yMhg%O1-1hXTC^i9ou41SskaRd>u9>QXL>((^nR1! z%5z;_AJR#=Nm+Mlus#y zKTC0af|q>^Q;2v<_qFgW`zv68vYI!Xt25XZ-!{BvfIBRLa`WP-AsDbpdWq%1T$~V| znmjVDIxIM=kyDsIlV-z69c2Sru6-&i`cdg7`Ml!1K#j(XxdO}bTb2CI)o(Z^Oql%E-|Z5Yn6#uKx*%Z#R@RT9Sf^7)RgU2$WtaER>PUz4~DCPCWFOB z;lMHI9^-zE`iIXX1(+R}7wGFswUrGuw|YNl6AHc>j5fA5fg=har5j@;&>fP+#3Qg9 zX5xN#OkrkJB>}T#gd7Z}K0(W+H^A>RJN@ZQh{alenVQ%fFV>C)Q^QSzV~z&nhRuLd ze79}L8^%n4Ip7+9Z;LoV?-06zT?RxSwzc}f9g*buA+IPe%NFwjXN1ph44b-&YzKuU zM$4N5dWkmijkrl9N3i`crpJ$tQ{Kxc^?v(lp{xc;iABdsSEje}Exj|+bGxn?zmAA? zTo%M;;$qob`h92v#R|oWGYUEuHK2@BI*E*iI8ej#*HO}vzCX&MI^E^vWLCqKrI+3Z^Fj4w4xWwUCHyM z$wT@BniDWRIZ5x0INf?Jrl6cUR$ou-fB~3u25JmLiE}@GF>C5>NwN{A0*N^jFu?R8 zS>9w+H9Q4I4g&f1A7mFka@0bIS11?`gxKv~DWB1OHO0~p$y7cIo9dGpFK*qKQ@KO1 zxVO){&;QEMy(kHy)_+AQhu;7*lc70?=JY;)D5&y$Wobh+TRw$l@R4#1kGbjrq}m)f zvgmu#9DaA;B__Y!jG1DwAT-i>xTv)0Zl6~{sead~vQ1QU?Z61jUx27y6Bsn}sJgOq zR?B{IEuH3q` z<9`L81TWpp4_Q)6&l>ucyH-y?N>wb;KCrlLfZI5|t9 zDfZCY3baDqG&dFKyfPh~wGZ80ajYo#=3JIv>lBN@l1%wCJ(V+N7fpkaSfd+Pyec3v zBS}juk%5)Kf6H`4Jb1`G)mYRQy%qkaQ=joZ6``*b$x)IkcUmS8@Q2X&$wa-U(`>@UI#fLI4F*ZvK&} zdj`S%FAq)8)Gnet|~hT9>#^N<+g>3P7X=%V-EAD(8F6fZ(VwWrCi{MW3) zYqE`Pl^peqrlbO4xh^k~S#|lNyjP@Nf4GIhVdqHVIBo-7tl0kdDC^MGP2hcMffXGoO1cnC5 zMXLE=g0%Vi%LRYvvx!P`TeR`K95AZEK`C?*`I!CX{=olQ(p|cJcyg}y{u8*sBhi(t z6YuQXFUb2l=ewqB-?NA@^VVfO(Qm0G$z?X$tOC6?u(b8`b=;6xES>q2_^kW%bwL(4 zqQ+pq!_kqx2@Jy!!3t7z)NNE7Zz=*9M0;vtZ1e*H!@}8%rBXdVu~sF27!m>IEui3i z`!Rj2pds!OPeJpibqW><#sI)-uNH{EAKsxyD$A4LwjTKj_J#Rt=@--($2Q{}OU-W; zXJKCwrsTjX9bELQnB3m08V@+VgQd4vy`BcchhN<23@Owas<_aENQI6Nc&W9JEvXG| z0ENVp#s6ifiSvJkn%F_$e}Y{98f#K)J76!YVra8TMr$e42M~& zQE{{VWnSFW0tTY`J&L6yAn?yUaQ|&I3&g?kzl@XVe(p7EXTuP?xJL(k3;8jSpO3?+ zj8eAc`}OWgmzt7@A!&!^+JQOqC_!BbMEK$zVX*YW6fbE9vn&7GwP*(v=ozWm5>4Mk zY35qzFX#NN$q;lI+Jll}+}H)#Cd&k8=|=F%fIN0*giTlbbCS0^#va!-7`YTxI8FKp zgtbPxU6?Ya1JUS%W(Yz>_SQCKye9JDshrNfB1lpblPKwrNoQC*#GhYphoog4?wVILdZ)_+kUl zLf=Ju6w0!Y1+E++$K?Og5s?2rM#^eg?h9JEf{XKDC<3%F7&yPH-?QnzcifYW{c@*|s_Uugojb<%tjgxOf|;4$iN~it?r5sN%v5ib_auSix4dQF z@$Z3O_~!?|(VsQ>v;6zTdg{O9Z7wMH2mg^blNXf}H!HgkZ(;r6;yd~8%dUkj>QA2? zIEDSoZ5?wCrljQOY(++!86|JrkeKmLwVTV}NE7dP{! z!VR|DF3I2P+ z{$s|}{l^dSvHjgL;y^6$fT-`^T)R@!R2jB7zk?%x{*3-;ggId$sB ztXKZ*f7D!7Zdv)`#sLLGzdSQNjuvMhjxz_pMkH}e7YLZqJ=fWTM?+7i;otslo3dx~ zqb0N0{|758NSm9hTglFyvB%=qf5rfBMkaA)5eCrdm@fUk<~GIhe>bu*FfdAhM0kPX zKsFFBX#}xyQ%j2VDspo|Ls%J@6@G%Q`(|K3HUjgWaEK8g%}W~J01Zam8V)o9bZa;^ yBT!H6L^fifKG+E88J>8IKwWo=Y=lxI*a+w Date: Thu, 27 Jul 2023 17:14:23 +0200 Subject: [PATCH 104/479] fix: add default HandleIdentifierProvider for disabled versioning Setting versioning.enabled = false in versioning.cfg is not enough to disable versioning. It is also required to replace the bean class VersionedHandleIdentifierProvider with a HandleIdentifierProvider in identifier-service.xml. I've added one that is commented out as by default versioning is enabled. (cherry picked from commit 92c38de99e0d8dfd0e863812eb307cadd351c1e3) --- dspace/config/spring/api/identifier-service.xml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dspace/config/spring/api/identifier-service.xml b/dspace/config/spring/api/identifier-service.xml index 0c58cc1de932..79e19e879e8e 100644 --- a/dspace/config/spring/api/identifier-service.xml +++ b/dspace/config/spring/api/identifier-service.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - + + + 11 - 5.3.31 + 5.3.32 2.7.18 5.7.11 5.6.15.Final @@ -1200,6 +1200,12 @@ ${spring.version} + + org.springframework + spring-context-support + ${spring.version} + + spring-tx org.springframework From e28a083ef79998d88fd9fb2e2661a065c63d5bab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:00:25 +0000 Subject: [PATCH 109/479] Bump org.apache.james:apache-mime4j-core from 0.8.9 to 0.8.10 Bumps org.apache.james:apache-mime4j-core from 0.8.9 to 0.8.10. --- updated-dependencies: - dependency-name: org.apache.james:apache-mime4j-core dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit 7f91661f8415a6ffb9c37646f2a2d5e6bb981312) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f940dfb97a37..01830987b388 100644 --- a/pom.xml +++ b/pom.xml @@ -1302,7 +1302,7 @@ org.apache.james apache-mime4j-core - 0.8.9 + 0.8.10 From ae2ee43df83abbd396ba912e2eb1bd69be8227a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 00:08:54 +0000 Subject: [PATCH 110/479] Bump org.postgresql:postgresql from 42.6.0 to 42.7.2 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.6.0 to 42.7.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/commits) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit 0b2b81682a29df9df0feb363e9c3812e9a034ee5) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 376557bcdc1a..32d4a5428e52 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.7.11 5.6.15.Final 6.2.5.Final - 42.6.0 + 42.7.2 8.11.2 3.10.8 From 678aa9bad5cd00ffad0c9456a26a9be571f8c602 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 20:14:49 +0000 Subject: [PATCH 111/479] Bump org.eclipse.jetty.http2:http2-common Bumps org.eclipse.jetty.http2:http2-common from 9.4.53.v20231009 to 9.4.54.v20240208. --- updated-dependencies: - dependency-name: org.eclipse.jetty.http2:http2-common dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit a6e3d7a55a849fdb443e5f9df991d4f00d9aea9e) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 22185bbc946c..c7a838bf01ac 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.3.9 1.1.1 - 9.4.53.v20231009 + 9.4.54.v20240208 2.22.1 2.0.30 1.19.0 From b36613951b0274b39e71cede8a33b9c31762d1b8 Mon Sep 17 00:00:00 2001 From: Martin Walk Date: Thu, 29 Feb 2024 15:21:15 +0100 Subject: [PATCH 112/479] Fix #9383: Set email subject for request copy form --- dspace-api/src/main/java/org/dspace/core/Email.java | 2 +- .../dspace/app/rest/repository/RequestItemRepository.java | 6 +++++- 2 files changed, 6 insertions(+), 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 f6df740a53ef..c157d16d4967 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -395,7 +395,7 @@ public void send() throws MessagingException, IOException { for (String headerName : templateHeaders) { String headerValue = (String) vctx.get(headerName); if ("subject".equalsIgnoreCase(headerName)) { - if (null != headerValue) { + if ((subject == null || subject.isEmpty()) && null != headerValue) { subject = headerValue; } } else if ("charset".equalsIgnoreCase(headerName)) { 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 6eb631cfa56e..5945d516600a 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 @@ -247,11 +247,15 @@ public RequestItemRest put(Context context, HttpServletRequest request, message = responseMessageNode.asText(); } + JsonNode responseSubjectNode = requestBody.findValue("subject"); + String subject = null; + if (responseSubjectNode != null && !responseSubjectNode.isNull()) { + subject = responseSubjectNode.asText(); + } ri.setDecision_date(new Date()); requestItemService.update(context, ri); // Send the response email - String subject = requestBody.findValue("subject").asText(); try { requestItemEmailNotifier.sendResponse(context, ri, subject, message); } catch (IOException ex) { From 78d8e86369f7495611f4b88c48d3df8ff41c5bec Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 19 Mar 2024 15:18:28 +0100 Subject: [PATCH 113/479] upgrade to Spring Framework v5.3.33 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 30ee3c3fd783..5bb7b3f3afca 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 11 - 5.3.32 + 5.3.33 2.7.18 5.7.11 5.6.15.Final From 3817f3ff9d3322e5aca572d49f6204444ed3ecd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:07:21 +0000 Subject: [PATCH 114/479] Bump org.apache.solr:solr-solrj from 8.11.2 to 8.11.3 Bumps org.apache.solr:solr-solrj from 8.11.2 to 8.11.3. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit e93455796623461e4a5f1fb5023ac9fb0f78f0e0) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5bb7b3f3afca..da252ad102c3 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.6.15.Final 6.2.5.Final 42.7.2 - 8.11.2 + 8.11.3 3.10.8 2.10.0 From 48079d70a95b820c087a39e1f26c821fe7423f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Fri, 22 Mar 2024 12:19:43 +0000 Subject: [PATCH 115/479] adding Unicode filtering for sorts --- dspace/solr/search/conf/schema.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace/solr/search/conf/schema.xml b/dspace/solr/search/conf/schema.xml index df21afbc6426..66f2f176d2d6 100644 --- a/dspace/solr/search/conf/schema.xml +++ b/dspace/solr/search/conf/schema.xml @@ -162,6 +162,7 @@ + From acfe272fbca0d1ffba13a9a0ab391256ad4cf3d1 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Fri, 19 Jan 2024 21:32:04 +0000 Subject: [PATCH 116/479] Fix generating versioned identifiers if pre-registration is enabled (cherry picked from commit 1844fd28a0df0395192ff2b17c9018b2c46ed72e) --- .../content/WorkspaceItemServiceImpl.java | 15 ++++++++++--- .../dspace/content/packager/PackageUtils.java | 2 +- .../content/service/WorkspaceItemService.java | 21 ++++++++++++++++++- .../DefaultItemVersionProvider.java | 4 +++- .../org/dspace/app/packager/PackagerIT.java | 2 +- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java index b6e7372af184..b78f6b9f7de8 100644 --- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java @@ -96,11 +96,18 @@ public WorkspaceItem find(Context context, int id) throws SQLException { @Override public WorkspaceItem create(Context context, Collection collection, boolean template) throws AuthorizeException, SQLException { - return create(context, collection, null, template); + return create(context, collection, null, template, false); } @Override - public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template) + public WorkspaceItem create(Context context, Collection collection, boolean template, boolean isNewVersion) + throws AuthorizeException, SQLException { + return create(context, collection, null, template, isNewVersion); + } + + @Override + public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template, + boolean isNewVersion) throws AuthorizeException, SQLException { // Check the user has permission to ADD to the collection authorizeService.authorizeAction(context, collection, Constants.ADD); @@ -174,8 +181,10 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b // If configured, register identifiers (eg handle, DOI) now. This is typically used with the Show Identifiers // submission step which previews minted handles and DOIs during the submission process. Default: false + // Additional check needed: if we are creating a new version of an existing item we skip the identifier + // generation here, as this will be performed when the new version is created in VersioningServiceImpl if (DSpaceServicesFactory.getInstance().getConfigurationService() - .getBooleanProperty("identifiers.submission.register", false)) { + .getBooleanProperty("identifiers.submission.register", false) && !isNewVersion) { try { // Get map of filters to use for identifier types, while the item is in progress Map, Filter> filters = FilterUtils.getIdentifierFilters(true); diff --git a/dspace-api/src/main/java/org/dspace/content/packager/PackageUtils.java b/dspace-api/src/main/java/org/dspace/content/packager/PackageUtils.java index 9e7d870076aa..9a8ae4606487 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/PackageUtils.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/PackageUtils.java @@ -503,7 +503,7 @@ public static DSpaceObject createDSpaceObject(Context context, DSpaceObject pare wsi = workspaceItemService.create(context, (Collection)parent, params.useCollectionTemplate()); } else { wsi = workspaceItemService.create(context, (Collection)parent, - uuid, params.useCollectionTemplate()); + uuid, params.useCollectionTemplate(), false); } // Please note that we are returning an Item which is *NOT* yet in the Archive, diff --git a/dspace-api/src/main/java/org/dspace/content/service/WorkspaceItemService.java b/dspace-api/src/main/java/org/dspace/content/service/WorkspaceItemService.java index c8df68e43498..8559bcc61402 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/WorkspaceItemService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/WorkspaceItemService.java @@ -56,6 +56,23 @@ public interface WorkspaceItemService extends InProgressSubmissionServicetrue, the workspace item starts as a copy + * of the collection's template item + * @param isNewVersion whether we are creating a new workspace item version of an existing item + * @return the newly created workspace item + * @throws SQLException if database error + * @throws AuthorizeException if authorization error + */ + public WorkspaceItem create(Context context, Collection collection, boolean template, boolean isNewVersion) + throws AuthorizeException, SQLException; + /** * Create a new workspace item, with a new ID. An Item is also created. The * submitter is the current user in the context. @@ -65,11 +82,13 @@ public WorkspaceItem create(Context context, Collection collection, boolean temp * @param uuid the preferred uuid of the new item (used if restoring an item and retaining old uuid) * @param template if true, the workspace item starts as a copy * of the collection's template item + * @param isNewVersion whether we are creating a new workspace item version of an existing item * @return the newly created workspace item * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template) + public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template, + boolean isNewVersion) throws AuthorizeException, SQLException; public WorkspaceItem create(Context c, WorkflowItem wfi) throws SQLException, AuthorizeException; diff --git a/dspace-api/src/main/java/org/dspace/versioning/DefaultItemVersionProvider.java b/dspace-api/src/main/java/org/dspace/versioning/DefaultItemVersionProvider.java index d4590ae24ea2..fa89b3441408 100644 --- a/dspace-api/src/main/java/org/dspace/versioning/DefaultItemVersionProvider.java +++ b/dspace-api/src/main/java/org/dspace/versioning/DefaultItemVersionProvider.java @@ -52,7 +52,9 @@ public class DefaultItemVersionProvider extends AbstractVersionProvider implemen @Override public Item createNewItemAndAddItInWorkspace(Context context, Item nativeItem) { try { - WorkspaceItem workspaceItem = workspaceItemService.create(context, nativeItem.getOwningCollection(), false); + WorkspaceItem workspaceItem = workspaceItemService.create(context, nativeItem.getOwningCollection(), + false, + true); Item itemNew = workspaceItem.getItem(); itemService.update(context, itemNew); return itemNew; diff --git a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java index 7d808ab8715c..2cddbb511f91 100644 --- a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java @@ -159,7 +159,7 @@ public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception { performExportScript(article.getHandle(), tempFile); UUID id = article.getID(); itemService.delete(context, article); - WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false); + WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false, false); installItemService.installItem(context, workspaceItem, "123456789/0100"); performImportNoForceScript(tempFile); Iterator items = itemService.findByCollection(context, col1); From 9779c175db5fe3e22ce9c8ef3c7d3301d221f082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Mon, 8 Apr 2024 16:44:43 +0100 Subject: [PATCH 117/479] Replace organisation->organization --- dspace/config/spring/api/discovery.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace/config/spring/api/discovery.xml b/dspace/config/spring/api/discovery.xml index fb25f11598fa..c5486ad84e63 100644 --- a/dspace/config/spring/api/discovery.xml +++ b/dspace/config/spring/api/discovery.xml @@ -2889,16 +2889,16 @@ - + - + - + From 5aa32dfd5a2c8867facec798dbc24e56168b3478 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 8 Apr 2024 10:35:20 -0500 Subject: [PATCH 118/479] Update all GitHub Actions. Add newly required CODECOV_TOKEN to codecov action --- .github/workflows/build.yml | 11 ++++++----- .github/workflows/codescan.yml | 2 +- .github/workflows/issue_opened.yml | 2 +- .github/workflows/pull_request_opened.yml | 2 +- .github/workflows/reusable-docker-build.yml | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d6913078e471..8b3cb1d017a1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: # https://github.com/actions/setup-java - name: Install JDK ${{ matrix.java }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: ${{ matrix.java }} distribution: 'temurin' @@ -65,14 +65,14 @@ jobs: # (This artifact is downloadable at the bottom of any job's summary page) - name: Upload Results of ${{ matrix.type }} to Artifact if: ${{ failure() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.type }} results path: ${{ matrix.resultsdir }} # Upload code coverage report to artifact, so that it can be shared with the 'codecov' job (see below) - name: Upload code coverage report to Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.type }} coverage report path: 'dspace/target/site/jacoco-aggregate/jacoco.xml' @@ -91,7 +91,7 @@ jobs: # Download artifacts from previous 'tests' job - name: Download coverage artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 # Now attempt upload to Codecov using its action. # NOTE: We use a retry action to retry the Codecov upload if it fails the first time. @@ -101,10 +101,11 @@ jobs: - name: Upload coverage to Codecov.io uses: Wandalen/wretry.action@v1.3.0 with: - action: codecov/codecov-action@v3 + action: codecov/codecov-action@v4 # Ensure codecov-action throws an error when it fails to upload with: | fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} # Try re-running action 5 times max attempt_limit: 5 # Run again in 30 seconds diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml index 13bb0d2278ad..1e3d835e2713 100644 --- a/.github/workflows/codescan.yml +++ b/.github/workflows/codescan.yml @@ -39,7 +39,7 @@ jobs: # https://github.com/actions/setup-java - name: Install JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 11 distribution: 'temurin' diff --git a/.github/workflows/issue_opened.yml b/.github/workflows/issue_opened.yml index b4436dca3aad..0a35a6a95044 100644 --- a/.github/workflows/issue_opened.yml +++ b/.github/workflows/issue_opened.yml @@ -16,7 +16,7 @@ jobs: # Only add to project board if issue is flagged as "needs triage" or has no labels # NOTE: By default we flag new issues as "needs triage" in our issue template if: (contains(github.event.issue.labels.*.name, 'needs triage') || join(github.event.issue.labels.*.name) == '') - uses: actions/add-to-project@v0.5.0 + uses: actions/add-to-project@v1.0.0 # Note, the authentication token below is an ORG level Secret. # It must be created/recreated manually via a personal access token with admin:org, project, public_repo permissions # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token diff --git a/.github/workflows/pull_request_opened.yml b/.github/workflows/pull_request_opened.yml index f16e81c9fd25..bbac52af2438 100644 --- a/.github/workflows/pull_request_opened.yml +++ b/.github/workflows/pull_request_opened.yml @@ -21,4 +21,4 @@ jobs: # 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@v2.0.1 + uses: toshimaru/auto-author-assign@v2.1.0 diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index aa8327f4d11b..2f80ef4ba925 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -152,7 +152,7 @@ jobs: # Upload digest to an artifact, so that it can be used in manifest below - name: Upload Docker build digest to artifact if: ${{ ! matrix.isPr }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: digests-${{ inputs.build_id }} path: /tmp/digests/* @@ -192,7 +192,7 @@ jobs: - docker-build steps: - name: Download Docker build digests - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: digests-${{ inputs.build_id }} path: /tmp/digests From a1d7c47f1ede3222cea38505de654113b962eefc Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Apr 2024 09:53:21 -0500 Subject: [PATCH 119/479] Fix Docker build by ensuring all artifacts are named with architecture (amd64 vs arm64) (cherry picked from commit f4edf9286075a8233b6bff4937e820dd279f58b8) --- .github/workflows/reusable-docker-build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 2f80ef4ba925..41a2b4b4fef7 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -154,7 +154,7 @@ jobs: if: ${{ ! matrix.isPr }} uses: actions/upload-artifact@v4 with: - name: digests-${{ inputs.build_id }} + name: digests-${{ inputs.build_id }}-${{ matrix.arch }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 @@ -172,7 +172,7 @@ jobs: # If this build is NOT a PR and passed in a REDEPLOY_DEMO_URL secret, # Then redeploy https://demo.dspace.org if this build is for our deployment architecture and demo branch. - - name: Redeploy demo.dspace.org (based on maintenace branch) + - name: Redeploy demo.dspace.org (based on maintenance branch) if: | !matrix.isPR && env.REDEPLOY_DEMO_URL != '' && @@ -194,8 +194,10 @@ jobs: - name: Download Docker build digests uses: actions/download-artifact@v4 with: - name: digests-${{ inputs.build_id }} path: /tmp/digests + # Download digests for both AMD64 and ARM64 into same directory + pattern: digests-${{ inputs.build_id }}-* + merge-multiple: true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 From 49e54eb34fb57380d2d47798e5c0e8270224ca98 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Apr 2024 12:35:05 -0500 Subject: [PATCH 120/479] Ensure build digest name does NOT have slashes by changing arch to use dashes --- .github/workflows/reusable-docker-build.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 41a2b4b4fef7..687474caed18 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -93,6 +93,14 @@ jobs: runs-on: ${{ matrix.os }} steps: + # This step converts the slashes in the "arch" matrix values above into dashes & saves to env.ARCH_NAME + # E.g. "linux/amd64" becomes "linux-amd64" + # This is necessary because all upload artifacts CANNOT have special chars (like slashes) + - name: Prepare + run: | + platform=${{ matrix.arch }} + echo "ARCH_NAME=${platform//\//-}" >> $GITHUB_ENV + # https://github.com/actions/checkout - name: Checkout codebase uses: actions/checkout@v4 @@ -154,7 +162,7 @@ jobs: if: ${{ ! matrix.isPr }} uses: actions/upload-artifact@v4 with: - name: digests-${{ inputs.build_id }}-${{ matrix.arch }} + name: digests-${{ inputs.build_id }}-${{ env.ARCH_NAME }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 From e22bf93a972dffe8279bf2793c43a311229a93ed Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Tue, 9 Apr 2024 17:57:00 +0200 Subject: [PATCH 121/479] Refactor SubmissionConfigConsumer to avoid reload the submission config multiple times during the creation of a collection (cherry picked from commit 00d0a01e1f2c05db67a797d809be3c04dcd215e2) --- .../consumer/SubmissionConfigConsumer.java | 49 ++++++------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/submit/consumer/SubmissionConfigConsumer.java b/dspace-api/src/main/java/org/dspace/submit/consumer/SubmissionConfigConsumer.java index a593fe8ae066..0cf4ae92c2ca 100644 --- a/dspace-api/src/main/java/org/dspace/submit/consumer/SubmissionConfigConsumer.java +++ b/dspace-api/src/main/java/org/dspace/submit/consumer/SubmissionConfigConsumer.java @@ -8,15 +8,10 @@ package org.dspace.submit.consumer; import org.apache.logging.log4j.Logger; -import org.dspace.content.Collection; -import org.dspace.content.DSpaceObject; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.discovery.IndexingService; -import org.dspace.discovery.indexobject.IndexableCollection; import org.dspace.event.Consumer; import org.dspace.event.Event; -import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.submit.factory.SubmissionServiceFactory; /** @@ -28,11 +23,9 @@ public class SubmissionConfigConsumer implements Consumer { /** * log4j logger */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(SubmissionConfigConsumer.class); + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SubmissionConfigConsumer.class); - IndexingService indexer = DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(IndexingService.class.getName(), - IndexingService.class); + private boolean reloadNeeded = false; @Override public void initialize() throws Exception { @@ -42,37 +35,27 @@ public void initialize() throws Exception { @Override public void consume(Context ctx, Event event) throws Exception { int st = event.getSubjectType(); - int et = event.getEventType(); + if (st == Constants.COLLECTION) { + // NOTE: IndexEventConsumer ("discovery") should be declared before this consumer + // We don't reindex the collection because it will normally be reindexed by IndexEventConsumer + // before the submission configurations are reloaded - if ( st == Constants.COLLECTION ) { - switch (et) { - case Event.MODIFY_METADATA: - // Submission configuration it's based on solr - // for collection's entity type but, at this point - // that info isn't indexed yet, we need to force it - DSpaceObject subject = event.getSubject(ctx); - Collection collectionFromDSOSubject = (Collection) subject; - indexer.indexContent(ctx, new IndexableCollection (collectionFromDSOSubject), true, false, false); - indexer.commit(); - - log.debug("SubmissionConfigConsumer occured: " + event.toString()); - // reload submission configurations - SubmissionServiceFactory.getInstance().getSubmissionConfigService().reload(); - break; - - default: - log.debug("SubmissionConfigConsumer occured: " + event.toString()); - // reload submission configurations - SubmissionServiceFactory.getInstance().getSubmissionConfigService().reload(); - break; - } + log.debug("SubmissionConfigConsumer occurred: " + event); + // submission configurations should be reloaded + reloadNeeded = true; } } @Override public void end(Context ctx) throws Exception { - // No-op + if (reloadNeeded) { + // reload submission configurations + SubmissionServiceFactory.getInstance().getSubmissionConfigService().reload(); + + // Reset the boolean used + reloadNeeded = false; + } } @Override From 7c505e113ed607cc692ca790943dfb6f1c920aa9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Apr 2024 14:47:21 -0500 Subject: [PATCH 122/479] Docker build IDs must all be unique to avoid image conflicts. Ensure no builds use a generic name like "dspace". --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9f1e407cff4b..5abfb89a5dc7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -43,7 +43,7 @@ jobs: needs: dspace-dependencies uses: ./.github/workflows/reusable-docker-build.yml with: - build_id: dspace + build_id: dspace-prod image_name: dspace/dspace dockerfile_path: ./Dockerfile secrets: From 2afc9b158eb3b076b6ed4e07d5cfbe939e17fa07 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 10 Apr 2024 09:26:16 -0500 Subject: [PATCH 123/479] Docker build IDs must all be unique to avoid image conflicts. Avoid conflict with "-loadsql" build by appending "-prod" on main build id. --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5abfb89a5dc7..a9ff8760e763 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -120,7 +120,7 @@ jobs: if: github.repository == 'dspace/dspace' uses: ./.github/workflows/reusable-docker-build.yml with: - build_id: dspace-postgres-pgcrypto + build_id: dspace-postgres-pgcrypto-prod image_name: dspace/dspace-postgres-pgcrypto # Must build out of subdirectory to have access to install script for pgcrypto. # NOTE: this context will build the image based on the Dockerfile in the specified directory From 2f7ee4bc769b9fca70fbcb8f59a990cdbe78e7e6 Mon Sep 17 00:00:00 2001 From: Thomas Misilo Date: Fri, 16 Feb 2024 08:45:50 -0600 Subject: [PATCH 124/479] httpd-shibd-foreground.sh needs to be executable In order for the shib container to start, the file that is running needs to be executable (cherry picked from commit 3f9274f23f32e6983f3da81d001d9ce319e7245f) --- .../src/main/docker/dspace-shibboleth/httpd-shibd-foreground.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 dspace/src/main/docker/dspace-shibboleth/httpd-shibd-foreground.sh diff --git a/dspace/src/main/docker/dspace-shibboleth/httpd-shibd-foreground.sh b/dspace/src/main/docker/dspace-shibboleth/httpd-shibd-foreground.sh old mode 100644 new mode 100755 From 8d45c1f94d89d598254c3d97a1e87a4d0713046c Mon Sep 17 00:00:00 2001 From: Tom Misilo <1446856+misilot@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:19:56 -0500 Subject: [PATCH 125/479] Add Space in ePerson's Name for rejection metadata and email (cherry picked from commit ec3089dc4d201201bf607b419b518d06dc1fb4f2) --- .../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 bc91a1fd9298..faf15d02c6d8 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java @@ -1177,7 +1177,7 @@ protected WorkspaceItem returnToWorkspace(Context c, XmlWorkflowItem wfi) public String getEPersonName(EPerson ePerson) { String submitter = ePerson.getFullName(); - submitter = submitter + "(" + ePerson.getEmail() + ")"; + submitter = submitter + " (" + ePerson.getEmail() + ")"; return submitter; } From 9b47b2215cd88c63889266a5950c58461dccd3b6 Mon Sep 17 00:00:00 2001 From: "David P. Steelman" Date: Wed, 17 Apr 2024 13:55:34 -0400 Subject: [PATCH 126/479] Fix OpenSearch NullPointerException for unknown valid UUIDs in scope Fixes a NullPointerException when the "scope" parameter provided to the OpenSearch endpoint is a valid UUID, but is not a UUID associated with a Community or Collection. Instead of throwing a NullPointerException, this change modifies the code to return a null scope (resulting in an "unscoped" OpenSearch request), which is the same behavior that occurs when the UUID is invalid, or otherwise not usable. --- .../dspace/app/rest/utils/ScopeResolver.java | 22 +++++++++++++++ .../opensearch/OpenSearchControllerIT.java | 28 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ScopeResolver.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ScopeResolver.java index 658a3e996aef..7a425f5e81a8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ScopeResolver.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ScopeResolver.java @@ -35,6 +35,18 @@ public class ScopeResolver { @Autowired CommunityService communityService; + /** + * Returns an IndexableObject corresponding to the community or collection + * of the given scope, or null if the scope is not a valid UUID, or is a + * valid UUID that does not correspond to a community of collection. + * + * @param context the DSpace context + * @param scope a String containing the UUID of the community or collection + * to return. + * @return an IndexableObject corresponding to the community or collection + * of the given scope, or null if the scope is not a valid UUID, or is a + * valid UUID that does not correspond to a community of collection. + */ public IndexableObject resolveScope(Context context, String scope) { IndexableObject scopeObj = null; if (StringUtils.isNotBlank(scope)) { @@ -43,6 +55,16 @@ public IndexableObject resolveScope(Context context, String scope) { scopeObj = new IndexableCommunity(communityService.find(context, uuid)); if (scopeObj.getIndexedObject() == null) { scopeObj = new IndexableCollection(collectionService.find(context, uuid)); + if (scopeObj.getIndexedObject() == null) { + // Can't find the UUID as a community or collection + // so log and return null + log.warn( + "The given scope string " + + StringUtils.trimToEmpty(scope) + + " is not a collection or community UUID." + ); + scopeObj = null; + } } } catch (IllegalArgumentException ex) { log.warn("The given scope string " + StringUtils.trimToEmpty(scope) + " is not a UUID", ex); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java index 1ddea619d2fc..b533e14568e6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java @@ -269,4 +269,32 @@ public void emptyDescriptionTest() throws Exception { .andExpect(status().isOk()) .andExpect(xpath("rss/channel/description").string("No Description")); } + + @Test + public void scopeNotCommunityOrCollectionUUIDTest() throws Exception { + // Tests that a OpenSearch response with 1 result (equivalent to an + // unscoped request) is returned if the "scope" UUID is a + // validly-formatted UUID, but not a community or collection UUID. + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection collection1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1") + .build(); + + Item publicItem1 = ItemBuilder.createItem(context, collection1) + .withTitle("Boars at Yellowstone") + .withIssueDate("2017-10-17") + .withAuthor("Ballini, Andreas").withAuthor("Moriarti, Susan") + .build(); + + // UUID is valid, but not a community or collection UUID + String testUUID = "b68f0d1c-7316-41dc-835d-46b79b35642e"; + + getClient().perform(get("/opensearch/search") + .param("scope", testUUID) + .param("query", "*")) + .andExpect(status().isOk()) + .andExpect(xpath("feed/totalResults").string("1")); + } } From e82b94fa435663419a2fd78a9aad9d9e488f7ecc Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 15 Apr 2024 12:56:58 +0200 Subject: [PATCH 127/479] Add support for LazyDownload of files from S3 --- .../storage/bitstore/S3BitStoreService.java | 87 ++++++++++++++++--- .../storage/bitstore/S3BitStoreServiceIT.java | 21 ++++- 2 files changed, 90 insertions(+), 18 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java index 48f3b9f325f8..54333abbf49b 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java @@ -10,6 +10,7 @@ import static java.lang.String.valueOf; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -24,6 +25,7 @@ import javax.validation.constraints.NotNull; import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; @@ -35,6 +37,7 @@ import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.transfer.Download; +import com.amazonaws.services.s3.transfer.Transfer.TransferState; import com.amazonaws.services.s3.transfer.TransferManager; import com.amazonaws.services.s3.transfer.TransferManagerBuilder; import com.amazonaws.services.s3.transfer.Upload; @@ -102,6 +105,11 @@ public class S3BitStoreService extends BaseBitStoreService { private String awsRegionName; private boolean useRelativePath; + /** + * The maximum size of individual chunk to download from S3 when a file is accessed. Default 5Mb + */ + private long bufferSize = 5 * 1024 * 1024; + /** * container for all the assets */ @@ -258,20 +266,7 @@ public InputStream get(Bitstream bitstream) throws IOException { if (isRegisteredBitstream(key)) { key = key.substring(REGISTERED_FLAG.length()); } - try { - File tempFile = File.createTempFile("s3-disk-copy-" + UUID.randomUUID(), "temp"); - tempFile.deleteOnExit(); - - GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key); - - Download download = tm.download(getObjectRequest, tempFile); - download.waitForCompletion(); - - return new DeleteOnCloseFileInputStream(tempFile); - } catch (AmazonClientException | InterruptedException e) { - log.error("get(" + key + ")", e); - throw new IOException(e); - } + return new S3LazyInputStream(key, bufferSize, bitstream.getSizeBytes()); } /** @@ -622,4 +617,68 @@ public boolean isRegisteredBitstream(String internalId) { return internalId.startsWith(REGISTERED_FLAG); } + public void setBufferSize(long bufferSize) { + this.bufferSize = bufferSize; + } + + public class S3LazyInputStream extends InputStream { + private InputStream currentChunkStream; + private String objectKey; + private long endOfChunk = -1; + private long chunkMaxSize; + private long currPos = 0; + private long fileSize; + + public S3LazyInputStream(String objectKey, long chunkMaxSize, long fileSize) throws IOException { + this.objectKey = objectKey; + this.chunkMaxSize = chunkMaxSize; + this.endOfChunk = 0; + this.fileSize = fileSize; + downloadChunk(); + } + + @Override + public int read() throws IOException { + if (currPos == endOfChunk && currPos < fileSize) { + currentChunkStream.close(); + downloadChunk(); + } + + int byteRead = currPos < endOfChunk ? currentChunkStream.read() : -1; + if (byteRead != -1) { + currPos++; + } else { + currentChunkStream.close(); + } + return byteRead; + } + + private void downloadChunk() throws IOException, FileNotFoundException { + // Create a DownloadFileRequest with the desired byte range + long startByte = currPos; // Start byte (inclusive) + long endByte = Long.min(startByte + chunkMaxSize - 1, fileSize - 1); // End byte (inclusive) + GetObjectRequest getRequest = new GetObjectRequest(bucketName, objectKey) + .withRange(startByte, endByte); + + File currentChunkFile = File.createTempFile("s3-disk-copy-" + UUID.randomUUID(), "temp"); + currentChunkFile.deleteOnExit(); + try { + Download download = tm.download(getRequest, currentChunkFile); + download.waitForCompletion(); + currentChunkStream = new DeleteOnCloseFileInputStream(currentChunkFile); + endOfChunk = endOfChunk + download.getProgress().getBytesTransferred(); + } catch (AmazonClientException | InterruptedException e) { + currentChunkFile.delete(); + throw new IOException(e); + } + } + + @Override + public void close() throws IOException { + if (currentChunkStream != null) { + currentChunkStream.close(); + } + } + + } } diff --git a/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java index 30eeff2ddc82..dffde0b3a6a8 100644 --- a/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java @@ -99,6 +99,7 @@ public void setup() throws Exception { s3BitStoreService = new S3BitStoreService(amazonS3Client); s3BitStoreService.setEnabled(BooleanUtils.toBoolean( configurationService.getProperty("assetstore.s3.enabled"))); + s3BitStoreService.setBufferSize(22); context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) @@ -129,12 +130,25 @@ public void testBitstreamPutAndGetWithAlreadyPresentBucket() throws IOException assertThat(amazonS3Client.listBuckets(), contains(bucketNamed(bucketName))); context.turnOffAuthorisationSystem(); - String content = "Test bitstream content"; + String content = "Test bitstream content"; + String contentOverOneSpan = "This content span two chunks"; + String contentExactlyTwoSpans = "Test bitstream contentTest bitstream content"; + String contentOverOneTwoSpans = "Test bitstream contentThis content span three chunks"; Bitstream bitstream = createBitstream(content); + Bitstream bitstreamOverOneSpan = createBitstream(contentOverOneSpan); + Bitstream bitstreamExactlyTwoSpans = createBitstream(contentExactlyTwoSpans); + Bitstream bitstreamOverOneTwoSpans = createBitstream(contentOverOneTwoSpans); context.restoreAuthSystemState(); - s3BitStoreService.put(bitstream, toInputStream(content)); + checkGetPut(bucketName, content, bitstream); + checkGetPut(bucketName, contentOverOneSpan, bitstreamOverOneSpan); + checkGetPut(bucketName, contentExactlyTwoSpans, bitstreamExactlyTwoSpans); + checkGetPut(bucketName, contentOverOneTwoSpans, bitstreamOverOneTwoSpans); + + } + private void checkGetPut(String bucketName, String content, Bitstream bitstream) throws IOException { + s3BitStoreService.put(bitstream, toInputStream(content)); String expectedChecksum = Utils.toHex(generateChecksum(content)); assertThat(bitstream.getSizeBytes(), is((long) content.length())); @@ -147,8 +161,7 @@ public void testBitstreamPutAndGetWithAlreadyPresentBucket() throws IOException String key = s3BitStoreService.getFullKey(bitstream.getInternalId()); ObjectMetadata objectMetadata = amazonS3Client.getObjectMetadata(bucketName, key); assertThat(objectMetadata.getContentMD5(), is(expectedChecksum)); - - } + } @Test public void testBitstreamPutAndGetWithoutSpecifingBucket() throws IOException { From 23ee2fd6cdd63b1fafe1916736a9359df2187348 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 19 Apr 2024 07:52:32 +0200 Subject: [PATCH 128/479] DURACOM-249 fix checkstyle issues, add javadoc --- .../storage/bitstore/S3BitStoreService.java | 58 ++++++++++++------- .../storage/bitstore/S3BitStoreServiceIT.java | 6 +- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java index 54333abbf49b..e21a101a7e98 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java @@ -25,7 +25,6 @@ import javax.validation.constraints.NotNull; import com.amazonaws.AmazonClientException; -import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; @@ -37,7 +36,6 @@ import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.transfer.Download; -import com.amazonaws.services.s3.transfer.Transfer.TransferState; import com.amazonaws.services.s3.transfer.TransferManager; import com.amazonaws.services.s3.transfer.TransferManagerBuilder; import com.amazonaws.services.s3.transfer.Upload; @@ -621,6 +619,14 @@ public void setBufferSize(long bufferSize) { this.bufferSize = bufferSize; } + /** + * This inner class represent an InputStream that uses temporary files to + * represent chunk of the object downloaded from S3. When the input stream is + * read the class look first to the current chunk and download a new one once if + * the current one as been fully read. The class is responsible to close a chunk + * as soon as a new one is retrieved, the last chunk is closed when the input + * stream itself is closed or the last byte is read (the first of the two) + */ public class S3LazyInputStream extends InputStream { private InputStream currentChunkStream; private String objectKey; @@ -639,12 +645,14 @@ public S3LazyInputStream(String objectKey, long chunkMaxSize, long fileSize) thr @Override public int read() throws IOException { + // is the current chunk completely read and other are available? if (currPos == endOfChunk && currPos < fileSize) { currentChunkStream.close(); downloadChunk(); } - int byteRead = currPos < endOfChunk ? currentChunkStream.read() : -1; + int byteRead = currPos < endOfChunk ? currentChunkStream.read() : -1; + // do we get any data or are we at the end of the file? if (byteRead != -1) { currPos++; } else { @@ -653,25 +661,31 @@ public int read() throws IOException { return byteRead; } - private void downloadChunk() throws IOException, FileNotFoundException { - // Create a DownloadFileRequest with the desired byte range - long startByte = currPos; // Start byte (inclusive) - long endByte = Long.min(startByte + chunkMaxSize - 1, fileSize - 1); // End byte (inclusive) - GetObjectRequest getRequest = new GetObjectRequest(bucketName, objectKey) - .withRange(startByte, endByte); - - File currentChunkFile = File.createTempFile("s3-disk-copy-" + UUID.randomUUID(), "temp"); - currentChunkFile.deleteOnExit(); - try { - Download download = tm.download(getRequest, currentChunkFile); - download.waitForCompletion(); - currentChunkStream = new DeleteOnCloseFileInputStream(currentChunkFile); - endOfChunk = endOfChunk + download.getProgress().getBytesTransferred(); - } catch (AmazonClientException | InterruptedException e) { - currentChunkFile.delete(); - throw new IOException(e); - } - } + /** + * This method download the next chunk from S3 + * + * @throws IOException + * @throws FileNotFoundException + */ + private void downloadChunk() throws IOException, FileNotFoundException { + // Create a DownloadFileRequest with the desired byte range + long startByte = currPos; // Start byte (inclusive) + long endByte = Long.min(startByte + chunkMaxSize - 1, fileSize - 1); // End byte (inclusive) + GetObjectRequest getRequest = new GetObjectRequest(bucketName, objectKey) + .withRange(startByte, endByte); + + File currentChunkFile = File.createTempFile("s3-disk-copy-" + UUID.randomUUID(), "temp"); + currentChunkFile.deleteOnExit(); + try { + Download download = tm.download(getRequest, currentChunkFile); + download.waitForCompletion(); + currentChunkStream = new DeleteOnCloseFileInputStream(currentChunkFile); + endOfChunk = endOfChunk + download.getProgress().getBytesTransferred(); + } catch (AmazonClientException | InterruptedException e) { + currentChunkFile.delete(); + throw new IOException(e); + } + } @Override public void close() throws IOException { diff --git a/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java index dffde0b3a6a8..6ea21eac8d6d 100644 --- a/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java @@ -147,8 +147,8 @@ public void testBitstreamPutAndGetWithAlreadyPresentBucket() throws IOException } - private void checkGetPut(String bucketName, String content, Bitstream bitstream) throws IOException { - s3BitStoreService.put(bitstream, toInputStream(content)); + private void checkGetPut(String bucketName, String content, Bitstream bitstream) throws IOException { + s3BitStoreService.put(bitstream, toInputStream(content)); String expectedChecksum = Utils.toHex(generateChecksum(content)); assertThat(bitstream.getSizeBytes(), is((long) content.length())); @@ -161,7 +161,7 @@ private void checkGetPut(String bucketName, String content, Bitstream bitstream) String key = s3BitStoreService.getFullKey(bitstream.getInternalId()); ObjectMetadata objectMetadata = amazonS3Client.getObjectMetadata(bucketName, key); assertThat(objectMetadata.getContentMD5(), is(expectedChecksum)); - } + } @Test public void testBitstreamPutAndGetWithoutSpecifingBucket() throws IOException { From 15d9d1c0423c9dc90ef67717043cb5e7dda6ccc8 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 19 Apr 2024 21:03:45 +0200 Subject: [PATCH 129/479] Port from #9259 the changes to use a collection object to return the item process submission configured --- .../org/dspace/app/util/DCInputsReader.java | 10 ++++---- .../app/util/SubmissionConfigReader.java | 24 ++++++++++++------- .../main/java/org/dspace/app/util/Util.java | 14 +++-------- .../authority/ChoiceAuthorityServiceImpl.java | 4 ++-- .../ctask/general/RequiredMetadata.java | 9 +++---- .../service/SubmissionConfigService.java | 2 +- .../service/SubmissionConfigServiceImpl.java | 4 ++-- .../dspace/app/util/SubmissionConfigTest.java | 10 +++++++- .../converter/AInprogressItemConverter.java | 2 +- .../SubmissionDefinitionRestRepository.java | 2 +- .../WorkspaceItemRestRepository.java | 2 +- 11 files changed, 45 insertions(+), 38 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 38692c73a6ce..8dc8239ca507 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -14,7 +14,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import javax.servlet.ServletException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; @@ -150,17 +149,16 @@ public List getPairs(String name) { * Returns the set of DC inputs used for a particular collection, or the * default set if no inputs defined for the collection * - * @param collectionHandle collection's unique Handle + * @param collection collection for which search the set of DC inputs * @return DC input set * @throws DCInputsReaderException if no default set defined - * @throws ServletException */ - public List getInputsByCollectionHandle(String collectionHandle) + public List getInputsByCollection(Collection collection) throws DCInputsReaderException { SubmissionConfig config; try { config = SubmissionServiceFactory.getInstance().getSubmissionConfigService() - .getSubmissionConfigByCollection(collectionHandle); + .getSubmissionConfigByCollection(collection); String formName = config.getSubmissionName(); if (formName == null) { throw new DCInputsReaderException("No form designated as default"); @@ -691,7 +689,7 @@ private String getValue(Node nd) { public String getInputFormNameByCollectionAndField(Collection collection, String field) throws DCInputsReaderException { - List inputSets = getInputsByCollectionHandle(collection.getHandle()); + List inputSets = getInputsByCollection(collection); for (DCInputSet inputSet : inputSets) { String[] tokenized = Utils.tokenize(field); String schema = tokenized[0]; diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 0f144fd69f46..82bb1c9ca4a8 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -210,18 +210,26 @@ public int countSubmissionConfigs() { * Returns the Item Submission process config used for a particular * collection, or the default if none is defined for the collection * - * @param collectionHandle collection's unique Handle + * @param col collection for which search Submission process config * @return the SubmissionConfig representing the item submission config - * @throws SubmissionConfigReaderException if no default submission process configuration defined + * @throws IllegalStateException if no default submission process configuration defined */ - public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) { - // get the name of the submission process config for this collection - String submitName = collectionToSubmissionConfig - .get(collectionHandle); - if (submitName == null) { + public SubmissionConfig getSubmissionConfigByCollection(Collection col) { + + String submitName; + + if (col != null) { + + // get the name of the submission process config for this collection submitName = collectionToSubmissionConfig - .get(DEFAULT_COLLECTION); + .get(col.getHandle()); + if (submitName != null) { + return getSubmissionConfigByName(submitName); + } } + + submitName = collectionToSubmissionConfig.get(DEFAULT_COLLECTION); + if (submitName == null) { throw new IllegalStateException( "No item submission process configuration designated as 'default' in 'submission-map' section of " + diff --git a/dspace-api/src/main/java/org/dspace/app/util/Util.java b/dspace-api/src/main/java/org/dspace/app/util/Util.java index f8ef3b1731f7..3bc828d6c496 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/Util.java +++ b/dspace-api/src/main/java/org/dspace/app/util/Util.java @@ -405,21 +405,13 @@ public static List getControlledVocabulariesDisplayValueLocalized( DCInput myInputs = null; boolean myInputsFound = false; String formFileName = I18nUtil.getInputFormsFileName(locale); - String col_handle = ""; Collection collection = item.getOwningCollection(); - if (collection == null) { - // set an empty handle so to get the default input set - col_handle = ""; - } else { - col_handle = collection.getHandle(); - } - // Read the input form file for the specific collection DCInputsReader inputsReader = new DCInputsReader(formFileName); - List inputSets = inputsReader.getInputsByCollectionHandle(col_handle); + List inputSets = inputsReader.getInputsByCollection(collection); // Replace the values of Metadatum[] with the correct ones in case // of @@ -500,8 +492,8 @@ public static List[] splitList(List idsList, int i) { public static List differenceInSubmissionFields(Collection fromCollection, Collection toCollection) throws DCInputsReaderException { DCInputsReader reader = new DCInputsReader(); - List from = reader.getInputsByCollectionHandle(fromCollection.getHandle()); - List to = reader.getInputsByCollectionHandle(toCollection.getHandle()); + List from = reader.getInputsByCollection(fromCollection); + List to = reader.getInputsByCollection(toCollection); Set fromFieldName = new HashSet<>(); Set toFieldName = new HashSet<>(); 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 34ba9e8c4550..f4d1f02710e1 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 @@ -242,7 +242,7 @@ public String getChoiceAuthorityName(String schema, String element, String quali // check if it is the requested collection Map controllerFormDef = controllerFormDefinitions.get(fieldKey); SubmissionConfig submissionConfig = submissionConfigService - .getSubmissionConfigByCollection(collection.getHandle()); + .getSubmissionConfigByCollection(collection); String submissionName = submissionConfig.getSubmissionName(); // check if the requested collection has a submission definition that use an authority for the metadata if (controllerFormDef.containsKey(submissionName)) { @@ -495,7 +495,7 @@ private ChoiceAuthority getAuthorityByFieldKeyCollection(String fieldKey, Collec try { configReaderService = SubmissionServiceFactory.getInstance().getSubmissionConfigService(); SubmissionConfig submissionName = configReaderService - .getSubmissionConfigByCollection(collection.getHandle()); + .getSubmissionConfigByCollection(collection); ma = controllerFormDefinitions.get(fieldKey).get(submissionName.getSubmissionName()); } catch (SubmissionConfigReaderException e) { // the system is in an illegal state as the submission definition is not valid diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java b/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java index 07bfed5fe572..2899e3f6bdd6 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java @@ -17,6 +17,7 @@ import org.dspace.app.util.DCInputSet; import org.dspace.app.util.DCInputsReader; import org.dspace.app.util.DCInputsReaderException; +import org.dspace.content.Collection; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.MetadataValue; @@ -69,7 +70,7 @@ public int perform(DSpaceObject dso) throws IOException { handle = "in workflow"; } sb.append("Item: ").append(handle); - for (String req : getReqList(item.getOwningCollection().getHandle())) { + for (String req : getReqList(item.getOwningCollection())) { List vals = itemService.getMetadataByMetadataString(item, req); if (vals.size() == 0) { sb.append(" missing required field: ").append(req); @@ -91,14 +92,14 @@ public int perform(DSpaceObject dso) throws IOException { } } - protected List getReqList(String handle) throws DCInputsReaderException { - List reqList = reqMap.get(handle); + protected List getReqList(Collection collection) throws DCInputsReaderException { + List reqList = reqMap.get(collection.getHandle()); if (reqList == null) { reqList = reqMap.get("default"); } if (reqList == null) { reqList = new ArrayList(); - List inputSet = reader.getInputsByCollectionHandle(handle); + List inputSet = reader.getInputsByCollection(collection); for (DCInputSet inputs : inputSet) { for (DCInput[] row : inputs.getFields()) { for (DCInput input : row) { diff --git a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java index c4b111a38f7e..36ba82f60d53 100644 --- a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java +++ b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java @@ -34,7 +34,7 @@ public interface SubmissionConfigService { public int countSubmissionConfigs(); - public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle); + public SubmissionConfig getSubmissionConfigByCollection(Collection collection); public SubmissionConfig getSubmissionConfigByName(String submitName); diff --git a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java index a72bcc2c3bf9..fe063954a1d3 100644 --- a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java @@ -57,8 +57,8 @@ public int countSubmissionConfigs() { } @Override - public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) { - return submissionConfigReader.getSubmissionConfigByCollection(collectionHandle); + public SubmissionConfig getSubmissionConfigByCollection(Collection collection) { + return submissionConfigReader.getSubmissionConfigByCollection(collection); } @Override diff --git a/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java index cb1f828b93c4..4ac193109875 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java @@ -9,17 +9,20 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; import org.dspace.AbstractUnitTest; +import org.dspace.content.Collection; import org.dspace.submit.factory.SubmissionServiceFactory; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mock; /** * Tests for parsing and utilities on submission config forms / readers @@ -30,6 +33,9 @@ public class SubmissionConfigTest extends AbstractUnitTest { DCInputsReader inputReader; + @Mock + private Collection col1; + @BeforeClass public static void setUpClass() { } @@ -56,6 +62,8 @@ public void testReadAndProcessTypeBindSubmissionConfig() String typeBindSubmissionName = "typebindtest"; String typeBindSubmissionStepName = "typebindtest"; + when(col1.getHandle()).thenReturn(typeBindHandle); + // Expected field lists from typebindtest form List allConfiguredFields = new ArrayList<>(); allConfiguredFields.add("dc.title"); @@ -67,7 +75,7 @@ public void testReadAndProcessTypeBindSubmissionConfig() // Get submission configuration SubmissionConfig submissionConfig = SubmissionServiceFactory.getInstance().getSubmissionConfigService() - .getSubmissionConfigByCollection(typeBindHandle); + .getSubmissionConfigByCollection(col1); // Submission name should match name defined in item-submission.xml assertEquals(typeBindSubmissionName, submissionConfig.getSubmissionName()); // Step 0 - our process only has one step. It should not be null and have the ID typebindtest diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java index a5431d90004f..a80c8bd948b9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java @@ -82,7 +82,7 @@ protected void fillFromModel(T obj, R witem, Projection projection) { if (collection != null) { SubmissionDefinitionRest def = converter.toRest( - submissionConfigService.getSubmissionConfigByCollection(collection.getHandle()), projection); + submissionConfigService.getSubmissionConfigByCollection(collection), projection); witem.setSubmissionDefinition(def); for (SubmissionSectionRest sections : def.getPanels()) { SubmissionStepConfig stepConfig = submissionSectionConverter.toModel(sections); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java index d964994928eb..17eb90b7901e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java @@ -70,7 +70,7 @@ public SubmissionDefinitionRest findByCollection(@Parameter(value = "uuid", requ return null; } SubmissionDefinitionRest def = converter - .toRest(submissionConfigService.getSubmissionConfigByCollection(col.getHandle()), + .toRest(submissionConfigService.getSubmissionConfigByCollection(col), utils.obtainProjection()); return def; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java index b4d04e59e32f..5f4bb0dfe927 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java @@ -251,7 +251,7 @@ public Iterable upload(Context context, HttpServletRequest re } SubmissionConfig submissionConfig = - submissionConfigService.getSubmissionConfigByCollection(collection.getHandle()); + submissionConfigService.getSubmissionConfigByCollection(collection); List result = null; List records = new ArrayList<>(); try { From cf6008271a731f7664a003bd797cb42b568848eb Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 19 Apr 2024 21:38:31 +0200 Subject: [PATCH 130/479] Refactor SubmissionConfigReader to use a map for the collections configured through the entityType value --- .../app/util/SubmissionConfigReader.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 82bb1c9ca4a8..7342cc4283fe 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -22,10 +22,10 @@ import org.apache.logging.log4j.Logger; import org.dspace.content.Collection; import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.core.Context; -import org.dspace.discovery.SearchServiceException; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.services.factory.DSpaceServicesFactory; import org.w3c.dom.Document; @@ -96,6 +96,13 @@ public class SubmissionConfigReader { */ private Map> stepDefns = null; + /** + * Hashmap which stores which submission process configuration is used by + * which entityType, computed from the item submission config file + * (specifically, the 'submission-map' tag) + */ + private Map entityTypeToSubmissionConfig = null; + /** * Reference to the item submission definitions defined in the * "submission-definitions" section @@ -127,6 +134,7 @@ public SubmissionConfigReader() throws SubmissionConfigReaderException { public void reload() throws SubmissionConfigReaderException { collectionToSubmissionConfig = null; + entityTypeToSubmissionConfig = null; stepDefns = null; submitDefns = null; buildInputs(configDir + SUBMIT_DEF_FILE_PREFIX + SUBMIT_DEF_FILE_SUFFIX); @@ -145,6 +153,7 @@ public void reload() throws SubmissionConfigReaderException { */ private void buildInputs(String fileName) throws SubmissionConfigReaderException { collectionToSubmissionConfig = new HashMap(); + entityTypeToSubmissionConfig = new HashMap(); submitDefns = new HashMap>>(); String uri = "file:" + new File(fileName).getAbsolutePath(); @@ -162,9 +171,6 @@ private void buildInputs(String fileName) throws SubmissionConfigReaderException } catch (FactoryConfigurationError fe) { throw new SubmissionConfigReaderException( "Cannot create Item Submission Configuration parser", fe); - } catch (SearchServiceException se) { - throw new SubmissionConfigReaderException( - "Cannot perform a discovery search for Item Submission Configuration", se); } catch (Exception e) { throw new SubmissionConfigReaderException( "Error creating Item Submission Configuration: " + e); @@ -228,6 +234,16 @@ public SubmissionConfig getSubmissionConfigByCollection(Collection col) { } } + // get the name of the submission process based on the entity type of this collections + if (!entityTypeToSubmissionConfig.isEmpty()) { + String entityType = collectionService.getMetadataFirstValue(col, "dspace", "entity", "type", Item.ANY); + submitName = entityTypeToSubmissionConfig + .get(entityType); + if (submitName != null) { + return getSubmissionConfigByName(submitName); + } + } + submitName = collectionToSubmissionConfig.get(DEFAULT_COLLECTION); if (submitName == null) { @@ -308,7 +324,7 @@ public SubmissionStepConfig getStepConfig(String stepID) * should correspond to the collection-form maps, the form definitions, and * the display/storage word pairs. */ - private void doNodes(Node n) throws SAXException, SearchServiceException, SubmissionConfigReaderException { + private void doNodes(Node n) throws SAXException, SubmissionConfigReaderException { if (n == null) { return; } @@ -355,9 +371,7 @@ private void doNodes(Node n) throws SAXException, SearchServiceException, Submis * the collection handle and item submission name, put name in hashmap keyed * by the collection handle. */ - private void processMap(Node e) throws SAXException, SearchServiceException { - // create a context - Context context = new Context(); + private void processMap(Node e) throws SAXException { NodeList nl = e.getChildNodes(); int len = nl.getLength(); @@ -377,7 +391,7 @@ private void processMap(Node e) throws SAXException, SearchServiceException { throw new SAXException( "name-map element is missing submission-name attribute in 'item-submission.xml'"); } - if (content != null && content.length() > 0) { + if (content != null && !content.isEmpty()) { throw new SAXException( "name-map element has content in 'item-submission.xml', it should be empty."); } @@ -385,12 +399,7 @@ private void processMap(Node e) throws SAXException, SearchServiceException { collectionToSubmissionConfig.put(id, value); } else { - // get all collections for this entity-type - List collections = collectionService.findAllCollectionsByEntityType( context, - entityType); - for (Collection collection : collections) { - collectionToSubmissionConfig.putIfAbsent(collection.getHandle(), value); - } + entityTypeToSubmissionConfig.put(entityType, value); } } // ignore any child node that isn't a "name-map" } From 1a65dfb15b021f5e58bdb821ee8d85330c416dd1 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 19 Apr 2024 21:39:19 +0200 Subject: [PATCH 131/479] Remove the consumer submissionconfig from default configuration because it is not needed to reload forms if a collection entitytype is changed --- dspace/config/dspace.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index b7cc13e508dc..60a7506d8766 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -780,7 +780,7 @@ event.dispatcher.default.class = org.dspace.event.BasicDispatcher # Add rdf here, if you are using dspace-rdf to export your repository content as RDF. # Add iiif here, if you are using dspace-iiif. # Add orcidqueue here, if the integration with ORCID is configured and wish to enable the synchronization queue functionality -event.dispatcher.default.consumers = versioning, discovery, eperson, submissionconfig +event.dispatcher.default.consumers = versioning, discovery, eperson # The noindex dispatcher will not create search or browse indexes (useful for batch item imports) event.dispatcher.noindex.class = org.dspace.event.BasicDispatcher From 770b38c7934ba58cea1bcf23d819332128e2a956 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 19 Apr 2024 22:35:55 +0200 Subject: [PATCH 132/479] Add test for item process submission forms mapped by entityType --- .../app/util/SubmissionConfigReader.java | 3 +- .../dspaceFolder/config/item-submission.xml | 10 + .../dspace/app/util/SubmissionConfigIT.java | 60 ++++ .../SubmissionDefinitionsControllerIT.java | 299 ++++++++++++------ 4 files changed, 275 insertions(+), 97 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 7342cc4283fe..70c5092602a3 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -154,7 +155,7 @@ public void reload() throws SubmissionConfigReaderException { private void buildInputs(String fileName) throws SubmissionConfigReaderException { collectionToSubmissionConfig = new HashMap(); entityTypeToSubmissionConfig = new HashMap(); - submitDefns = new HashMap>>(); + submitDefns = new LinkedHashMap>>(); String uri = "file:" + new File(fileName).getAbsolutePath(); diff --git a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml index 452460501a54..2b4cee044916 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -24,6 +24,8 @@ + + @@ -257,6 +259,14 @@ + + + + + + + + diff --git a/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java new file mode 100644 index 000000000000..f171c45328a7 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.util; + +import static org.junit.Assert.assertEquals; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.submit.factory.SubmissionServiceFactory; +import org.dspace.submit.service.SubmissionConfigService; +import org.junit.Test; + +/** + * Integration Tests for parsing and utilities on submission config forms / readers + * + * @author Toni Prieto + */ +public class SubmissionConfigIT extends AbstractIntegrationTestWithDatabase { + + @Test + public void testSubmissionConfigMapByCollectionOrEntityType() + throws SubmissionConfigReaderException { + + context.turnOffAuthorisationSystem(); + // Sep up a structure with one top community and two collections + Community topcom = CommunityBuilder.createCommunity(context, "123456789/topcommunity-test") + .withName("Parent Community") + .build(); + // col1 should use the item submission form directly mapped for this collection + Collection col1 = CollectionBuilder.createCollection(context, topcom, "123456789/collection-test") + .withName("Collection 1") + .withEntityType("CustomEntityType") + .build(); + // col2 should use the item submission form mapped for the entity type CustomEntityType + Collection col2 = CollectionBuilder.createCollection(context, topcom, "123456789/not-mapped1") + .withName("Collection 2") + .withEntityType("CustomEntityType") + .build(); + context.restoreAuthSystemState(); + + SubmissionConfigService submissionConfigService = SubmissionServiceFactory.getInstance() + .getSubmissionConfigService(); + + // for col1, it should return the item submission form defined directly for the collection + SubmissionConfig submissionConfig1 = submissionConfigService.getSubmissionConfigByCollection(col1); + assertEquals("collectiontest", submissionConfig1.getSubmissionName()); + + // for col2, it should return the item submission form defined for the entitytype CustomEntityType + SubmissionConfig submissionConfig2 = submissionConfigService.getSubmissionConfigByCollection(col2); + assertEquals("entitytypetest", submissionConfig2.getSubmissionName()); + } +} \ No newline at end of file diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java index babb1fac2326..1a1a4576dc89 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java @@ -32,6 +32,11 @@ */ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegrationTest { + // The total number of expected submission definitions is referred to in multiple tests and assertions as + // is the last page (totalDefinitions - 1) + // This integer should be maintained along with any changes to item-submissions.xml + private static final int totalDefinitions = 9; + @Test public void findAll() throws Exception { //When we call the root endpoint as anonymous user @@ -243,110 +248,110 @@ public void findAllPaginationTest() throws Exception { getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") .param("size", "1") .param("page", "0")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("traditional"))) - .andExpect(jsonPath("$._links.first.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=0"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.self.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=0"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.next.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=1"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.last.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=6"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(7))) - .andExpect(jsonPath("$.page.totalPages", is(7))) - .andExpect(jsonPath("$.page.number", is(0))); + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("traditional"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=1"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(0))); getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") .param("size", "1") .param("page", "1")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("test-hidden"))) - .andExpect(jsonPath("$._links.first.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=0"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=0"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.next.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=2"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.self.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=1"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.last.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page="), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(7))) - .andExpect(jsonPath("$.page.totalPages", is(7))) - .andExpect(jsonPath("$.page.number", is(1))); + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("languagetestprocess"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=2"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=1"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(1))); getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") .param("size", "1") .param("page", "2")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("accessConditionNotDiscoverable"))) - .andExpect(jsonPath("$._links.first.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=0"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=1"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.next.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=3"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.self.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=2"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.last.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=6"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(7))) - .andExpect(jsonPath("$.page.totalPages", is(7))) - .andExpect(jsonPath("$.page.number", is(2))); + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("extractiontestprocess"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=1"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=3"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=2"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(2))); getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") .param("size", "1") .param("page", "3")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("languagetestprocess"))) - .andExpect(jsonPath("$._links.first.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=0"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=2"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.next.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=4"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.self.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=3"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$._links.last.href", Matchers.allOf( - Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=6"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(7))) - .andExpect(jsonPath("$.page.totalPages", is(7))) - .andExpect(jsonPath("$.page.number", is(3))); + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("qualdroptest"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=2"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=4"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=3"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(3))); getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") - .param("size", "1") - .param("page", "4")) + .param("size", "1") + .param("page", "4")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("qualdroptest"))) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("typebindtest"))) .andExpect(jsonPath("$._links.first.href", Matchers.allOf( Matchers.containsString("/api/config/submissiondefinitions?"), Matchers.containsString("page=0"), Matchers.containsString("size=1")))) @@ -361,18 +366,18 @@ public void findAllPaginationTest() throws Exception { Matchers.containsString("page=4"), Matchers.containsString("size=1")))) .andExpect(jsonPath("$._links.last.href", Matchers.allOf( Matchers.containsString("/api/config/submissiondefinitions?"), - Matchers.containsString("page=6"), Matchers.containsString("size=1")))) + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(7))) - .andExpect(jsonPath("$.page.totalPages", is(7))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) .andExpect(jsonPath("$.page.number", is(4))); getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") - .param("size", "1") - .param("page", "5")) + .param("size", "1") + .param("page", "5")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("extractiontestprocess"))) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("accessConditionNotDiscoverable"))) .andExpect(jsonPath("$._links.first.href", Matchers.allOf( Matchers.containsString("/api/config/submissiondefinitions?"), Matchers.containsString("page=0"), Matchers.containsString("size=1")))) @@ -386,12 +391,114 @@ public void findAllPaginationTest() throws Exception { Matchers.containsString("/api/config/submissiondefinitions?"), Matchers.containsString("page=5"), Matchers.containsString("size=1")))) .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(5))); + + getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") + .param("size", "1") + .param("page", "5")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("accessConditionNotDiscoverable"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=4"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( Matchers.containsString("/api/config/submissiondefinitions?"), Matchers.containsString("page=6"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=5"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(7))) - .andExpect(jsonPath("$.page.totalPages", is(7))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) .andExpect(jsonPath("$.page.number", is(5))); + + getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") + .param("size", "1") + .param("page", "6")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("test-hidden"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=5"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=7"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=6"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(6))); + + getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") + .param("size", "1") + .param("page", "7")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("collectiontest"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=6"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=8"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=7"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(7))); + + getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions") + .param("size", "1") + .param("page", "8")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("entitytypetest"))) + .andExpect(jsonPath("$._links.first.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=0"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.prev.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=7"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=8"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.last.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=" + (totalDefinitions - 1)), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(totalDefinitions))) + .andExpect(jsonPath("$.page.totalPages", is(totalDefinitions))) + .andExpect(jsonPath("$.page.number", is(8))); + } } From d5e2c71da40ae60c578557dda697b5d2824a5141 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 22 Apr 2024 10:42:21 -0500 Subject: [PATCH 133/479] Minor fixes to Entities import. MUST run "ignored" migrations. Also modifying submission configs no longer needed --- .../src/main/docker-compose/db.entities.yml | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/dspace/src/main/docker-compose/db.entities.yml b/dspace/src/main/docker-compose/db.entities.yml index 32c54a5d0bd1..809cf52df0b6 100644 --- a/dspace/src/main/docker-compose/db.entities.yml +++ b/dspace/src/main/docker-compose/db.entities.yml @@ -18,27 +18,12 @@ services: ### OVERRIDE default 'entrypoint' in 'docker-compose.yml #### # Ensure that the database is ready BEFORE starting tomcat # 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep - # 2. Then, run database migration to init database tables - # 3. (Custom for Entities) enable Entity-specific collection submission mappings in item-submission.xml - # This 'sed' command inserts the sample configurations specific to the Entities data set, see: - # https://github.com/DSpace/DSpace/blob/main/dspace/config/item-submission.xml#L36-L49 - # 4. Finally, start Tomcat + # 2. Then, run migration latest version of database tables (run with "ignored" in case this SQL data is outdated) + # 3. Finally, start Tomcat entrypoint: - /bin/bash - '-c' - | while (! /dev/null 2>&1; do sleep 1; done; - /dspace/bin/dspace database migrate - sed -i '/name-map collection-handle="default".*/a \\n \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - ' /dspace/config/item-submission.xml + /dspace/bin/dspace database migrate ignored catalina.sh run From 69f7f85defd40d1d97a48d0a4a27ccee6de91bfb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 22 Apr 2024 10:43:06 -0500 Subject: [PATCH 134/479] Minor fixes to DB scripts. Use Postgres 15. Don't error out if pgcrypto already installed. --- .../src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile | 2 +- .../docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh | 4 ++-- dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile | 2 +- .../main/docker/dspace-postgres-pgcrypto/install-pgcrypto.sh | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index b2131a740262..aabf87df3eda 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -10,7 +10,7 @@ # docker build --build-arg POSTGRES_VERSION=13 --build-arg POSTGRES_PASSWORD=mypass ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/ # This will be published as dspace/dspace-postgres-pgcrypto:$DSPACE_VERSION-loadsql -ARG POSTGRES_VERSION=13 +ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace FROM postgres:${POSTGRES_VERSION} diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh index 3f8e95e1044f..ff042ec1bba8 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh @@ -40,9 +40,9 @@ fi # Then, setup pgcrypto on this database psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL -- Create a new schema in this database named "extensions" (or whatever you want to name it) - CREATE SCHEMA extensions; + CREATE SCHEMA IF NOT EXISTS extensions; -- Enable this extension in this new schema - CREATE EXTENSION pgcrypto SCHEMA extensions; + CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; -- Update your database's "search_path" to also search the new "extensions" schema. -- You are just appending it on the end of the existing comma-separated list. ALTER DATABASE dspace SET search_path TO "\$user",public,extensions; diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 7dde1a6bfd1c..2298cd4e76ea 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -10,7 +10,7 @@ # docker build --build-arg POSTGRES_VERSION=13 --build-arg POSTGRES_PASSWORD=mypass ./dspace/src/main/docker/dspace-postgres-pgcrypto/ # This will be published as dspace/dspace-postgres-pgcrypto:$DSPACE_VERSION -ARG POSTGRES_VERSION=13 +ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace FROM postgres:${POSTGRES_VERSION} diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/install-pgcrypto.sh b/dspace/src/main/docker/dspace-postgres-pgcrypto/install-pgcrypto.sh index 65405aa7bdb6..67c4539b5a46 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/install-pgcrypto.sh +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/install-pgcrypto.sh @@ -11,9 +11,9 @@ set -e psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL -- Create a new schema in this database named "extensions" (or whatever you want to name it) - CREATE SCHEMA extensions; + CREATE SCHEMA IF NOT EXISTS extensions; -- Enable this extension in this new schema - CREATE EXTENSION pgcrypto SCHEMA extensions; + CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; -- Update your database's "search_path" to also search the new "extensions" schema. -- You are just appending it on the end of the existing comma-separated list. ALTER DATABASE dspace SET search_path TO "\$user",public,extensions; From b1fb884aec4f407be205ceaf90416f6e8497d2da Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 22 Apr 2024 14:15:13 -0500 Subject: [PATCH 135/479] Remove unnecessary "exit" statements which stop running container (cherry picked from commit 11158ae525a68be445099072168c324197697b96) --- .../docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh index ff042ec1bba8..d8e0382010df 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/install-pgcrypto.sh @@ -23,7 +23,6 @@ then rm /tmp/dspace-db-init.sql touch $CHECKFILE - exit fi # If $LOCALSQL environment variable set, then simply run it in PostgreSQL @@ -34,7 +33,6 @@ then psql -U $POSTGRES_USER < ${LOCALSQL} touch $CHECKFILE - exit fi # Then, setup pgcrypto on this database From 362c80136bfde04e3801796afd2d7e3d8485d392 Mon Sep 17 00:00:00 2001 From: Thomas Misilo Date: Sat, 9 Mar 2024 13:48:42 -0600 Subject: [PATCH 136/479] Change from openjdk to eclipse-temurin base images Since the opendjk image has been deprecated, it was suggested to change to eclipse-temurin Fixes #9277 (cherry picked from commit 86ca5aabf9ab71b3041562f0ac600f872ccd443f) --- Dockerfile | 2 +- Dockerfile.cli | 4 ++-- Dockerfile.dependencies | 2 +- Dockerfile.test | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3c536b317629..ee48dec5083c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ mvn clean # Step 2 - Run Ant Deploy -FROM openjdk:${JDK_VERSION}-slim as ant_build +FROM eclipse-temurin:${JDK_VERSION} as ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src diff --git a/Dockerfile.cli b/Dockerfile.cli index 62e83b79ef02..96a84bc56268 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -24,7 +24,7 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM openjdk:${JDK_VERSION}-slim as ant_build +FROM eclipse-temurin:${JDK_VERSION} as ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src @@ -45,7 +45,7 @@ RUN mkdir $ANT_HOME && \ RUN ant init_installation update_configs update_code # Step 3 - Run jdk -FROM openjdk:${JDK_VERSION} +FROM eclipse-temurin:${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 6f72ab058536..1400b356d418 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM maven:3-openjdk-${JDK_VERSION}-slim as build +FROM maven:3-eclipse-temurin-${JDK_VERSION} as build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory diff --git a/Dockerfile.test b/Dockerfile.test index 4e9b2b5b4343..f6f8c1a290f9 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -27,7 +27,7 @@ RUN mvn --no-transfer-progress package -Pdspace-rest && \ mvn clean # Step 2 - Run Ant Deploy -FROM openjdk:${JDK_VERSION}-slim as ant_build +FROM eclipse-temurin:${JDK_VERSION} as ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From 1cfa041a315d023a9ded6766571d4e8dc0280b2f Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 26 Apr 2024 10:07:10 +0200 Subject: [PATCH 137/479] Remove unused function findAllCollectionsByEntityType of CollectionService --- .../dspace/content/CollectionServiceImpl.java | 20 ------------------- .../content/service/CollectionService.java | 15 -------------- 2 files changed, 35 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java index 0d9507e3aa35..a0ac5b1c639a 100644 --- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java @@ -1054,26 +1054,6 @@ public int countCollectionsWithSubmit(String q, Context context, Community commu return (int) resp.getTotalSearchResults(); } - @Override - @SuppressWarnings("rawtypes") - public List findAllCollectionsByEntityType(Context context, String entityType) - throws SearchServiceException { - List collectionList = new ArrayList<>(); - - DiscoverQuery discoverQuery = new DiscoverQuery(); - discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE); - discoverQuery.addFilterQueries("dspace.entity.type:" + entityType); - - DiscoverResult discoverResult = searchService.search(context, discoverQuery); - List solrIndexableObjects = discoverResult.getIndexableObjects(); - - for (IndexableObject solrCollection : solrIndexableObjects) { - Collection c = ((IndexableCollection) solrCollection).getIndexedObject(); - collectionList.add(c); - } - return collectionList; - } - /** * Returns total collection archived items * diff --git a/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java b/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java index 90db5c731402..b923549b1998 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java @@ -456,21 +456,6 @@ public int countCollectionsWithSubmit(String q, Context context, Community commu public int countCollectionsWithSubmit(String q, Context context, Community community, String entityType) throws SQLException, SearchServiceException; - /** - * Returns a list of all collections for a specific entity type. - * NOTE: for better performance, this method retrieves its results from an index (cache) - * and does not query the database directly. - * This means that results may be stale or outdated until - * https://github.com/DSpace/DSpace/issues/2853 is resolved." - * - * @param context DSpace Context - * @param entityType limit the returned collection to those related to given entity type - * @return list of collections found - * @throws SearchServiceException if search error - */ - public List findAllCollectionsByEntityType(Context context, String entityType) - throws SearchServiceException; - /** * Returns total collection archived items * From 10aa3189198e3c6023625acb8b94037239d5cc51 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 26 Apr 2024 14:48:08 +0200 Subject: [PATCH 138/479] Add comment to clarify in which use case the consumer submissionconfig is useful --- dspace/config/dspace.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 60a7506d8766..0b7cd6ee44ae 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -823,6 +823,11 @@ event.consumer.orcidqueue.class = org.dspace.orcid.consumer.OrcidQueueConsumer event.consumer.orcidqueue.filters = Item+Install|Modify|Modify_Metadata|Delete|Remove # item submission config reload consumer +# This consumer can be useful for reloading changes made in the item-submission.xml config file, +# without restarting Tomcat, primarily for adding new collection mappings. +# With this consumer, configuration reloading is triggered after a collection is updated. +# It is disabled by default. To enable it, add 'submissionconfig' to the list of +# activated consumers (event.dispatcher.default.consumers). event.consumer.submissionconfig.class = org.dspace.submit.consumer.SubmissionConfigConsumer event.consumer.submissionconfig.filters = Collection+Modify_Metadata From 9d12600d13c6f5783d2c9afc2902ff448aaac8e8 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 18 Apr 2023 15:47:14 +0200 Subject: [PATCH 139/479] [Ticket 2124] Slow response times --- .../authorization/impl/DeleteFeature.java | 6 +- .../converter/AInprogressItemConverter.java | 10 +- .../rest/model/AInprogressSubmissionRest.java | 29 +-- .../app/rest/model/WorkflowItemRest.java | 25 +- .../app/rest/model/WorkspaceItemRest.java | 15 ++ .../WorkflowItemCollectionLinkRepository.java | 60 +++++ .../WorkflowItemItemLinkRepository.java | 60 +++++ .../WorkflowItemSubmitterLinkRepository.java | 60 +++++ ...WorkspaceItemCollectionLinkRepository.java | 60 +++++ .../WorkspaceItemItemLinkRepository.java | 60 +++++ .../WorkspaceItemSubmitterLinkRepository.java | 60 +++++ .../jwt/ShortLivedJWTTokenHandler.java | 1 - .../WorkflowItemRestLinkRepositoryIT.java | 226 ++++++++++++++++++ .../WorkspaceItemRestLinkRepositoryIT.java | 222 +++++++++++++++++ 14 files changed, 854 insertions(+), 40 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemCollectionLinkRepository.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemItemLinkRepository.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemSubmitterLinkRepository.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemCollectionLinkRepository.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemItemLinkRepository.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemSubmitterLinkRepository.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestLinkRepositoryIT.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestLinkRepositoryIT.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/DeleteFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/DeleteFeature.java index 02ca816290d0..0f5378125d8f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/DeleteFeature.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/DeleteFeature.java @@ -65,11 +65,13 @@ public class DeleteFeature implements AuthorizationFeature { @Override public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException { if (object instanceof BaseObjectRest) { + DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object); + if (object.getType().equals(WorkspaceItemRest.NAME)) { - object = ((WorkspaceItemRest)object).getItem(); + WorkspaceItem workspaceItem = (WorkspaceItem) utils.getDSpaceAPIObjectFromRest(context, object); + dSpaceObject = workspaceItem.getItem(); } - DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object); DSpaceObject parentObject = getParentObject(context, dSpaceObject); switch (object.getType()) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java index a5431d90004f..f40022f19f79 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java @@ -70,11 +70,11 @@ protected void fillFromModel(T obj, R witem, Projection projection) { submitter = obj.getSubmitter(); witem.setId(obj.getID()); - witem.setCollection(collection != null ? converter.toRest(collection, projection) : null); - witem.setItem(converter.toRest(item, projection)); - if (submitter != null) { - witem.setSubmitter(converter.toRest(submitter, projection)); - } +// witem.setCollection(collection != null ? converter.toRest(collection, projection) : null); +// witem.setItem(converter.toRest(item, projection)); +// if (submitter != null) { +// witem.setSubmitter(converter.toRest(submitter, projection)); +// } // 1. retrieve the submission definition // 2. iterate over the submission section to allow to plugin additional diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AInprogressSubmissionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AInprogressSubmissionRest.java index 903e2866c855..79cf007ba190 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AInprogressSubmissionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AInprogressSubmissionRest.java @@ -22,16 +22,11 @@ */ public abstract class AInprogressSubmissionRest extends BaseObjectRest { + private Date lastModified = new Date(); private Map sections; @JsonIgnore - private CollectionRest collection; - @JsonIgnore - private ItemRest item; - @JsonIgnore private SubmissionDefinitionRest submissionDefinition; - @JsonIgnore - private EPersonRest submitter; public Date getLastModified() { return lastModified; @@ -41,14 +36,6 @@ public void setLastModified(Date lastModified) { this.lastModified = lastModified; } - public ItemRest getItem() { - return item; - } - - public void setItem(ItemRest item) { - this.item = item; - } - public SubmissionDefinitionRest getSubmissionDefinition() { return submissionDefinition; } @@ -57,14 +44,6 @@ public void setSubmissionDefinition(SubmissionDefinitionRest submissionDefinitio this.submissionDefinition = submissionDefinition; } - public EPersonRest getSubmitter() { - return submitter; - } - - public void setSubmitter(EPersonRest submitter) { - this.submitter = submitter; - } - public Map getSections() { if (sections == null) { sections = new HashMap(); @@ -76,12 +55,6 @@ public void setSections(Map sections) { this.sections = sections; } - public CollectionRest getCollection() { - return collection; - } - public void setCollection(CollectionRest collection) { - this.collection = collection; - } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java index 8f580f441477..4a840a0bb77b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java @@ -15,10 +15,22 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkflowItemRest.STEP, - method = "getStep" - ) + @LinkRest( + name = WorkflowItemRest.STEP, + method = "getStep" + ), + @LinkRest( + name = WorkflowItemRest.SUBMITTER, + method = "getWorkflowItemSubmitter" + ), + @LinkRest( + name = WorkflowItemRest.ITEM, + method = "getWorkflowItemItem" + ), + @LinkRest( + name = WorkflowItemRest.COLLECTION, + method = "getWorkflowItemCollection" + ) }) public class WorkflowItemRest extends AInprogressSubmissionRest { public static final String NAME = "workflowitem"; @@ -26,6 +38,11 @@ public class WorkflowItemRest extends AInprogressSubmissionRest { public static final String STEP = "step"; + public static final String SUBMITTER = "submitter"; + public static final String ITEM = "item"; + public static final String COLLECTION = "collection"; + + @Override public String getCategory() { return CATEGORY; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java index 57a5ab5c7f0e..b40d1a4494f4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java @@ -18,6 +18,18 @@ @LinkRest( name = WorkspaceItemRest.SUPERVISION_ORDERS, method = "getSupervisionOrders" + ), + @LinkRest( + name = WorkspaceItemRest.SUBMITTER, + method = "getWorkspaceItemSubmitter" + ), + @LinkRest( + name = WorkspaceItemRest.ITEM, + method = "getWorkspaceItemItem" + ), + @LinkRest( + name = WorkspaceItemRest.COLLECTION, + method = "getWorkspaceItemCollection" ) }) public class WorkspaceItemRest extends AInprogressSubmissionRest { @@ -25,6 +37,9 @@ public class WorkspaceItemRest extends AInprogressSubmissionRest { public static final String CATEGORY = RestAddressableModel.SUBMISSION; public static final String SUPERVISION_ORDERS = "supervisionOrders"; + public static final String SUBMITTER = "submitter"; + public static final String ITEM = "item"; + public static final String COLLECTION = "collection"; @Override public String getCategory() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemCollectionLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemCollectionLinkRepository.java new file mode 100644 index 000000000000..fa92a69e77d6 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemCollectionLinkRepository.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.CollectionRest; +import org.dspace.app.rest.model.WorkflowItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.core.Context; +import org.dspace.workflow.WorkflowItem; +import org.dspace.xmlworkflow.storedcomponents.service.XmlWorkflowItemService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "collection" subresource of a workflow item. + */ +@Component(WorkflowItemRest.CATEGORY + "." + WorkflowItemRest.NAME + "." + WorkflowItemRest.COLLECTION) +public class WorkflowItemCollectionLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + XmlWorkflowItemService wis; + + /** + * Retrieve the item for a workflow collection. + * + * @param request - The current request + * @param id - The workflow item ID for which to retrieve the collection + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the item for the workflow collection + */ + @PreAuthorize("hasPermission(#id, 'WORKFLOWITEM', 'READ')") + public CollectionRest getWorkflowItemCollection(@Nullable HttpServletRequest request, Integer id, + @Nullable Pageable optionalPageable, Projection projection) { + try { + Context context = obtainContext(); + WorkflowItem witem = wis.find(context, id); + if (witem == null) { + throw new ResourceNotFoundException("No such workflow item: " + id); + } + + return converter.toRest(witem.getCollection(), projection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemItemLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemItemLinkRepository.java new file mode 100644 index 000000000000..40624799bff4 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemItemLinkRepository.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.ItemRest; +import org.dspace.app.rest.model.WorkflowItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.core.Context; +import org.dspace.workflow.WorkflowItem; +import org.dspace.xmlworkflow.storedcomponents.service.XmlWorkflowItemService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "item" subresource of a workflow item. + */ +@Component(WorkflowItemRest.CATEGORY + "." + WorkflowItemRest.NAME + "." + WorkflowItemRest.ITEM) +public class WorkflowItemItemLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + XmlWorkflowItemService wis; + + /** + * Retrieve the item for a workflow item. + * + * @param request - The current request + * @param id - The workflow item ID for which to retrieve the item + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the item for the workflow item + */ + @PreAuthorize("hasPermission(#id, 'WORKFLOWITEM', 'READ')") + public ItemRest getWorkflowItemItem(@Nullable HttpServletRequest request, Integer id, + @Nullable Pageable optionalPageable, Projection projection) { + try { + Context context = obtainContext(); + WorkflowItem witem = wis.find(context, id); + if (witem == null) { + throw new ResourceNotFoundException("No such workflow item: " + id); + } + + return converter.toRest(witem.getItem(), projection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemSubmitterLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemSubmitterLinkRepository.java new file mode 100644 index 000000000000..b4b0ec0adfff --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemSubmitterLinkRepository.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.EPersonRest; +import org.dspace.app.rest.model.WorkflowItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.core.Context; +import org.dspace.workflow.WorkflowItem; +import org.dspace.xmlworkflow.storedcomponents.service.XmlWorkflowItemService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "submitter" subresource of a workflow item. + */ +@Component(WorkflowItemRest.CATEGORY + "." + WorkflowItemRest.NAME + "." + WorkflowItemRest.SUBMITTER) +public class WorkflowItemSubmitterLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + XmlWorkflowItemService wis; + + /** + * Retrieve the submitter for a workflow item. + * + * @param request - The current request + * @param id - The workflow item ID for which to retrieve the submitter + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the submitter for the workflow item + */ + @PreAuthorize("hasPermission(#id, 'WORKFLOWITEM', 'READ')") + public EPersonRest getWorkflowItemSubmitter(@Nullable HttpServletRequest request, Integer id, + @Nullable Pageable optionalPageable, Projection projection) { + try { + Context context = obtainContext(); + WorkflowItem witem = wis.find(context, id); + if (witem == null) { + throw new ResourceNotFoundException("No such workflow item: " + id); + } + + return converter.toRest(witem.getSubmitter(), projection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemCollectionLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemCollectionLinkRepository.java new file mode 100644 index 000000000000..c8f9373b07c7 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemCollectionLinkRepository.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.CollectionRest; +import org.dspace.app.rest.model.WorkspaceItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.service.WorkspaceItemService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "collection" subresource of a workspace item. + */ +@Component(WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME + "." + WorkspaceItemRest.COLLECTION) +public class WorkspaceItemCollectionLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + WorkspaceItemService wis; + + /** + * Retrieve the collection for a workspace item. + * + * @param request - The current request + * @param id - The workspace item ID for which to retrieve the collection + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the collection for the workspace item + */ + @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')") + public CollectionRest getWorkspaceItemCollection(@Nullable HttpServletRequest request, Integer id, + @Nullable Pageable optionalPageable, Projection projection) { + try { + Context context = obtainContext(); + WorkspaceItem witem = wis.find(context, id); + if (witem == null) { + throw new ResourceNotFoundException("No such workspace item: " + id); + } + + return converter.toRest(witem.getCollection(), projection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemItemLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemItemLinkRepository.java new file mode 100644 index 000000000000..48052fe5371f --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemItemLinkRepository.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.ItemRest; +import org.dspace.app.rest.model.WorkspaceItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.service.WorkspaceItemService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "item" subresource of a workspace item. + */ +@Component(WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME + "." + WorkspaceItemRest.ITEM) +public class WorkspaceItemItemLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + WorkspaceItemService wis; + + /** + * Retrieve the item for a workspace item. + * + * @param request - The current request + * @param id - The workspace item ID for which to retrieve the item + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the item for the workspace item + */ + @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')") + public ItemRest getWorkspaceItemItem(@Nullable HttpServletRequest request, Integer id, + @Nullable Pageable optionalPageable, Projection projection) { + try { + Context context = obtainContext(); + WorkspaceItem witem = wis.find(context, id); + if (witem == null) { + throw new ResourceNotFoundException("No such workspace item: " + id); + } + + return converter.toRest(witem.getItem(), projection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemSubmitterLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemSubmitterLinkRepository.java new file mode 100644 index 000000000000..a98de6a0b3ac --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemSubmitterLinkRepository.java @@ -0,0 +1,60 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.EPersonRest; +import org.dspace.app.rest.model.WorkspaceItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.service.WorkspaceItemService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "submitter" subresource of a workspace item. + */ +@Component(WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME + "." + WorkspaceItemRest.SUBMITTER) +public class WorkspaceItemSubmitterLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + WorkspaceItemService wis; + + /** + * Retrieve the submitter for a workspace item. + * + * @param request - The current request + * @param id - The workspace item ID for which to retrieve the submitter + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the submitter for the workspace item + */ + @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')") + public EPersonRest getWorkspaceItemSubmitter(@Nullable HttpServletRequest request, Integer id, + @Nullable Pageable optionalPageable, Projection projection) { + try { + Context context = obtainContext(); + WorkspaceItem witem = wis.find(context, id); + if (witem == null) { + throw new ResourceNotFoundException("No such workspace item: " + id); + } + + return converter.toRest(witem.getSubmitter(), projection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java index fc4ab39407a4..72c305b5d12e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java @@ -16,7 +16,6 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.util.DateUtils; -import org.apache.commons.lang3.StringUtils; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.springframework.stereotype.Component; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestLinkRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestLinkRepositoryIT.java new file mode 100644 index 000000000000..7228a7ba0b9d --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestLinkRepositoryIT.java @@ -0,0 +1,226 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.jayway.jsonpath.matchers.JsonPathMatchers; +import org.dspace.app.rest.matcher.CollectionMatcher; +import org.dspace.app.rest.matcher.EPersonMatcher; +import org.dspace.app.rest.matcher.ItemMatcher; +import org.dspace.app.rest.matcher.WorkflowItemMatcher; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EPersonBuilder; +import org.dspace.builder.WorkflowItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.eperson.EPerson; +import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Test suite for the WorkflowItem Link repositories + */ +public class WorkflowItemRestLinkRepositoryIT extends AbstractControllerIntegrationTest { + + @Test + /** + * The workflowitem resource endpoint must have an embeddable submitter + * + * @throws Exception + */ + public void findOneEmbedSubmitterTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@dspace.org") + .withNameInMetadata("Sub", "Mitter") + .build(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withWorkflowGroup("reviewer", admin) + .build(); + + XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1) + .withSubmitter(submitter) + .withTitle("Workflow Item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workflow Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.submitter"))); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID()).param("embed", "submitter")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workflow Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$._embedded.submitter", Matchers.is( + EPersonMatcher.matchEPersonEntry(submitter)))); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/submitter")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", EPersonMatcher.matchEPersonEntry(submitter))); + + } + + @Test + /** + * The workflowitem resource endpoint must have an embeddable collection + * + * @throws Exception + */ + public void findOneEmbedCollectionTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@dspace.org") + .withNameInMetadata("Sub", "Mitter") + .build(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withWorkflowGroup("reviewer", admin) + .build(); + + XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1) + .withSubmitter(submitter) + .withTitle("Workflow Item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workflow Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.collection"))); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID()).param("embed", "collection")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workflow Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$._embedded.collection", Matchers.is( + CollectionMatcher.matchCollection(col1)))); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/collection")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", CollectionMatcher.matchCollection(col1))); + + } + + @Test + /** + * The workflowitem resource endpoint must have an embeddable item + * + * @throws Exception + */ + public void findOneEmbedItemTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@dspace.org") + .withNameInMetadata("Sub", "Mitter") + .build(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withWorkflowGroup("reviewer", admin) + .build(); + + XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1) + .withSubmitter(submitter) + .withTitle("Workflow Item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())).andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workflow Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.item"))); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID()).param("embed", "item")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workflow Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$._embedded.item", Matchers.is( + ItemMatcher.matchItemProperties(witem.getItem())))); + + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/item")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", ItemMatcher.matchItemProperties(witem.getItem()))); + + } + + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestLinkRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestLinkRepositoryIT.java new file mode 100644 index 000000000000..709480e8cd52 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestLinkRepositoryIT.java @@ -0,0 +1,222 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.jayway.jsonpath.matchers.JsonPathMatchers; +import org.dspace.app.rest.matcher.CollectionMatcher; +import org.dspace.app.rest.matcher.EPersonMatcher; +import org.dspace.app.rest.matcher.ItemMatcher; +import org.dspace.app.rest.matcher.WorkspaceItemMatcher; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EPersonBuilder; +import org.dspace.builder.WorkspaceItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.WorkspaceItem; +import org.dspace.eperson.EPerson; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Test suite for the WorkspaceItem Link repositories + */ +public class WorkspaceItemRestLinkRepositoryIT extends AbstractControllerIntegrationTest { + + + @Test + /** + * The workspaceitem resource endpoint must have an embeddable submitter + * + * @throws Exception + */ + public void findOneEmbedSubmitterTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@dspace.org") + .withNameInMetadata("Sub", "Mitter") + .build(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + //2. a workspace item + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withSubmitter(submitter) + .withTitle("Workspace Item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workspace Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.submitter"))); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()).param("embed", "submitter")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workspace Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$._embedded.submitter", Matchers.is( + EPersonMatcher.matchEPersonEntry(submitter)))); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/submitter")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", EPersonMatcher.matchEPersonEntry(submitter))); + + } + + @Test + /** + * The workspaceitem resource endpoint must have an embeddable collection + * + * @throws Exception + */ + public void findOneEmbedCollectionTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@dspace.org") + .withNameInMetadata("Sub", "Mitter") + .build(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + //2. a workspace item + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withSubmitter(submitter) + .withTitle("Workspace Item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workspace Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.collection"))); + + getClient(authToken).perform( + get("/api/submission/workspaceitems/" + witem.getID()).param("embed", "collection")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workspace Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$._embedded.collection", Matchers.is( + CollectionMatcher.matchCollection(col1)))); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", CollectionMatcher.matchCollection(col1))); + + } + + @Test + /** + * The workspaceitem resource endpoint must have an embeddable item + * + * @throws Exception + */ + public void findOneEmbedItemTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@dspace.org") + .withNameInMetadata("Sub", "Mitter") + .build(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + //2. a workspace item + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withSubmitter(submitter) + .withTitle("Workspace Item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workspace Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.item"))); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()).param("embed", "item")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is( + WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Workspace Item 1", + "2017-10-17", + "ExtraEntry")))) + .andExpect(jsonPath("$._embedded.item", Matchers.is( + ItemMatcher.matchItemProperties(witem.getItem())))); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/item")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", ItemMatcher.matchItemProperties(witem.getItem()))); + + } + + +} From d139d06c58e95b6585c46987b716c2ecdfbf8d3b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 29 Apr 2024 12:58:20 +0200 Subject: [PATCH 140/479] 105866: test fixes --- .../jwt/ShortLivedJWTTokenHandler.java | 1 + .../app/rest/DiscoveryRestControllerIT.java | 18 ------- .../org/dspace/app/rest/LoginAsEPersonIT.java | 4 +- .../rest/WorkspaceItemRestRepositoryIT.java | 53 ++++++++++++++----- 4 files changed, 43 insertions(+), 33 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java index 72c305b5d12e..fc4ab39407a4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/ShortLivedJWTTokenHandler.java @@ -16,6 +16,7 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.util.DateUtils; +import org.apache.commons.lang3.StringUtils; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.springframework.stereotype.Component; 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 80d8ab2df422..cd84567d6493 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 @@ -5822,10 +5822,6 @@ public void discoverSearchPoolTaskObjectsTest() throws Exception { .andExpect(jsonPath("$._embedded.searchResult._embedded.objects", Matchers.contains( SearchResultMatcher.match("workflow", "pooltask", "pooltasks") ))) - .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",Matchers.contains( - allOf(hasJsonPath("$._embedded.indexableObject._embedded.workflowitem._embedded.item", - is(SearchResultMatcher.matchEmbeddedObjectOnItemName("item", "Mathematical Theory")))) - ))) .andExpect(jsonPath("$._embedded.searchResult.page.totalElements", is(1))); getClient(adminToken).perform(get("/api/discover/search/objects") @@ -5839,12 +5835,6 @@ public void discoverSearchPoolTaskObjectsTest() throws Exception { SearchResultMatcher.match("workflow", "pooltask", "pooltasks"), SearchResultMatcher.match("workflow", "pooltask", "pooltasks") ))) - .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",Matchers.containsInAnyOrder( - allOf(hasJsonPath("$._embedded.indexableObject._embedded.workflowitem._embedded.item", - is(SearchResultMatcher.matchEmbeddedObjectOnItemName("item", "Metaphysics")))), - allOf(hasJsonPath("$._embedded.indexableObject._embedded.workflowitem._embedded.item", - is(SearchResultMatcher.matchEmbeddedObjectOnItemName("item", "Test Metaphysics")))) - ))) .andExpect(jsonPath("$._embedded.searchResult.page.totalElements", is(2))); } @@ -5909,14 +5899,6 @@ public void discoverSearchPoolTaskObjectsEmptyQueryTest() throws Exception { SearchResultMatcher.match("workflow", "pooltask", "pooltasks"), SearchResultMatcher.match("workflow", "pooltask", "pooltasks") ))) - .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",Matchers.containsInAnyOrder( - allOf(hasJsonPath("$._embedded.indexableObject._embedded.workflowitem._embedded.item", - is(SearchResultMatcher.matchEmbeddedObjectOnItemName("item", "Mathematical Theory")))), - allOf(hasJsonPath("$._embedded.indexableObject._embedded.workflowitem._embedded.item", - is(SearchResultMatcher.matchEmbeddedObjectOnItemName("item", "Metaphysics")))), - allOf(hasJsonPath("$._embedded.indexableObject._embedded.workflowitem._embedded.item", - is(SearchResultMatcher.matchEmbeddedObjectOnItemName("item", "Test Metaphysics")))) - ))) .andExpect(jsonPath("$._embedded.searchResult.page.totalElements", is(3))); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LoginAsEPersonIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LoginAsEPersonIT.java index bf3b54c9224a..60d0e1a2a56d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LoginAsEPersonIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LoginAsEPersonIT.java @@ -191,6 +191,7 @@ public void createEmptyWorkspaceItemLoginOnBehalfOfCheckSubmitterTest() throws E // create a workspaceitem explicitly in the col1 MvcResult mvcResult = getClient(authToken).perform(post("/api/submission/workspaceitems") .param("owningCollection", col1.getID().toString()) + .param("embed", "collection") .header("X-On-Behalf-Of", eperson.getID()) .contentType(org.springframework .http.MediaType.APPLICATION_JSON)) @@ -204,7 +205,8 @@ public void createEmptyWorkspaceItemLoginOnBehalfOfCheckSubmitterTest() throws E Map map = mapper.readValue(content, Map.class); String workspaceItemId = String.valueOf(map.get("id")); - getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItemId)) + getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItemId) + .param("embed", "submitter")) .andExpect(jsonPath("$._embedded.submitter", EPersonMatcher.matchProperties(eperson))); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 8b2f3f093a37..82108c4bbeb0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -878,6 +878,7 @@ public void createEmptyWorkspateItemTest() throws Exception { // create a workspaceitem explicitly in the col1 getClient(authToken).perform(post("/api/submission/workspaceitems") .param("owningCollection", col1.getID().toString()) + .param("embed", "collection") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString()))) @@ -886,6 +887,7 @@ public void createEmptyWorkspateItemTest() throws Exception { // create a workspaceitem explicitly in the col2 getClient(authToken).perform(post("/api/submission/workspaceitems") .param("owningCollection", col2.getID().toString()) + .param("embed", "collection") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.collection.id", is(col2.getID().toString()))) @@ -894,10 +896,10 @@ public void createEmptyWorkspateItemTest() throws Exception { // create a workspaceitem without an explicit collection, this will go in the first valid collection for the // user: the col1 getClient(authToken).perform(post("/api/submission/workspaceitems") + .param("embed", "collection") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString()))) - .andExpect(jsonPath("$", WorkspaceItemMatcher.matchFullEmbeds())) .andDo(result -> idRef3.set(read(result.getResponse().getContentAsString(), "$.id"))); @@ -945,7 +947,8 @@ public void createSingleWorkspaceItemFromBibtexFileWithOneEntryTest() throws Exc try { // create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1) getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(bibtexFile)) + .file(bibtexFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -974,6 +977,7 @@ public void createSingleWorkspaceItemFromBibtexFileWithOneEntryTest() throws Exc try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") .file(bibtexFile) + .param("embed", "collection") .param("owningCollection", col2.getID().toString())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1037,7 +1041,8 @@ public void createSingleWorkspaceItemFromBibtexArticleFileWithOneEntryTest() thr try { // create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1) getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(bibtexFile)) + .file(bibtexFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0]" + @@ -1071,6 +1076,7 @@ public void createSingleWorkspaceItemFromBibtexArticleFileWithOneEntryTest() thr try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") .file(bibtexFile) + .param("embed", "collection") .param("owningCollection", col2.getID().toString())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0]" + @@ -1134,7 +1140,8 @@ public void createSingleWorkspaceItemFromBibtexFileWithDiacriticsTest() throws E try { // create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1) getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(bibtexFile)) + .file(bibtexFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections." + @@ -1165,6 +1172,7 @@ public void createSingleWorkspaceItemFromBibtexFileWithDiacriticsTest() throws E try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") .file(bibtexFile) + .param("embed", "collection") .param("owningCollection", col2.getID().toString())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections." + @@ -1231,7 +1239,8 @@ public void createSingleWorkspaceItemFromBibtexFileWithMultipleAuthorsTest() thr try { // create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1) getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(bibtexFile)) + .file(bibtexFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0]" + @@ -1271,6 +1280,7 @@ public void createSingleWorkspaceItemFromBibtexFileWithMultipleAuthorsTest() thr try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") .file(bibtexFile) + .param("embed", "collection") .param("owningCollection", col2.getID().toString())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0]" + @@ -1336,7 +1346,8 @@ public void createSingleWorkspaceItemFromCSVWithOneEntryTest() throws Exception AtomicReference> idRef = new AtomicReference<>(); try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(csvFile)) + .file(csvFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1376,6 +1387,7 @@ public void createSingleWorkspaceItemFromCSVWithOneEntryTest() throws Exception try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") .file(csvFile) + .param("embed", "collection") .param("owningCollection", col2.getID().toString())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone" @@ -1453,7 +1465,8 @@ public void createSingleWorkspaceItemFromCSVWithOneEntryAndMissingDataTest() thr try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(csvFile)) + .file(csvFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1531,7 +1544,8 @@ public void createSingleWorkspaceItemFromTSVWithOneEntryTest() throws Exception // create workspaceitems in the default collection (col1) try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(tsvFile)) + .file(tsvFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1607,7 +1621,8 @@ public void createSingleWorkspaceItemFromRISWithOneEntryTest() throws Exception // create workspaceitems in the default collection (col1) try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(tsvFile)) + .file(tsvFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1684,7 +1699,8 @@ public void createSingleWorkspaceItemFromEndnoteWithOneEntryTest() throws Except // create workspaceitems in the default collection (col1) try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(endnoteFile)) + .file(endnoteFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1763,7 +1779,8 @@ public void createSingleWorkspaceItemFromTSVWithOneEntryAndMissingDataTest() thr // create workspaceitems in the default collection (col1) try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(csvFile)) + .file(csvFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1844,7 +1861,9 @@ public void createSingleWorkspaceItemFromMultipleFilesWithOneEntryTest() throws // create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1) try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") - .file(bibtexFile).file(pubmedFile)) + .file(bibtexFile) + .file(pubmedFile) + .param("embed", "collection")) // create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -1879,6 +1898,7 @@ public void createSingleWorkspaceItemFromMultipleFilesWithOneEntryTest() throws try { getClient(authToken).perform(multipart("/api/submission/workspaceitems") .file(bibtexFile).file(pubmedFile) + .param("embed", "collection") .param("owningCollection", col2.getID().toString())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", @@ -4323,6 +4343,7 @@ public void createWorkspaceItemFromExternalSources() throws Exception { String token = getAuthToken(admin.getEmail(), password); MvcResult mvcResult = getClient(token).perform(post("/api/submission/workspaceitems?owningCollection=" + col1.getID().toString()) + .param("embed", "item") .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) .content("https://localhost:8080/server/api/integration/" + "externalsources/mock/entryValues/one")) @@ -4333,7 +4354,8 @@ public void createWorkspaceItemFromExternalSources() throws Exception { workspaceItemId = (Integer) map.get("id"); String itemUuidString = String.valueOf(((Map) ((Map) map.get("_embedded")).get("item")).get("uuid")); - getClient(token).perform(get("/api/submission/workspaceitems/" + workspaceItemId)) + getClient(token).perform(get("/api/submission/workspaceitems/" + workspaceItemId) + .param("embed", "item")) .andExpect(status().isOk()) .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.id", is(workspaceItemId)), @@ -4528,6 +4550,7 @@ public void createWorkspaceItemFromExternalSourcesNonAdminWithPermission() throw String token = getAuthToken(eperson.getEmail(), password); getClient(token).perform(post("/api/submission/workspaceitems") + .param("embed", "collection") .param("owningCollection", col1.getID().toString()) .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) .content("https://localhost:8080/server/api/integration/externalsources/" + @@ -4537,7 +4560,9 @@ public void createWorkspaceItemFromExternalSourcesNonAdminWithPermission() throw .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); workspaceItemId = idRef.get(); - getClient(token).perform(get("/api/submission/workspaceitems/" + workspaceItemId)) + getClient(token).perform(get("/api/submission/workspaceitems/" + workspaceItemId) + .param("embed", "collection") + .param("embed", "item")) .andExpect(status().isOk()) .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.id", is(workspaceItemId)), From 75c688daef432348e916aca0335dbf5b368d8586 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:31:20 -0500 Subject: [PATCH 141/479] [Port dspace-7_x] Drop not null for EPerson in Process table (#9510) * Set EPerson nullable in ProcessTable (cherry picked from commit 9ed997d2ddcce046b6affc4f2f8edbe62f03cc51) * chore: Remove nullable = false in EPerson Entity (cherry picked from commit f1a38c2485715613115734f887567fdfcf412d2c) * chore: Avoid NPE when getEPerson is called (cherry picked from commit 2f9ad722986ae2997856e88ce125cbd1e8500037) * test: Add test for insert nullable EPerson in Process' table (cherry picked from commit 8fe264ee6930f278def5f54df964ea8c8f4a5b60) * Revert "Set EPerson nullable in ProcessTable" This reverts commit aea7b9385b6c6972b0ac27e061d082bb92eaf4a6. (cherry picked from commit 7de931c7be8ab885a20030115ad730fc40e73832) * feat: Drop NOT NULL for user_id in Process table (cherry picked from commit 3b73786c554e8fe768cedf04baa86ac6e447fd6e) * chore: add endline (cherry picked from commit 27ed14c38ca57cb6fcf5185c0517c3aef27fd589) * fix: add headers file (cherry picked from commit a3ea7cbaaa89fe34094f110071aef4acfa811d35) * Rename file sql (cherry picked from commit eb22285924752aec13ea6aa16fbba7bff5c54daf) * Rename sql script to 7.6.x (cherry picked from commit d2ef87eab6467524e73bd8c5edb5f97ed314a327) * fix: Set fetch lazy in EPerson Process (cherry picked from commit d6340403ffade379a4c2745b52c37b28940b063f) * fix: set the user attached to a process to null if none is found in the DB (cherry picked from commit afb734df5fa20f9e814397efa6ab7aef93b1a3b7) --------- Co-authored-by: Roy Bruschini Co-authored-by: Jens Vannerum --- .../src/main/java/org/dspace/scripts/Process.java | 4 ++-- .../org/dspace/scripts/ProcessServiceImpl.java | 15 +++++++++++---- ...6_2024.03.07__set_eperson_process_nullable.sql | 9 +++++++++ ...6_2024.03.07__set_eperson_process_nullable.sql | 9 +++++++++ .../test/java/org/dspace/process/ProcessIT.java | 11 +++++++++++ .../app/rest/converter/ProcessConverter.java | 7 ++++++- 6 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.03.07__set_eperson_process_nullable.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.03.07__set_eperson_process_nullable.sql diff --git a/dspace-api/src/main/java/org/dspace/scripts/Process.java b/dspace-api/src/main/java/org/dspace/scripts/Process.java index eab3ba460c09..9834c78c1551 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/Process.java +++ b/dspace-api/src/main/java/org/dspace/scripts/Process.java @@ -51,8 +51,8 @@ public class Process implements ReloadableEntity { @Column(name = "process_id", unique = true, nullable = false) private Integer processId; - @ManyToOne - @JoinColumn(name = "user_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") private EPerson ePerson; @Column(name = "start_time") diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index 2e14aeaa36c0..f242ec7acdad 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -92,10 +92,17 @@ public Process create(Context context, EPerson ePerson, String scriptName, }); Process createdProcess = processDAO.create(context, process); - log.info(LogHelper.getHeader(context, "process_create", - "Process has been created for eperson with email " + ePerson.getEmail() - + " with ID " + createdProcess.getID() + " and scriptName " + - scriptName + " and parameters " + parameters)); + + if (ePerson != null) { + log.info(LogHelper.getHeader(context, "process_create", + "Process has been created for eperson with email " + ePerson.getEmail() + + " with ID " + createdProcess.getID() + " and scriptName " + + scriptName + " and parameters " + parameters)); + } else { + log.info(LogHelper.getHeader(context, "process_create", + "Process has been created for command-line user with ID " + createdProcess.getID() + + " and scriptName " + scriptName + " and parameters " + parameters)); + } return createdProcess; } diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.03.07__set_eperson_process_nullable.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.03.07__set_eperson_process_nullable.sql new file mode 100644 index 000000000000..39dc1115be49 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.03.07__set_eperson_process_nullable.sql @@ -0,0 +1,9 @@ +-- +-- 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 ALTER COLUMN user_id DROP NOT NULL; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.03.07__set_eperson_process_nullable.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.03.07__set_eperson_process_nullable.sql new file mode 100644 index 000000000000..39dc1115be49 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.03.07__set_eperson_process_nullable.sql @@ -0,0 +1,9 @@ +-- +-- 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 ALTER COLUMN user_id DROP NOT NULL; diff --git a/dspace-api/src/test/java/org/dspace/process/ProcessIT.java b/dspace-api/src/test/java/org/dspace/process/ProcessIT.java index d6640652121c..6cf840ea6abe 100644 --- a/dspace-api/src/test/java/org/dspace/process/ProcessIT.java +++ b/dspace-api/src/test/java/org/dspace/process/ProcessIT.java @@ -9,6 +9,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.HashSet; import java.util.LinkedList; @@ -87,4 +88,14 @@ public void removeOneGroupTest() throws Exception { assertFalse(isPresent); } + + @Test + public void addProcessWithNullEPersonTest() throws Exception { + try { + ProcessBuilder.createProcess(context, null, "mock-script", new LinkedList<>(), + new HashSet<>()).build(); + } catch (NullPointerException e) { + fail("Should not have thrown NullPointerException"); + } + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java index de03d6063019..38eaf4204798 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java @@ -14,6 +14,7 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.scripts.Process; import org.dspace.scripts.service.ProcessService; +import org.hibernate.ObjectNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -39,7 +40,11 @@ public ProcessRest convert(Process process, Projection projection) { processRest.setId(process.getID()); processRest.setScriptName(process.getName()); processRest.setProcessId(process.getID()); - processRest.setUserId(process.getEPerson().getID()); + try { + processRest.setUserId(process.getEPerson().getID()); + } catch (ObjectNotFoundException e ) { + processRest.setUserId(null); + } processRest.setProcessStatus(process.getProcessStatus()); processRest.setStartTime(process.getStartTime()); processRest.setEndTime(process.getFinishedTime()); From 2f99646417117fc54eb0b2ed301af88ea306c71f Mon Sep 17 00:00:00 2001 From: haoueclf Date: Mon, 11 Dec 2023 13:42:50 +0100 Subject: [PATCH 142/479] [DS-3439] Copy collection template item specified metadata during Sword v2 METS deposit ingestion. (cherry picked from commit 7ead4ae7f005f79f8c6a9fe704d60511259bdaee) --- .../org/dspace/content/ItemServiceImpl.java | 50 +++++++++++++++++++ .../content/WorkspaceItemServiceImpl.java | 48 +----------------- .../packager/AbstractMETSIngester.java | 3 ++ .../dspace/content/service/ItemService.java | 12 +++++ 4 files changed, 66 insertions(+), 47 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 26a61877fe7f..285b3b6940ff 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.UUID; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -276,6 +277,55 @@ public Item createTemplateItem(Context context, Collection collection) throws SQ } } + @Override + public void populateWithTemplateItemMetadata(Context context, Collection collection, boolean template, Item item) + throws SQLException { + + Item templateItem = collection.getTemplateItem(); + + Optional colEntityType = getDSpaceEntityType(collection); + Optional templateItemEntityType = getDSpaceEntityType(templateItem); + + if (template && colEntityType.isPresent() && templateItemEntityType.isPresent() && + !StringUtils.equals(colEntityType.get().getValue(), templateItemEntityType.get().getValue())) { + throw new IllegalStateException("The template item has entity type : (" + + templateItemEntityType.get().getValue() + ") different than collection entity type : " + + colEntityType.get().getValue()); + } + + if (template && colEntityType.isPresent() && templateItemEntityType.isEmpty()) { + MetadataValue original = colEntityType.get(); + MetadataField metadataField = original.getMetadataField(); + MetadataSchema metadataSchema = metadataField.getMetadataSchema(); + // NOTE: dspace.entity.type = does not make sense + // the collection entity type is by default blank when a collection is first created + if (StringUtils.isNotBlank(original.getValue())) { + addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(), + metadataField.getQualifier(), original.getLanguage(), original.getValue()); + } + } + + if (template && (templateItem != null)) { + List md = getMetadata(templateItem, Item.ANY, Item.ANY, Item.ANY, Item.ANY); + + for (MetadataValue aMd : md) { + MetadataField metadataField = aMd.getMetadataField(); + MetadataSchema metadataSchema = metadataField.getMetadataSchema(); + addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(), + metadataField.getQualifier(), aMd.getLanguage(), aMd.getValue()); + } + } + } + + private Optional getDSpaceEntityType(DSpaceObject dSpaceObject) { + return Objects.nonNull(dSpaceObject) ? dSpaceObject.getMetadata() + .stream() + .filter(x -> x.getMetadataField().toString('.') + .equalsIgnoreCase("dspace.entity.type")) + .findFirst() + : Optional.empty(); + } + @Override public Iterator findAll(Context context) throws SQLException { return itemDAO.findAll(context, true); diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java index b78f6b9f7de8..1da9e6e44a6a 100644 --- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java @@ -12,11 +12,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.UUID; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.util.DCInputsReaderException; import org.dspace.app.util.Util; @@ -141,41 +138,7 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b .addPolicy(context, item, Constants.DELETE, item.getSubmitter(), ResourcePolicy.TYPE_SUBMISSION); // Copy template if appropriate - Item templateItem = collection.getTemplateItem(); - - Optional colEntityType = getDSpaceEntityType(collection); - Optional templateItemEntityType = getDSpaceEntityType(templateItem); - - if (template && colEntityType.isPresent() && templateItemEntityType.isPresent() && - !StringUtils.equals(colEntityType.get().getValue(), templateItemEntityType.get().getValue())) { - throw new IllegalStateException("The template item has entity type : (" + - templateItemEntityType.get().getValue() + ") different than collection entity type : " + - colEntityType.get().getValue()); - } - - if (template && colEntityType.isPresent() && templateItemEntityType.isEmpty()) { - MetadataValue original = colEntityType.get(); - MetadataField metadataField = original.getMetadataField(); - MetadataSchema metadataSchema = metadataField.getMetadataSchema(); - // NOTE: dspace.entity.type = does not make sense - // the collection entity type is by default blank when a collection is first created - if (StringUtils.isNotBlank(original.getValue())) { - itemService.addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(), - metadataField.getQualifier(), original.getLanguage(), original.getValue()); - } - } - - if (template && (templateItem != null)) { - List md = itemService.getMetadata(templateItem, Item.ANY, Item.ANY, Item.ANY, Item.ANY); - - for (MetadataValue aMd : md) { - MetadataField metadataField = aMd.getMetadataField(); - MetadataSchema metadataSchema = metadataField.getMetadataSchema(); - itemService.addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(), - metadataField.getQualifier(), aMd.getLanguage(), - aMd.getValue()); - } - } + itemService.populateWithTemplateItemMetadata(context, collection, template, item); itemService.update(context, item); @@ -213,15 +176,6 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b return workspaceItem; } - private Optional getDSpaceEntityType(DSpaceObject dSpaceObject) { - return Objects.nonNull(dSpaceObject) ? dSpaceObject.getMetadata() - .stream() - .filter(x -> x.getMetadataField().toString('.') - .equalsIgnoreCase("dspace.entity.type")) - .findFirst() - : Optional.empty(); - } - @Override public WorkspaceItem create(Context c, WorkflowItem workflowItem) throws SQLException, AuthorizeException { WorkspaceItem workspaceItem = workspaceItemDAO.create(c, new WorkspaceItem()); diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 98277c4f9c06..0ed0abe21825 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -636,6 +636,9 @@ protected DSpaceObject replaceObject(Context context, DSpaceObject dso, owningCollection = inProgressSubmission.getCollection(); } + itemService.populateWithTemplateItemMetadata(context, owningCollection, params.useCollectionTemplate(), + item); + addLicense(context, item, license, owningCollection , params); 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 de7644af83fe..12867ad18c3f 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 @@ -83,6 +83,18 @@ public interface ItemService */ public Item createTemplateItem(Context context, Collection collection) throws SQLException, AuthorizeException; + /** + * Populate the given item with all template item specified metadata. + * + * @param context DSpace context object + * @param collection Collection (parent) + * @param template if true, the item inherits all collection's template item metadata + * @param item item to populate with template item specified metadata + * @throws SQLException if database error + */ + public void populateWithTemplateItemMetadata (Context context, Collection collection, boolean template, Item item) + throws SQLException; + /** * Get all the items in the archive. Only items with the "in archive" flag * set are included. The order of the list is indeterminate. From 849a252fa85e79c7669648bcd317e18b8c66e8ad Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 24 Jan 2024 21:13:52 +0000 Subject: [PATCH 143/479] Update ContainerManagerDSpace.java Remove duplicate item.delete call that causes hibernate errors as item is already deleted (cherry picked from commit 57032050b97e264a2a875d4bae49f092e06df481) --- .../main/java/org/dspace/sword2/ContainerManagerDSpace.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java b/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java index 454afd80dc1c..81b48d22c081 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java @@ -760,9 +760,6 @@ protected void doContainerDelete(SwordContext swordContext, Item item, WorkflowItem wfi = wft.getWorkflowItem(context, item); workflowItemService.deleteWrapper(context, wfi); } - - // then delete the item - itemService.delete(context, item); } catch (SQLException | IOException e) { throw new DSpaceSwordException(e); } catch (AuthorizeException e) { From 435988ca00f00fa9c116792defe0925c05d65cd3 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 30 Apr 2024 16:40:47 +0200 Subject: [PATCH 144/479] Swap the delete and constraint db lines --- ...023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql | 4 ++-- ...023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql index 2e352e2ee7cf..61f361625dd9 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql @@ -6,7 +6,7 @@ -- http://www.dspace.org/license/ -- +DELETE FROM ResourcePolicy WHERE eperson_id is null and epersongroup_id is null; + ALTER TABLE ResourcePolicy ADD CONSTRAINT resourcepolicy_eperson_and_epersongroup_not_nullobject_chk CHECK (eperson_id is not null or epersongroup_id is not null) ; - -DELETE FROM ResourcePolicy WHERE eperson_id is null and epersongroup_id is null; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql index 2e352e2ee7cf..61f361625dd9 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.09.28__enforce_group_or_eperson_for_resourcepolicy.sql @@ -6,7 +6,7 @@ -- http://www.dspace.org/license/ -- +DELETE FROM ResourcePolicy WHERE eperson_id is null and epersongroup_id is null; + ALTER TABLE ResourcePolicy ADD CONSTRAINT resourcepolicy_eperson_and_epersongroup_not_nullobject_chk CHECK (eperson_id is not null or epersongroup_id is not null) ; - -DELETE FROM ResourcePolicy WHERE eperson_id is null and epersongroup_id is null; From 07377d12c8ba249bb1cf17c094e84c2f2f8eda30 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 30 Apr 2024 09:48:55 -0500 Subject: [PATCH 145/479] Add ITs which test SWORD deletion for workspace vs workflow items (we already have a test for archived items) (cherry picked from commit 70b0a2874964682ff5efbff9452351fae534f1f6) --- .../java/org/dspace/app/sword2/Swordv2IT.java | 137 +++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java index 64e3db7dfc1a..3ff79f78055c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java @@ -20,9 +20,13 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkflowItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.WorkspaceItem; import org.dspace.services.ConfigurationService; +import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.junit.Assume; import org.junit.Before; import org.junit.ClassRule; @@ -295,7 +299,7 @@ public void depositAndEditViaSwordTest() throws Exception { assertThat(response.getBody(), containsString(newTitle)); //---- - // STEP 5: Verify uploaded content can be DELETED via SWORDv2 (by an Admin ONLY) + // STEP 5: Verify archived Item can be DELETED via SWORDv2 (by an Admin ONLY) //---- // Edit URI should also allow user to DELETE the uploaded content // Since we submitted to a collection WITHOUT a workflow, this item is in archive. That means DELETE @@ -306,7 +310,6 @@ public void depositAndEditViaSwordTest() throws Exception { .headers(authHeaders) .build(); response = responseAsString(request); - // Expect a 204 No Content response assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); @@ -322,6 +325,136 @@ public void depositAndEditViaSwordTest() throws Exception { assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); } + @Test + public void deleteWorkspaceItemViaSwordTest() throws Exception { + context.turnOffAuthorisationSystem(); + // Create a top level community and one Collection + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test SWORDv2 Collection") + .withSubmitterGroup(eperson) + .build(); + + String titleOfItem = "This is a test SWORD workspace item"; + WorkspaceItem wsi = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withSubmitter(eperson) + .withTitle(titleOfItem) + .build(); + + // Above changes MUST be committed to the database for SWORDv2 to see them. + context.commit(); + context.restoreAuthSystemState(); + + // Edit link of WorkspaceItem is the Item UUID + String editLink = "/swordv2/edit/" + wsi.getItem().getID(); + + //---- + // STEP 1: Verify WorkspaceItem is found via SWORDv2 when logged in as the submitter + //---- + HttpHeaders authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + RequestEntity request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + ResponseEntity response = responseAsString(request); + assertEquals(HttpStatus.OK, response.getStatusCode()); + // Verify the new Item title is now included in the response body + assertThat(response.getBody(), containsString(titleOfItem)); + + //---- + // STEP 2: Verify WorkspaceItem can be deleted by submitter + //---- + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + request = RequestEntity.delete(editLink) + .headers(authHeaders) + .build(); + response = responseAsString(request); + // Expect a 204 No Content response + assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + + // Verify that Edit URI now returns a 404 (deleted successfully) + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + response = responseAsString(request); + // Expect a 404 response as content was deleted + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + + @Test + public void deleteWorkflowItemViaSwordTest() throws Exception { + context.turnOffAuthorisationSystem(); + // Create a top level community and one Collection + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + // Create a Collection with a workflow step enabled + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test SWORDv2 Workflow Collection") + .withSubmitterGroup(eperson) + .withWorkflowGroup(1, admin) + .build(); + + String titleOfItem = "This is a test SWORD workflow item"; + XmlWorkflowItem workflowItem = WorkflowItemBuilder.createWorkflowItem(context, collection) + .withSubmitter(eperson) + .withTitle(titleOfItem) + .withIssueDate("2017-10-17") + .build(); + // Above changes MUST be committed to the database for SWORDv2 to see them. + context.commit(); + context.restoreAuthSystemState(); + + // Edit link of WorkflowItem is the Item UUID + String editLink = "/swordv2/edit/" + workflowItem.getItem().getID(); + + //---- + // STEP 1: Verify WorkflowItem is found via SWORDv2 when logged in as the submitter + //---- + HttpHeaders authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + RequestEntity request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + ResponseEntity response = responseAsString(request); + assertEquals(HttpStatus.OK, response.getStatusCode()); + // Verify the new Item title is now included in the response body + assertThat(response.getBody(), containsString(titleOfItem)); + + //---- + // STEP 2: Verify WorkflowItem can be deleted by ADMIN only + //---- + // NOTE: Once Item is in Workflow, deletion requires ADMIN permissions + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(admin.getEmail(), password); + request = RequestEntity.delete(editLink) + .headers(authHeaders) + .build(); + response = responseAsString(request); + // Expect a 204 No Content response + assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + + // Verify that Edit URI now returns a 404 (deleted successfully) + authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + request = RequestEntity.get(editLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + response = responseAsString(request); + // Expect a 404 response as content was deleted + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + @Test public void editUnauthorizedTest() throws Exception { // Attempt to POST to /edit endpoint without sending authentication information From 67d7c0bf3bb646ba94f2d7196f7ed20a41d0e701 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 30 Apr 2024 10:30:12 -0500 Subject: [PATCH 146/479] Fix deletion by only cleaning up wrapper for Workspace/workflow items (cherry picked from commit 5c13569d0fc6709d43fd8824e875220b4ccad302) --- .../main/java/org/dspace/sword2/ContainerManagerDSpace.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java b/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java index 81b48d22c081..c0e8ef6bdc46 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java @@ -755,11 +755,14 @@ protected void doContainerDelete(SwordContext swordContext, Item item, WorkflowTools wft = new WorkflowTools(); if (wft.isItemInWorkspace(swordContext.getContext(), item)) { WorkspaceItem wsi = wft.getWorkspaceItem(context, item); - workspaceItemService.deleteAll(context, wsi); + workspaceItemService.deleteWrapper(context, wsi); } else if (wft.isItemInWorkflow(context, item)) { WorkflowItem wfi = wft.getWorkflowItem(context, item); workflowItemService.deleteWrapper(context, wfi); } + + // then delete the item + itemService.delete(context, item); } catch (SQLException | IOException e) { throw new DSpaceSwordException(e); } catch (AuthorizeException e) { From f9d3efd1e7efc7ac6f96d5ed48f6bc16b2f2da0d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 26 Jan 2024 09:58:57 +0100 Subject: [PATCH 147/479] added validation of page url (cherry picked from commit 771f37a1facf97d29850db835490b6c2bfa07adb) --- .../app/rest/repository/FeedbackRestRepository.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java index 8fd28bbe94b8..8188237114e0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java @@ -79,8 +79,14 @@ protected FeedbackRest createAndReturn(Context context) throws AuthorizeExceptio throw new DSpaceBadRequestException("e-mail and message fields are mandatory!"); } + String pageUrl = feedbackRest.getPage(); + String urlPrefix = configurationService.getProperty("dspace.ui.url"); + if (! StringUtils.startsWith(pageUrl, urlPrefix)) { + throw new DSpaceBadRequestException("unexpected page url was submitted"); + } + try { - feedbackService.sendEmail(context, req, recipientEmail, senderEmail, message, feedbackRest.getPage()); + feedbackService.sendEmail(context, req, recipientEmail, senderEmail, message, pageUrl); } catch (IOException | MessagingException e) { throw new RuntimeException(e.getMessage(), e); } @@ -100,4 +106,4 @@ public void setFeedbackService(FeedbackService feedbackService) { this.feedbackService = feedbackService; } -} \ No newline at end of file +} From 0cfe4aec73db1681c80cbc914898c3c050c3cc11 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 26 Jan 2024 10:40:48 +0100 Subject: [PATCH 148/479] allow empty feedback page url (cherry picked from commit c4cc56a4291106beb8b3250e4e4058d13ab962e2) --- .../org/dspace/app/rest/repository/FeedbackRestRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java index 8188237114e0..42672062175a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/FeedbackRestRepository.java @@ -81,7 +81,7 @@ protected FeedbackRest createAndReturn(Context context) throws AuthorizeExceptio String pageUrl = feedbackRest.getPage(); String urlPrefix = configurationService.getProperty("dspace.ui.url"); - if (! StringUtils.startsWith(pageUrl, urlPrefix)) { + if (StringUtils.isNotBlank(pageUrl) && ! StringUtils.startsWith(pageUrl, urlPrefix)) { throw new DSpaceBadRequestException("unexpected page url was submitted"); } From 30e3cde30d2b996a151626fd3068c482ef198772 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Mon, 29 Jan 2024 16:33:46 +0000 Subject: [PATCH 149/479] Update ItemImportServiceImpl.java expand setPermission to take the rpType string (cherry picked from commit 679df52bbdcb3f9b85da2d3128417f99b2133b80) --- .../org/dspace/app/itemimport/ItemImportServiceImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 255f4bdcbb15..c1357aff9eb5 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1743,7 +1743,8 @@ protected void processOptions(Context c, Item myItem, List options) } else { logInfo("\tSetting special permissions for " + bitstreamName); - setPermission(c, myGroup, actionID, bs); + String rpType = useWorkflow ? ResourcePolicy.TYPE_SUBMISSION : ResourcePolicy.TYPE_INHERITED; + setPermission(c, myGroup, rpType, actionID, bs); } } @@ -1801,13 +1802,14 @@ protected void processOptions(Context c, Item myItem, List options) * * @param c DSpace Context * @param g Dspace Group + * @param rpType resource policy type string * @param actionID action identifier * @param bs Bitstream * @throws SQLException if database error * @throws AuthorizeException if authorization error * @see org.dspace.core.Constants */ - protected void setPermission(Context c, Group g, int actionID, Bitstream bs) + protected void setPermission(Context c, Group g, String rpType, int actionID, Bitstream bs) throws SQLException, AuthorizeException { if (!isTest) { // remove the default policy @@ -1819,6 +1821,7 @@ protected void setPermission(Context c, Group g, int actionID, Bitstream bs) rp.setdSpaceObject(bs); rp.setAction(actionID); rp.setGroup(g); + rp.setRpType(rpType); resourcePolicyService.update(c, rp); } else { From a97d72f993ce6cb6edf2e3991707895fc4288626 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 1 May 2024 11:50:22 -0500 Subject: [PATCH 150/479] Minor update to latest Spring version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da252ad102c3..9f8f8f247024 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 11 - 5.3.33 + 5.3.34 2.7.18 5.7.11 5.6.15.Final From 735906c42921f1e79b7ee843b9d836049f051afb Mon Sep 17 00:00:00 2001 From: John Abrahams Date: Wed, 1 May 2024 11:54:44 -0400 Subject: [PATCH 151/479] Install unzip util in final built image (cherry picked from commit 3eae430beca18f18d1396e614f8aa1f5b85169a7) --- Dockerfile.cli | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile.cli b/Dockerfile.cli index 96a84bc56268..53040a2fad89 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -33,9 +33,9 @@ WORKDIR /dspace-src ENV ANT_VERSION 1.10.13 ENV ANT_HOME /tmp/ant-$ANT_VERSION ENV PATH $ANT_HOME/bin:$PATH -# Need wget to install ant, and unzip for managing AIPs +# Need wget to install ant RUN apt-get update \ - && apt-get install -y --no-install-recommends wget unzip \ + && apt-get install -y --no-install-recommends wget \ && apt-get purge -y --auto-remove \ && rm -rf /var/lib/apt/lists/* # Download and install 'ant' @@ -52,3 +52,8 @@ ENV DSPACE_INSTALL=/dspace COPY --from=ant_build /dspace $DSPACE_INSTALL # Give java extra memory (1GB) ENV JAVA_OPTS=-Xmx1000m +# Install unzip for AIPs +RUN apt-get update \ + && apt-get install -y --no-install-recommends unzip \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* From e1dc6951028b468e8676341ad1ccc28a95d0dd28 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 1 May 2024 11:37:05 -0500 Subject: [PATCH 152/479] Remove obsolete "version" tag from compose files (cherry picked from commit 9ca0ad5579348e4e092307e7688bda1c5c9a178f) --- docker-compose-cli.yml | 1 - docker-compose.yml | 1 - dspace/src/main/docker-compose/cli.assetstore.yml | 2 -- dspace/src/main/docker-compose/cli.ingest.yml | 2 -- dspace/src/main/docker-compose/db.entities.yml | 2 -- dspace/src/main/docker-compose/db.restore.yml | 2 -- dspace/src/main/docker-compose/docker-compose-angular.yml | 1 - dspace/src/main/docker-compose/docker-compose-iiif.yml | 1 - dspace/src/main/docker-compose/docker-compose-shibboleth.yml | 1 - 9 files changed, 13 deletions(-) diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index 4f4d4a189161..d6a194617e02 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -1,4 +1,3 @@ -version: "3.7" networks: # Default to using network named 'dspacenet' from docker-compose.yml. # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") diff --git a/docker-compose.yml b/docker-compose.yml index a7894135c6ab..23fce37db245 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.7' networks: dspacenet: ipam: diff --git a/dspace/src/main/docker-compose/cli.assetstore.yml b/dspace/src/main/docker-compose/cli.assetstore.yml index d6a6a4395f4e..6563aa081eb1 100644 --- a/dspace/src/main/docker-compose/cli.assetstore.yml +++ b/dspace/src/main/docker-compose/cli.assetstore.yml @@ -6,8 +6,6 @@ # http://www.dspace.org/license/ # -version: "3.7" - services: dspace-cli: environment: diff --git a/dspace/src/main/docker-compose/cli.ingest.yml b/dspace/src/main/docker-compose/cli.ingest.yml index d22a235d4fdd..6172147f1955 100644 --- a/dspace/src/main/docker-compose/cli.ingest.yml +++ b/dspace/src/main/docker-compose/cli.ingest.yml @@ -6,8 +6,6 @@ # http://www.dspace.org/license/ # -version: "3.7" - services: dspace-cli: environment: diff --git a/dspace/src/main/docker-compose/db.entities.yml b/dspace/src/main/docker-compose/db.entities.yml index 809cf52df0b6..3480c0df870d 100644 --- a/dspace/src/main/docker-compose/db.entities.yml +++ b/dspace/src/main/docker-compose/db.entities.yml @@ -6,8 +6,6 @@ # http://www.dspace.org/license/ # -version: "3.7" - services: dspacedb: image: dspace/dspace-postgres-pgcrypto:dspace-7_x-loadsql diff --git a/dspace/src/main/docker-compose/db.restore.yml b/dspace/src/main/docker-compose/db.restore.yml index fc2f30b9d8e0..09990e675619 100644 --- a/dspace/src/main/docker-compose/db.restore.yml +++ b/dspace/src/main/docker-compose/db.restore.yml @@ -6,8 +6,6 @@ # http://www.dspace.org/license/ # -version: "3.7" - # # Overrides the default "dspacedb" container behavior to load a local SQL file into PostgreSQL. # diff --git a/dspace/src/main/docker-compose/docker-compose-angular.yml b/dspace/src/main/docker-compose/docker-compose-angular.yml index 8f9bdf94b38b..c9b87c904f17 100644 --- a/dspace/src/main/docker-compose/docker-compose-angular.yml +++ b/dspace/src/main/docker-compose/docker-compose-angular.yml @@ -6,7 +6,6 @@ # http://www.dspace.org/license/ # -version: '3.7' networks: # Default to using network named 'dspacenet' from docker-compose.yml. # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") diff --git a/dspace/src/main/docker-compose/docker-compose-iiif.yml b/dspace/src/main/docker-compose/docker-compose-iiif.yml index d795192f13c4..6f5470e5d157 100644 --- a/dspace/src/main/docker-compose/docker-compose-iiif.yml +++ b/dspace/src/main/docker-compose/docker-compose-iiif.yml @@ -10,7 +10,6 @@ # Test environment for DSpace + Cantaloupe for IIIF support. See README for instructions. # This should NEVER be used in production scenarios. # -version: '3.7' networks: # Default to using network named 'dspacenet' from docker-compose.yml. # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") diff --git a/dspace/src/main/docker-compose/docker-compose-shibboleth.yml b/dspace/src/main/docker-compose/docker-compose-shibboleth.yml index 33eadcb142d7..f7fb2dcbd1ae 100644 --- a/dspace/src/main/docker-compose/docker-compose-shibboleth.yml +++ b/dspace/src/main/docker-compose/docker-compose-shibboleth.yml @@ -10,7 +10,6 @@ # Test environment for DSpace + Shibboleth (running via mod_shib in Apache). See README for instructions. # This should NEVER be used in production scenarios. # -version: '3.7' networks: # Default to using network named 'dspacenet' from docker-compose.yml. # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") From 08c46bd0a1dcbfd238f52b454c0b6eb33c97f651 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 16:41:54 +0000 Subject: [PATCH 153/479] Bump org.xmlunit:xmlunit-core from 2.9.1 to 2.10.0 in /dspace-api Bumps [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit) from 2.9.1 to 2.10.0. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.9.1...v2.10.0) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-core dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit 1fdc3ce220b8bb7e13410176537cf06122bb5ea7) --- 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 5b578fa49d5a..4c881cbd2465 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -900,7 +900,7 @@ org.xmlunit xmlunit-core - 2.9.1 + 2.10.0 test From 097d2530630fd862e6af13ed734374bdb1fb1046 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 22 Feb 2024 20:58:00 +0100 Subject: [PATCH 154/479] Make sure vocab init is run for vocabulary returned in /api/submission/vocabularies (cherry picked from commit 42437dd65be8400d96d1abf36d7abce58426b118) --- .../dspace/content/authority/DSpaceControlledVocabulary.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java index 16632ee5466b..94c9452356b0 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java @@ -224,6 +224,7 @@ public Choice getChoice(String authKey, String locale) { @Override public boolean isHierarchical() { + init(); return true; } @@ -256,6 +257,7 @@ public Choice getParentChoice(String authorityName, String childId, String local @Override public Integer getPreloadLevel() { + init(); return preloadLevel; } From 62b42ff2d1f13ebdca1efbb47f63f05796717405 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 21 Mar 2024 11:12:59 +0000 Subject: [PATCH 155/479] Update RoleDisseminator.java Fix NPE if empty password hash string (cherry picked from commit 04824a2e9826d03f83f77a36361f0f42e23b9ec0) --- .../main/java/org/dspace/content/packager/RoleDisseminator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java b/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java index f627779af8dc..15e9f1b14494 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java @@ -431,7 +431,7 @@ protected void writeEPerson(EPerson eperson, XMLStreamWriter writer, if (emitPassword) { PasswordHash password = ePersonService.getPasswordHash(eperson); - if (null != password) { + if (null != password && password.getHashString() != null) { writer.writeStartElement(PASSWORD_HASH); String algorithm = password.getAlgorithm(); From ce9129c961a65ef9027872e27946d656606227f9 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 3 May 2024 14:20:58 +0200 Subject: [PATCH 156/479] 105866: Remove commented code --- .../dspace/app/rest/converter/AInprogressItemConverter.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java index f40022f19f79..4cd3d2bfb2e7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java @@ -70,11 +70,6 @@ protected void fillFromModel(T obj, R witem, Projection projection) { submitter = obj.getSubmitter(); witem.setId(obj.getID()); -// witem.setCollection(collection != null ? converter.toRest(collection, projection) : null); -// witem.setItem(converter.toRest(item, projection)); -// if (submitter != null) { -// witem.setSubmitter(converter.toRest(submitter, projection)); -// } // 1. retrieve the submission definition // 2. iterate over the submission section to allow to plugin additional From 24f2bca4b3619691fb27f40d7869e79fb83bae78 Mon Sep 17 00:00:00 2001 From: Xiqinger <163422483+Xiqinger@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:42:47 +0800 Subject: [PATCH 157/479] check path before mkdirs (cherry picked from commit fda62bc10154e9e3e84fcadabd46f6915321c4c6) --- .../app/itemimport/ItemImportServiceImpl.java | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 6531b7d0c194..cd55e97a6356 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1959,58 +1959,57 @@ public String unzip(File zipfile, String destDir) throws IOException { try { while (entries.hasMoreElements()) { entry = entries.nextElement(); + String entryName = entry.getName(); + File outFile = new File(zipDir + entryName); + // Verify that this file/directory will be extracted into our zipDir (and not somewhere else!) + if (!outFile.toPath().normalize().startsWith(zipDir)) { + throw new IOException("Bad zip entry: '" + entryName + + "' in file '" + zipfile.getAbsolutePath() + "'!" + + " Cannot process this file or directory."); + } if (entry.isDirectory()) { - if (!new File(zipDir + entry.getName()).mkdirs()) { + if (!outFile.mkdirs()) { logError("Unable to create contents directory: " + zipDir + entry.getName()); } } else { - String entryName = entry.getName(); - File outFile = new File(zipDir + entryName); - // Verify that this file will be extracted into our zipDir (and not somewhere else!) - if (!outFile.toPath().normalize().startsWith(zipDir)) { - throw new IOException("Bad zip entry: '" + entryName - + "' in file '" + zipfile.getAbsolutePath() + "'!" - + " Cannot process this file."); - } else { - logInfo("Extracting file: " + entryName); + logInfo("Extracting file: " + entryName); - int index = entryName.lastIndexOf('/'); - if (index == -1) { - // Was it created on Windows instead? - index = entryName.lastIndexOf('\\'); + int index = entryName.lastIndexOf('/'); + if (index == -1) { + // Was it created on Windows instead? + index = entryName.lastIndexOf('\\'); + } + if (index > 0) { + File dir = new File(zipDir + entryName.substring(0, index)); + if (!dir.exists() && !dir.mkdirs()) { + logError("Unable to create directory: " + dir.getAbsolutePath()); } - if (index > 0) { - File dir = new File(zipDir + entryName.substring(0, index)); - if (!dir.exists() && !dir.mkdirs()) { - logError("Unable to create directory: " + dir.getAbsolutePath()); - } - //Entries could have too many directories, and we need to adjust the sourcedir - // file1.zip (SimpleArchiveFormat / item1 / contents|dublin_core|... - // SimpleArchiveFormat / item2 / contents|dublin_core|... - // or - // file2.zip (item1 / contents|dublin_core|... - // item2 / contents|dublin_core|... - - //regex supports either windows or *nix file paths - String[] entryChunks = entryName.split("/|\\\\"); - if (entryChunks.length > 2) { - if (StringUtils.equals(sourceDirForZip, sourcedir)) { - sourceDirForZip = sourcedir + "/" + entryChunks[0]; - } + //Entries could have too many directories, and we need to adjust the sourcedir + // file1.zip (SimpleArchiveFormat / item1 / contents|dublin_core|... + // SimpleArchiveFormat / item2 / contents|dublin_core|... + // or + // file2.zip (item1 / contents|dublin_core|... + // item2 / contents|dublin_core|... + + //regex supports either windows or *nix file paths + String[] entryChunks = entryName.split("/|\\\\"); + if (entryChunks.length > 2) { + if (StringUtils.equals(sourceDirForZip, sourcedir)) { + sourceDirForZip = sourcedir + "/" + entryChunks[0]; } } - byte[] buffer = new byte[1024]; - int len; - InputStream in = zf.getInputStream(entry); - BufferedOutputStream out = new BufferedOutputStream( - new FileOutputStream(outFile)); - while ((len = in.read(buffer)) >= 0) { - out.write(buffer, 0, len); - } - in.close(); - out.close(); } + byte[] buffer = new byte[1024]; + int len; + InputStream in = zf.getInputStream(entry); + BufferedOutputStream out = new BufferedOutputStream( + new FileOutputStream(outFile)); + while ((len = in.read(buffer)) >= 0) { + out.write(buffer, 0, len); + } + in.close(); + out.close(); } } } finally { From cbfe0beede1b969698db89b7fccca60412930946 Mon Sep 17 00:00:00 2001 From: Xiqinger <163422483+Xiqinger@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:54:00 +0800 Subject: [PATCH 158/479] Add empty check before call iterator().next() (cherry picked from commit b2663c810c2372c7ab841fc223d3ecd0d6823ba0) --- .../app/itemupdate/DeleteBitstreamsAction.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemupdate/DeleteBitstreamsAction.java b/dspace-api/src/main/java/org/dspace/app/itemupdate/DeleteBitstreamsAction.java index cb5dcfb75dc0..65157f5207e7 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemupdate/DeleteBitstreamsAction.java +++ b/dspace-api/src/main/java/org/dspace/app/itemupdate/DeleteBitstreamsAction.java @@ -70,16 +70,19 @@ public void execute(Context context, ItemArchive itarch, boolean isTest, } } - if (alterProvenance) { + if (alterProvenance && !bundles.isEmpty()) { DtoMetadata dtom = DtoMetadata.create("dc.description.provenance", "en", ""); String append = "Bitstream " + bs.getName() + " deleted on " + DCDate .getCurrent() + "; "; - Item item = bundles.iterator().next().getItems().iterator().next(); - ItemUpdate.pr("Append provenance with: " + append); + List items = bundles.iterator().next().getItems(); + if (!items.isEmpty()) { + Item item = items.iterator().next(); + ItemUpdate.pr("Append provenance with: " + append); - if (!isTest) { - MetadataUtilities.appendMetadata(context, item, dtom, false, append); + if (!isTest) { + MetadataUtilities.appendMetadata(context, item, dtom, false, append); + } } } } From 1920101273933a3b0310ae696c3a5ef369ce70e0 Mon Sep 17 00:00:00 2001 From: Xiqinger <163422483+Xiqinger@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:08:13 +0800 Subject: [PATCH 159/479] avoid outputStream write after close (cherry picked from commit 214568d480782a4792aebb916403930d61d13052) --- .../CreativeCommonsRDFStreamDisseminationCrosswalk.java | 1 - .../CreativeCommonsTextStreamDisseminationCrosswalk.java | 1 - .../content/crosswalk/LicenseStreamDisseminationCrosswalk.java | 1 - .../org/dspace/content/packager/AbstractMETSDisseminator.java | 1 + .../src/main/java/org/dspace/subscriptions/ContentGenerator.java | 1 + 5 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsRDFStreamDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsRDFStreamDisseminationCrosswalk.java index 9042a3a7f523..0528c0c20570 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsRDFStreamDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsRDFStreamDisseminationCrosswalk.java @@ -59,7 +59,6 @@ public void disseminate(Context context, DSpaceObject dso, OutputStream out) Bitstream cc = creativeCommonsService.getLicenseRdfBitstream((Item) dso); if (cc != null) { Utils.copy(bitstreamService.retrieve(context, cc), out); - out.close(); } } } diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsTextStreamDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsTextStreamDisseminationCrosswalk.java index edb9d60a623e..cd26b4d19cee 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsTextStreamDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/CreativeCommonsTextStreamDisseminationCrosswalk.java @@ -65,7 +65,6 @@ public void disseminate(Context context, DSpaceObject dso, OutputStream out) Bitstream cc = creativeCommonsService.getLicenseTextBitstream((Item) dso); if (cc != null) { Utils.copy(bitstreamService.retrieve(context, cc), out); - out.close(); } } } diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java index 75b884613db9..46858747870d 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java @@ -57,7 +57,6 @@ public void disseminate(Context context, DSpaceObject dso, OutputStream out) if (licenseBs != null) { Utils.copy(bitstreamService.retrieve(context, licenseBs), out); - out.close(); } } } diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java index 685fd9000da8..fd50ec8023e2 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java @@ -628,6 +628,7 @@ protected MdSec makeMdSec(Context context, DSpaceObject dso, Class mdSecClass, // Disseminate crosswalk output to an outputstream ByteArrayOutputStream disseminateOutput = new ByteArrayOutputStream(); sxwalk.disseminate(context, dso, disseminateOutput); + disseminateOutput.close(); // Convert output to an inputstream, so we can write to manifest or Zip file ByteArrayInputStream crosswalkedStream = new ByteArrayInputStream( disseminateOutput.toByteArray()); diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java index c3035614343b..e8f6996cbde8 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java @@ -89,6 +89,7 @@ private String generateBodyMail(Context context, List indexable .orElseGet(() -> entityType2Disseminator.get("Item")) .disseminate(context, item, out); } + out.close(); return out.toString(); } catch (Exception e) { log.error(e.getMessage(), e); From d6f6444f5891761626695b732721a2de8ae85e40 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Fri, 5 Apr 2024 14:22:23 -0500 Subject: [PATCH 160/479] updated query to get collections referenced in the subscribers table (cherry picked from commit c5989c65bbfa587c173a222e71966ec073a9222c) --- .../java/org/dspace/content/dao/impl/CollectionDAOImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java index c0ef6ea42fce..e07da9f44490 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java @@ -159,7 +159,8 @@ public List findAuthorizedByGroup(Context context, EPerson ePerson, @Override public List findCollectionsWithSubscribers(Context context) throws SQLException { - return list(createQuery(context, "SELECT DISTINCT col FROM Subscription s join s.collection col")); + return list(createQuery(context, "SELECT c FROM Collection c JOIN Subscription s ON c.id = " + + "s.dSpaceObject")); } @Override From 1da7c6b93317ae68203cc8c1c854ca85519e0588 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Thu, 11 Apr 2024 11:01:21 -0500 Subject: [PATCH 161/479] make sure collections list is unique (cherry picked from commit d27331a0b3ab4fba393f697e9cb48fc8b9760894) --- .../java/org/dspace/content/dao/impl/CollectionDAOImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java index e07da9f44490..befa1397a8ee 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java @@ -159,7 +159,7 @@ public List findAuthorizedByGroup(Context context, EPerson ePerson, @Override public List findCollectionsWithSubscribers(Context context) throws SQLException { - return list(createQuery(context, "SELECT c FROM Collection c JOIN Subscription s ON c.id = " + + return list(createQuery(context, "SELECT DISTINCT c FROM Collection c JOIN Subscription s ON c.id = " + "s.dSpaceObject")); } From 4d54f74ae139bd6c837648c67c9dca2dfad0acff Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 14:06:54 +0100 Subject: [PATCH 162/479] trim trailing whitespaces in search query (cherry picked from commit 43e2a1d402139f32b2e239172e5636b62798eae1) --- .../CrossRefImportMetadataSourceServiceImpl.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java index 71b088ff162b..f4cdb4e61602 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java @@ -192,8 +192,9 @@ private SearchByIdCallable(String id) { @Override public List call() throws Exception { List results = new ArrayList<>(); + URIBuilder uriBuilder = new URIBuilder(url); String ID = URLDecoder.decode(query.getParameterAsClass("id", String.class), "UTF-8"); - URIBuilder uriBuilder = new URIBuilder(url + "/" + ID); + uriBuilder.setPath(uriBuilder.getPath() + "/" + ID); Map> params = new HashMap>(); String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); JsonNode jsonNode = convertStringJsonToJsonNode(responseString); @@ -277,7 +278,7 @@ private class CountByQueryCallable implements Callable { private CountByQueryCallable(String queryString) { query = new Query(); - query.addParameter("query", queryString); + query.addParameter("query", StringUtils.trim(queryString)); } private CountByQueryCallable(Query query) { @@ -308,7 +309,7 @@ private class DoiCheckCallable implements Callable { private DoiCheckCallable(final String id) { final Query query = new Query(); - query.addParameter("id", id); + query.addParameter("id", StringUtils.trim(id)); this.query = query; } @@ -319,7 +320,8 @@ private DoiCheckCallable(final Query query) { @Override public Integer call() throws Exception { Map> params = new HashMap>(); - URIBuilder uriBuilder = new URIBuilder(url + "/" + query.getParameterAsClass("id", String.class)); + URIBuilder uriBuilder = new URIBuilder(url); + uriBuilder.setPath(uriBuilder.getPath() + "/" + query.getParameterAsClass("id", String.class)); String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); JsonNode jsonNode = convertStringJsonToJsonNode(responseString); return StringUtils.equals(jsonNode.at("/status").toString(), "ok") ? 1 : 0; From cef170ad121f82047ad61b7d4bf5910a04be6632 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 14:44:51 +0100 Subject: [PATCH 163/479] extend trimming (cherry picked from commit 2c6f43d48f4527f41e54d4c41fa8ddd232960bd0) --- .../crossref/CrossRefImportMetadataSourceServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java index f4cdb4e61602..419f6ca8a085 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java @@ -133,7 +133,7 @@ private class SearchByQueryCallable implements Callable> { private SearchByQueryCallable(String queryString, Integer maxResult, Integer start) { query = new Query(); - query.addParameter("query", queryString); + query.addParameter("query", StringUtils.trim(queryString)); query.addParameter("count", maxResult); query.addParameter("start", start); } @@ -186,7 +186,7 @@ private SearchByIdCallable(Query query) { private SearchByIdCallable(String id) { this.query = new Query(); - query.addParameter("id", id); + query.addParameter("id", StringUtils.trim(id)); } @Override From 1606862aa38cbf2a387dfeade1055b71fc0fc255 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 14:19:25 +0100 Subject: [PATCH 164/479] add processor to handle CrossRef abstracts (cherry picked from commit 498d1379701e670c276b3daf104d2069c965c9c0) --- dspace/config/spring/api/crossref-integration.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace/config/spring/api/crossref-integration.xml b/dspace/config/spring/api/crossref-integration.xml index d1e416d2b0c6..4786c44a7865 100644 --- a/dspace/config/spring/api/crossref-integration.xml +++ b/dspace/config/spring/api/crossref-integration.xml @@ -132,8 +132,11 @@ - + + + + From a000f1430a4a150c0ac46667ea60a97c5265f781 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 14:22:49 +0100 Subject: [PATCH 165/479] add CrossRefAbstractProcessor (cherry picked from commit fa3f10e0b632b0e6bcc344193cbefeead3edb0f7) --- .../crossref/CrossRefAbstractProcessor.java | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java new file mode 100644 index 000000000000..689c4e587028 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java @@ -0,0 +1,117 @@ +package org.dspace.importer.external.crossref; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; + +public class CrossRefAbstractProcessor implements JsonPathMetadataProcessor { + + private final static Logger log = LogManager.getLogger(); + + private String path; + + @Override + public Collection processMetadata(String json) { + JsonNode rootNode = convertStringJsonToJsonNode(json); + JsonNode abstractNode = rootNode.at(path); + Collection values = new ArrayList<>(); + if (!abstractNode.isMissingNode()) { + String abstractValue = abstractNode.textValue(); + if (StringUtils.isNotEmpty(abstractValue)) { + abstractValue = prettifyAbstract(abstractValue); + if (abstractValue != null) { + values.add(abstractValue); + } + } + } + return values; + } + + /** + * remove JATS markup from abstract + * + * @param abstractValue abstract with JATS markup + * @return abstract without JATS markup + */ + private String prettifyAbstract(String abstractValue) { + if (!abstractValue.contains(""; + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + Document xmlDoc; + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(xmlString)); + xmlDoc = builder.parse(is); + } catch (SAXException | IOException | ParserConfigurationException e) { + log.warn("unable to parse XML markup in CrossRef abstract field: " + e.getMessage()); + return null; + } + + StringBuilder sb = new StringBuilder(); + + NodeList rootElements = xmlDoc.getElementsByTagName("root"); + Node rootElement = rootElements.item(0); + NodeList childElements = rootElement.getChildNodes(); + for (int i = 0; i < childElements.getLength(); i++) { + Node childElement = childElements.item(i); + String nodeName = childElement.getNodeName(); + if (StringUtils.equals(nodeName, "jats:title")) { + if (! StringUtils.equals(childElement.getTextContent(), "Abstract")) { + sb.append(childElement.getTextContent()); + sb.append("\n"); + } + } + else if (StringUtils.equals(nodeName, "jats:sec")) { + NodeList secElements = childElement.getChildNodes(); + for (int j = 0; j < secElements.getLength(); j++) { + Node secChildElement = secElements.item(j); + sb.append(secChildElement.getTextContent()); + sb.append("\n"); + } + sb.append("\n"); + } + } + + return sb.toString().trim(); + } + + private JsonNode convertStringJsonToJsonNode(String json) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode body = null; + try { + body = mapper.readTree(json); + } catch (JsonProcessingException e) { + log.error("Unable to process json response.", e); + } + return body; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} From 6b57079569abddf8e7de7726fac850f5608925b7 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 14:52:07 +0100 Subject: [PATCH 166/479] add header in class file (cherry picked from commit b7720043974fbbadf104b37798fefaf619e58e5e) --- .../external/crossref/CrossRefAbstractProcessor.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java index 689c4e587028..b774e07fc86e 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.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 com.fasterxml.jackson.core.JsonProcessingException; From 71798f0f743353a7bcc1a8828a1b1b215e338e79 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 20:52:24 +0100 Subject: [PATCH 167/479] fix code style violations (cherry picked from commit 23b8b696a078000c06c56a9608defdaef36318c5) --- .../crossref/CrossRefAbstractProcessor.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java index b774e07fc86e..21c702aeabac 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java @@ -7,6 +7,14 @@ */ package org.dspace.importer.external.crossref; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -20,14 +28,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collection; - public class CrossRefAbstractProcessor implements JsonPathMetadataProcessor { private final static Logger log = LogManager.getLogger(); @@ -88,8 +88,7 @@ private String prettifyAbstract(String abstractValue) { sb.append(childElement.getTextContent()); sb.append("\n"); } - } - else if (StringUtils.equals(nodeName, "jats:sec")) { + } else if (StringUtils.equals(nodeName, "jats:sec")) { NodeList secElements = childElement.getChildNodes(); for (int j = 0; j < secElements.getLength(); j++) { Node secChildElement = secElements.item(j); From 263b45046cc8068221b668c64f3722576e823498 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Feb 2024 21:07:02 +0100 Subject: [PATCH 168/479] sort imports lexicographically (cherry picked from commit 2867b36ebb0aeb67f8fda70cc72bd9a7dc88a6c5) --- .../external/crossref/CrossRefAbstractProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java index 21c702aeabac..1b6da9d37b16 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java @@ -7,13 +7,13 @@ */ package org.dspace.importer.external.crossref; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; From e22f9a211f789d4cd218c70a78fc4dae2255bf15 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 6 May 2024 10:09:23 -0500 Subject: [PATCH 169/479] Upgrade Tika and bcprov-jdk18on to latest versions. Synced other dependencies with versions used on `main` (where possible to do so) --- pom.xml | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 9f8f8f247024..82718c0f3e9e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.7.11 5.6.15.Final 6.2.5.Final - 42.7.2 + 42.7.3 8.11.3 3.10.8 @@ -38,13 +38,13 @@ 1.1.1 9.4.54.v20240208 - 2.22.1 - 2.0.30 + 2.23.1 + 2.0.31 1.19.0 1.7.36 - 2.5.0 + 2.9.2 - 1.70 + 1.78.1 @@ -1295,13 +1295,19 @@ org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on ${bouncycastle.version} org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on + ${bouncycastle.version} + + + + org.bouncycastle + bcutil-jdk18on ${bouncycastle.version} @@ -1502,13 +1508,18 @@ commons-lang3 3.14.0 - + commons-logging commons-logging 1.3.0 + + org.apache.commons + commons-compress + 1.26.0 + org.apache.commons commons-pool2 From dbf063dfb9aff359f0ec153497f2a58b5d189258 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 6 May 2024 12:24:13 -0500 Subject: [PATCH 170/479] Ensure potentially large Solr query uses POST, not GET (cherry picked from commit 0a21a11c9a88f0b87b05ea2fe4ba619a15daaf10) --- .../src/main/java/org/dspace/content/EntityTypeServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java index 0e0c6d51e501..b5b066d9c36f 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java @@ -148,7 +148,7 @@ public List getSubmitAuthorizedTypes(Context context) sQuery.setFacetMinCount(1); sQuery.setFacetLimit(Integer.MAX_VALUE); sQuery.setFacetSort(FacetParams.FACET_SORT_INDEX); - QueryResponse qResp = solrSearchCore.getSolr().query(sQuery); + QueryResponse qResp = solrSearchCore.getSolr().query(sQuery, solrSearchCore.REQUEST_METHOD); FacetField facetField = qResp.getFacetField("search.entitytype"); if (Objects.nonNull(facetField)) { for (Count c : facetField.getValues()) { From 606b10ccb76ef7307164142adce5bcf59829ac9e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 7 May 2024 17:16:33 -0500 Subject: [PATCH 171/479] Add migration to set user_id to null if EPerson no longer exists & enforce it for future (cherry picked from commit ef24645cbf31ed1572f98669e883033d65fc6c4d) --- .../V7.6_2024.05.07__process_eperson_constraint.sql | 13 +++++++++++++ .../V7.6_2024.05.07__process_eperson_constraint.sql | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.05.07__process_eperson_constraint.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.05.07__process_eperson_constraint.sql diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.05.07__process_eperson_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.05.07__process_eperson_constraint.sql new file mode 100644 index 000000000000..26de16f466ee --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.05.07__process_eperson_constraint.sql @@ -0,0 +1,13 @@ +-- +-- 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/ +-- + +-- If Process references an EPerson that no longer exists, set the "user_id" to null. +UPDATE process SET user_id = null WHERE NOT EXISTS (SELECT * FROM EPerson where uuid = process.user_id); + +-- Add new constraint where process.user_id is nullified if referenced EPerson is deleted. +ALTER TABLE process ADD CONSTRAINT process_eperson FOREIGN KEY (user_id) REFERENCES EPerson(uuid) ON DELETE SET NULL; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.05.07__process_eperson_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.05.07__process_eperson_constraint.sql new file mode 100644 index 000000000000..26de16f466ee --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.05.07__process_eperson_constraint.sql @@ -0,0 +1,13 @@ +-- +-- 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/ +-- + +-- If Process references an EPerson that no longer exists, set the "user_id" to null. +UPDATE process SET user_id = null WHERE NOT EXISTS (SELECT * FROM EPerson where uuid = process.user_id); + +-- Add new constraint where process.user_id is nullified if referenced EPerson is deleted. +ALTER TABLE process ADD CONSTRAINT process_eperson FOREIGN KEY (user_id) REFERENCES EPerson(uuid) ON DELETE SET NULL; From 448a69483bf0bce10b58cfc3d3bdb37dda6d3e6c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 7 May 2024 17:17:00 -0500 Subject: [PATCH 172/479] Fix NullPointerException. Only return userid if EPerson not null (cherry picked from commit f34c592352bf64383721d489afcb6846000e290e) --- .../java/org/dspace/app/rest/converter/ProcessConverter.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java index 38eaf4204798..54231132d769 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java @@ -14,7 +14,6 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.scripts.Process; import org.dspace.scripts.service.ProcessService; -import org.hibernate.ObjectNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -40,10 +39,8 @@ public ProcessRest convert(Process process, Projection projection) { processRest.setId(process.getID()); processRest.setScriptName(process.getName()); processRest.setProcessId(process.getID()); - try { + if (process.getEPerson() != null) { processRest.setUserId(process.getEPerson().getID()); - } catch (ObjectNotFoundException e ) { - processRest.setUserId(null); } processRest.setProcessStatus(process.getProcessStatus()); processRest.setStartTime(process.getStartTime()); From 1b091a033e6ce2b043cd5482a97108d2f87316c7 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Tue, 12 Mar 2024 15:50:11 +0100 Subject: [PATCH 173/479] Fix OAIHarvester#extractHandle not handling config properly - defaults for oai.harvester.acceptedHandleServer and oai.harvester.rejectedHandlePrefix are now set - rejected handles no longer pass if multiple prefixes were configured for oai.harvester.rejectedHandlePrefix (cherry picked from commit 862a6c5be6f6abf7f91c50ce387fbc468fa29081) --- .../java/org/dspace/harvest/OAIHarvester.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java b/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java index 5aeb40bdd912..1b8d0002ce2c 100644 --- a/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java +++ b/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java @@ -16,6 +16,7 @@ import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -683,15 +684,11 @@ protected void processRecord(Element record, String OREPrefix, final long curren * @return null or the handle to be used. */ protected String extractHandle(Item item) { - String[] acceptedHandleServers = configurationService.getArrayProperty("oai.harvester.acceptedHandleServer"); - if (acceptedHandleServers == null) { - acceptedHandleServers = new String[] {"hdl.handle.net"}; - } + String[] acceptedHandleServers = configurationService + .getArrayProperty("oai.harvester.acceptedHandleServer", new String[] {"hdl.handle.net"}); - String[] rejectedHandlePrefixes = configurationService.getArrayProperty("oai.harvester.rejectedHandlePrefix"); - if (rejectedHandlePrefixes == null) { - rejectedHandlePrefixes = new String[] {"123456789"}; - } + String[] rejectedHandlePrefixes = configurationService + .getArrayProperty("oai.harvester.rejectedHandlePrefix", new String[] {"123456789"}); List values = itemService.getMetadata(item, "dc", "identifier", Item.ANY, Item.ANY); @@ -706,12 +703,9 @@ protected String extractHandle(Item item) { for (String server : acceptedHandleServers) { if (urlPieces[2].equals(server)) { - for (String prefix : rejectedHandlePrefixes) { - if (!urlPieces[3].equals(prefix)) { - return urlPieces[3] + "/" + urlPieces[4]; - } + if (Arrays.stream(rejectedHandlePrefixes).noneMatch(prefix -> prefix.equals(urlPieces[3]))) { + return urlPieces[3] + "/" + urlPieces[4]; } - } } } From 532e21ffb9072178b3e299b66327a425a3a4f06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Morin?= Date: Wed, 8 May 2024 15:22:56 -0400 Subject: [PATCH 174/479] Updated CheckStyle so that new Java features such as string blocks are supported, but without requiring to change anything else (cherry picked from commit ef8c621a0fdf0ced3c13d8a1b93522dee5b4f421) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 82718c0f3e9e..f7ce30b30df1 100644 --- a/pom.xml +++ b/pom.xml @@ -287,7 +287,7 @@ com.puppycrawl.tools checkstyle - 8.30 + 8.38 From 2fdc422b249993045e8b905a9f2ea0826a355b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Thu, 9 May 2024 14:39:51 +0100 Subject: [PATCH 175/479] change IT from organisation to organization --- .../java/org/dspace/app/rest/DiscoveryRestControllerIT.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 80d8ab2df422..c70b214731f0 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 @@ -1277,11 +1277,11 @@ public void checkSortOrderInPersonOrOrgunitConfigurationTest() throws Exception DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()), SortOptionMatcher.sortOptionMatcher("organization.legalName", DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()), - SortOptionMatcher.sortOptionMatcher("organisation.address.addressCountry", + SortOptionMatcher.sortOptionMatcher("organization.address.addressCountry", DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()), - SortOptionMatcher.sortOptionMatcher("organisation.address.addressLocality", + SortOptionMatcher.sortOptionMatcher("organization.address.addressLocality", DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()), - SortOptionMatcher.sortOptionMatcher("organisation.foundingDate", + SortOptionMatcher.sortOptionMatcher("organization.foundingDate", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()), SortOptionMatcher.sortOptionMatcher("dc.date.accessioned", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()), From 31020d1d4ff0d2af6baf9adb32b4bb2fef753b1e Mon Sep 17 00:00:00 2001 From: Roy Bruschini Date: Mon, 25 Mar 2024 16:52:55 +0100 Subject: [PATCH 176/479] Create templates mail and renamed name in getEmailFileName (cherry picked from commit 7a637e6871134a3d81cc9abbbe158090bdcec6f0) --- .../app/itemimport/ItemImportServiceImpl.java | 4 ++-- dspace/config/emails/batch_import_error | 19 +++++++++++++++++++ dspace/config/emails/batch_import_success | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 dspace/config/emails/batch_import_error create mode 100644 dspace/config/emails/batch_import_success diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index cd55e97a6356..1e219ee6314c 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -2234,7 +2234,7 @@ public void emailSuccessMessage(Context context, EPerson eperson, String fileName) throws MessagingException { try { Locale supportedLocale = I18nUtil.getEPersonLocale(eperson); - Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "bte_batch_import_success")); + Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "batch_import_success")); email.addRecipient(eperson.getEmail()); email.addArgument(fileName); @@ -2250,7 +2250,7 @@ public void emailErrorMessage(EPerson eperson, String error) logError("An error occurred during item import, the user will be notified. " + error); try { Locale supportedLocale = I18nUtil.getEPersonLocale(eperson); - Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "bte_batch_import_error")); + Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "batch_import_error")); email.addRecipient(eperson.getEmail()); email.addArgument(error); email.addArgument(configurationService.getProperty("dspace.ui.url") + "/feedback"); diff --git a/dspace/config/emails/batch_import_error b/dspace/config/emails/batch_import_error new file mode 100644 index 000000000000..2e2a2b742720 --- /dev/null +++ b/dspace/config/emails/batch_import_error @@ -0,0 +1,19 @@ +## Email sent to DSpace users when they batch import fails. +## +## Parameters: {0} the export error +## {1} the URL to the feedback page +## +## +## See org.dspace.core.Email for information on the format of this file. +## +#set($subject = 'DSpace - The batch import was not completed.') +The batch import you initiated from the DSpace UI was not completed, due to the following reason: + ${params[0]} + +For more information you may contact your system administrator: + ${params[1]} + + + +The DSpace Team + diff --git a/dspace/config/emails/batch_import_success b/dspace/config/emails/batch_import_success new file mode 100644 index 000000000000..7e9fdbf7416a --- /dev/null +++ b/dspace/config/emails/batch_import_success @@ -0,0 +1,16 @@ + +## Email sent to DSpace users when they successfully batch import items. +## +## Parameters: {0} the filepath to the mapfile created by the batch import +## +## +## See org.dspace.core.Email for information on the format of this file. +## +#set($subject = 'DSpace - Batch import successfully completed') +The batch item import you initiated from the DSpace UI has completed successfully. + +You may find the mapfile for the import in the following path: ${params[0]} + + +The DSpace Team + From d90cf24a21a47268e710392985994e9a385ac23a Mon Sep 17 00:00:00 2001 From: Roy Bruschini Date: Mon, 25 Mar 2024 16:55:43 +0100 Subject: [PATCH 177/479] Fix typo (cherry picked from commit 74c15e8d55c88e68bb7d7f716deffe5409922868) --- dspace/config/emails/batch_import_error | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/emails/batch_import_error b/dspace/config/emails/batch_import_error index 2e2a2b742720..2c62d72bf9c9 100644 --- a/dspace/config/emails/batch_import_error +++ b/dspace/config/emails/batch_import_error @@ -1,4 +1,4 @@ -## Email sent to DSpace users when they batch import fails. +## Email sent to DSpace users when their batch import fails. ## ## Parameters: {0} the export error ## {1} the URL to the feedback page From ca27ea5fc2b071b6bfe76ba6de6715e6577f237d Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Thu, 9 May 2024 17:42:21 +0200 Subject: [PATCH 178/479] Remove not unique id from inner beans to avoid xml validation issues (cherry picked from commit 844b73ff4903ef59b2a69f438b5bec207a63ead4) --- .../data/dspaceFolder/config/spring/api/item-filters.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/item-filters.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/item-filters.xml index 836d4f089677..4107202be566 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/item-filters.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/item-filters.xml @@ -295,8 +295,7 @@ - + @@ -352,8 +351,7 @@ - + From 2976ae397167a5969481c109894d6482b8e90f93 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 10 May 2024 16:41:09 +0200 Subject: [PATCH 179/479] bugfix: array index can be out of bound (cherry picked from commit 4fc6b07466a7b131fe61f62b8585087c3697afb5) --- .../crosswalk/SubscriptionDsoMetadataForEmailCompose.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/SubscriptionDsoMetadataForEmailCompose.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/SubscriptionDsoMetadataForEmailCompose.java index 05fda2b97475..ad92018b2220 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/SubscriptionDsoMetadataForEmailCompose.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/SubscriptionDsoMetadataForEmailCompose.java @@ -49,7 +49,7 @@ public void disseminate(Context context, DSpaceObject dso, OutputStream out) thr for (String actualMetadata : metadata) { String[] splitted = actualMetadata.split("\\."); String qualifier = null; - if (splitted.length == 1) { + if (splitted.length == 3) { qualifier = splitted[2]; } var metadataValue = itemService.getMetadataFirstValue(item, splitted[0], splitted[1], qualifier, ANY); From d14f1130241fc312d94f9df1cedc98bf32e207ad Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Thu, 7 Mar 2024 15:31:03 +0100 Subject: [PATCH 180/479] use datacite api response for recordscount and paginated calls. update test file for response as expected from the api. (cherry picked from commit 65ac01a4a655176351b0a74a8daf5b701a418d35) --- ...taCiteImportMetadataSourceServiceImpl.java | 39 ++++++++++++++++++- .../org/dspace/app/rest/dataCite-test.json | 2 +- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java index a11f2bc2471d..298c30ec39a5 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java @@ -73,8 +73,32 @@ public ImportRecord getRecord(String recordId) throws MetadataSourceException { @Override public int getRecordsCount(String query) throws MetadataSourceException { - Collection records = getRecords(query, 0, -1); - return records == null ? 0 : records.size(); + String id = getID(query); + Map> params = new HashMap<>(); + Map uriParameters = new HashMap<>(); + params.put("uriParameters", uriParameters); + if (StringUtils.isBlank(id)) { + id = query; + } + uriParameters.put("query", id); + uriParameters.put("page[size]", "1"); + int timeoutMs = configurationService.getIntProperty("datacite.timeout", 180000); + String url = configurationService.getProperty("datacite.url", "https://api.datacite.org/dois/"); + String responseString = liveImportClient.executeHttpGetRequest(timeoutMs, url, params); + JsonNode jsonNode = convertStringJsonToJsonNode(responseString); + if (jsonNode == null) { + log.warn("DataCite returned invalid JSON"); + throw new MetadataSourceException("Could not read datacite source"); + } + JsonNode dataNode = jsonNode.at("/meta/total"); + if (dataNode != null) { + try { + return Integer.valueOf(dataNode.toString()); + } catch (Exception e) { + log.debug("Could not read integer value" + dataNode.toString()); + } + } + return 0; } @Override @@ -95,6 +119,17 @@ public Collection getRecords(String query, int start, int count) t id = query; } uriParameters.put("query", id); + // start = current dspace page / datacite page number starting with 1 + // dspace rounds up/down to the next configured pagination settings. + if (start > 0 && count > 0) { + uriParameters.put("page[number]", Integer.toString((start / count) + 1)); + } + + // count = dspace page size / default datacite page size is currently 25 https://support.datacite.org/docs/pagination + if (count > 0) { + uriParameters.put("page[size]", Integer.toString(count)); + } + int timeoutMs = configurationService.getIntProperty("datacite.timeout", 180000); String url = configurationService.getProperty("datacite.url", "https://api.datacite.org/dois/"); String responseString = liveImportClient.executeHttpGetRequest(timeoutMs, url, params); diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-test.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-test.json index 8ede6f29a08e..1b7f63cd89c0 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-test.json +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-test.json @@ -1 +1 @@ -{"data":{"id":"10.48550/arxiv.2207.04779","type":"dois","attributes":{"doi":"10.48550/arxiv.2207.04779","prefix":"10.48550","suffix":"arxiv.2207.04779","identifiers":[{"identifier":"2207.04779","identifierType":"arXiv"}],"alternateIdentifiers":[{"alternateIdentifierType":"arXiv","alternateIdentifier":"2207.04779"}],"creators":[{"name":"Bayer, Jonas","nameType":"Personal","givenName":"Jonas","familyName":"Bayer","affiliation":[],"nameIdentifiers":[]},{"name":"Benzmüller, Christoph","nameType":"Personal","givenName":"Christoph","familyName":"Benzmüller","affiliation":[],"nameIdentifiers":[]},{"name":"Buzzard, Kevin","nameType":"Personal","givenName":"Kevin","familyName":"Buzzard","affiliation":[],"nameIdentifiers":[]},{"name":"David, Marco","nameType":"Personal","givenName":"Marco","familyName":"David","affiliation":[],"nameIdentifiers":[]},{"name":"Lamport, Leslie","nameType":"Personal","givenName":"Leslie","familyName":"Lamport","affiliation":[],"nameIdentifiers":[]},{"name":"Matiyasevich, Yuri","nameType":"Personal","givenName":"Yuri","familyName":"Matiyasevich","affiliation":[],"nameIdentifiers":[]},{"name":"Paulson, Lawrence","nameType":"Personal","givenName":"Lawrence","familyName":"Paulson","affiliation":[],"nameIdentifiers":[]},{"name":"Schleicher, Dierk","nameType":"Personal","givenName":"Dierk","familyName":"Schleicher","affiliation":[],"nameIdentifiers":[]},{"name":"Stock, Benedikt","nameType":"Personal","givenName":"Benedikt","familyName":"Stock","affiliation":[],"nameIdentifiers":[]},{"name":"Zelmanov, Efim","nameType":"Personal","givenName":"Efim","familyName":"Zelmanov","affiliation":[],"nameIdentifiers":[]}],"titles":[{"title":"Mathematical Proof Between Generations"}],"publisher":"arXiv","container":{},"publicationYear":2022,"subjects":[{"lang":"en","subject":"History and Overview (math.HO)","subjectScheme":"arXiv"},{"lang":"en","subject":"Logic in Computer Science (cs.LO)","subjectScheme":"arXiv"},{"subject":"FOS: Mathematics","subjectScheme":"Fields of Science and Technology (FOS)"},{"subject":"FOS: Mathematics","schemeUri":"http://www.oecd.org/science/inno/38235147.pdf","subjectScheme":"Fields of Science and Technology (FOS)"},{"subject":"FOS: Computer and information sciences","subjectScheme":"Fields of Science and Technology (FOS)"},{"subject":"FOS: Computer and information sciences","schemeUri":"http://www.oecd.org/science/inno/38235147.pdf","subjectScheme":"Fields of Science and Technology (FOS)"}],"contributors":[],"dates":[{"date":"2022-07-08T14:42:33Z","dateType":"Submitted","dateInformation":"v1"},{"date":"2022-07-13T00:14:24Z","dateType":"Updated","dateInformation":"v1"},{"date":"2022-07","dateType":"Available","dateInformation":"v1"},{"date":"2022","dateType":"Issued"}],"language":null,"types":{"ris":"GEN","bibtex":"misc","citeproc":"article","schemaOrg":"CreativeWork","resourceType":"Article","resourceTypeGeneral":"Preprint"},"relatedIdentifiers":[],"relatedItems":[],"sizes":[],"formats":[],"version":"1","rightsList":[{"rights":"arXiv.org perpetual, non-exclusive license","rightsUri":"http://arxiv.org/licenses/nonexclusive-distrib/1.0/"}],"descriptions":[{"description":"A proof is one of the most important concepts of mathematics. However, there is a striking difference between how a proof is defined in theory and how it is used in practice. This puts the unique status of mathematics as exact science into peril. Now may be the time to reconcile theory and practice, i.e. precision and intuition, through the advent of computer proof assistants. For the most time this has been a topic for experts in specialized communities. However, mathematical proofs have become increasingly sophisticated, stretching the boundaries of what is humanly comprehensible, so that leading mathematicians have asked for formal verification of their proofs. At the same time, major theorems in mathematics have recently been computer-verified by people from outside of these communities, even by beginning students. This article investigates the gap between the different definitions of a proof and possibilities to build bridges. It is written as a polemic or a collage by different members of the communities in mathematics and computer science at different stages of their careers, challenging well-known preconceptions and exploring new perspectives.","descriptionType":"Abstract"},{"description":"17 pages, 1 figure","descriptionType":"Other"}],"geoLocations":[],"fundingReferences":[],"xml":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHJlc291cmNlIHhtbG5zPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtNCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtNCBodHRwOi8vc2NoZW1hLmRhdGFjaXRlLm9yZy9tZXRhL2tlcm5lbC00LjMvbWV0YWRhdGEueHNkIj4KICA8aWRlbnRpZmllciBpZGVudGlmaWVyVHlwZT0iRE9JIj4xMC40ODU1MC9BUlhJVi4yMjA3LjA0Nzc5PC9pZGVudGlmaWVyPgogIDxhbHRlcm5hdGVJZGVudGlmaWVycz4KICAgIDxhbHRlcm5hdGVJZGVudGlmaWVyIGFsdGVybmF0ZUlkZW50aWZpZXJUeXBlPSJhclhpdiI+MjIwNy4wNDc3OTwvYWx0ZXJuYXRlSWRlbnRpZmllcj4KICA8L2FsdGVybmF0ZUlkZW50aWZpZXJzPgogIDxjcmVhdG9ycz4KICAgIDxjcmVhdG9yPgogICAgICA8Y3JlYXRvck5hbWUgbmFtZVR5cGU9IlBlcnNvbmFsIj5CYXllciwgSm9uYXM8L2NyZWF0b3JOYW1lPgogICAgICA8Z2l2ZW5OYW1lPkpvbmFzPC9naXZlbk5hbWU+CiAgICAgIDxmYW1pbHlOYW1lPkJheWVyPC9mYW1pbHlOYW1lPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZSBuYW1lVHlwZT0iUGVyc29uYWwiPkJlbnptw7xsbGVyLCBDaHJpc3RvcGg8L2NyZWF0b3JOYW1lPgogICAgICA8Z2l2ZW5OYW1lPkNocmlzdG9waDwvZ2l2ZW5OYW1lPgogICAgICA8ZmFtaWx5TmFtZT5CZW56bcO8bGxlcjwvZmFtaWx5TmFtZT4KICAgIDwvY3JlYXRvcj4KICAgIDxjcmVhdG9yPgogICAgICA8Y3JlYXRvck5hbWUgbmFtZVR5cGU9IlBlcnNvbmFsIj5CdXp6YXJkLCBLZXZpbjwvY3JlYXRvck5hbWU+CiAgICAgIDxnaXZlbk5hbWU+S2V2aW48L2dpdmVuTmFtZT4KICAgICAgPGZhbWlseU5hbWU+QnV6emFyZDwvZmFtaWx5TmFtZT4KICAgIDwvY3JlYXRvcj4KICAgIDxjcmVhdG9yPgogICAgICA8Y3JlYXRvck5hbWUgbmFtZVR5cGU9IlBlcnNvbmFsIj5EYXZpZCwgTWFyY288L2NyZWF0b3JOYW1lPgogICAgICA8Z2l2ZW5OYW1lPk1hcmNvPC9naXZlbk5hbWU+CiAgICAgIDxmYW1pbHlOYW1lPkRhdmlkPC9mYW1pbHlOYW1lPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZSBuYW1lVHlwZT0iUGVyc29uYWwiPkxhbXBvcnQsIExlc2xpZTwvY3JlYXRvck5hbWU+CiAgICAgIDxnaXZlbk5hbWU+TGVzbGllPC9naXZlbk5hbWU+CiAgICAgIDxmYW1pbHlOYW1lPkxhbXBvcnQ8L2ZhbWlseU5hbWU+CiAgICA8L2NyZWF0b3I+CiAgICA8Y3JlYXRvcj4KICAgICAgPGNyZWF0b3JOYW1lIG5hbWVUeXBlPSJQZXJzb25hbCI+TWF0aXlhc2V2aWNoLCBZdXJpPC9jcmVhdG9yTmFtZT4KICAgICAgPGdpdmVuTmFtZT5ZdXJpPC9naXZlbk5hbWU+CiAgICAgIDxmYW1pbHlOYW1lPk1hdGl5YXNldmljaDwvZmFtaWx5TmFtZT4KICAgIDwvY3JlYXRvcj4KICAgIDxjcmVhdG9yPgogICAgICA8Y3JlYXRvck5hbWUgbmFtZVR5cGU9IlBlcnNvbmFsIj5QYXVsc29uLCBMYXdyZW5jZTwvY3JlYXRvck5hbWU+CiAgICAgIDxnaXZlbk5hbWU+TGF3cmVuY2U8L2dpdmVuTmFtZT4KICAgICAgPGZhbWlseU5hbWU+UGF1bHNvbjwvZmFtaWx5TmFtZT4KICAgIDwvY3JlYXRvcj4KICAgIDxjcmVhdG9yPgogICAgICA8Y3JlYXRvck5hbWUgbmFtZVR5cGU9IlBlcnNvbmFsIj5TY2hsZWljaGVyLCBEaWVyazwvY3JlYXRvck5hbWU+CiAgICAgIDxnaXZlbk5hbWU+RGllcms8L2dpdmVuTmFtZT4KICAgICAgPGZhbWlseU5hbWU+U2NobGVpY2hlcjwvZmFtaWx5TmFtZT4KICAgIDwvY3JlYXRvcj4KICAgIDxjcmVhdG9yPgogICAgICA8Y3JlYXRvck5hbWUgbmFtZVR5cGU9IlBlcnNvbmFsIj5TdG9jaywgQmVuZWRpa3Q8L2NyZWF0b3JOYW1lPgogICAgICA8Z2l2ZW5OYW1lPkJlbmVkaWt0PC9naXZlbk5hbWU+CiAgICAgIDxmYW1pbHlOYW1lPlN0b2NrPC9mYW1pbHlOYW1lPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZSBuYW1lVHlwZT0iUGVyc29uYWwiPlplbG1hbm92LCBFZmltPC9jcmVhdG9yTmFtZT4KICAgICAgPGdpdmVuTmFtZT5FZmltPC9naXZlbk5hbWU+CiAgICAgIDxmYW1pbHlOYW1lPlplbG1hbm92PC9mYW1pbHlOYW1lPgogICAgPC9jcmVhdG9yPgogIDwvY3JlYXRvcnM+CiAgPHRpdGxlcz4KICAgIDx0aXRsZT5NYXRoZW1hdGljYWwgUHJvb2YgQmV0d2VlbiBHZW5lcmF0aW9uczwvdGl0bGU+CiAgPC90aXRsZXM+CiAgPHB1Ymxpc2hlcj5hclhpdjwvcHVibGlzaGVyPgogIDxwdWJsaWNhdGlvblllYXI+MjAyMjwvcHVibGljYXRpb25ZZWFyPgogIDxzdWJqZWN0cz4KICAgIDxzdWJqZWN0IHhtbDpsYW5nPSJlbiIgc3ViamVjdFNjaGVtZT0iYXJYaXYiPkhpc3RvcnkgYW5kIE92ZXJ2aWV3IChtYXRoLkhPKTwvc3ViamVjdD4KICAgIDxzdWJqZWN0IHhtbDpsYW5nPSJlbiIgc3ViamVjdFNjaGVtZT0iYXJYaXYiPkxvZ2ljIGluIENvbXB1dGVyIFNjaWVuY2UgKGNzLkxPKTwvc3ViamVjdD4KICAgIDxzdWJqZWN0IHN1YmplY3RTY2hlbWU9IkZpZWxkcyBvZiBTY2llbmNlIGFuZCBUZWNobm9sb2d5IChGT1MpIj5GT1M6IE1hdGhlbWF0aWNzPC9zdWJqZWN0PgogICAgPHN1YmplY3Qgc3ViamVjdFNjaGVtZT0iRmllbGRzIG9mIFNjaWVuY2UgYW5kIFRlY2hub2xvZ3kgKEZPUykiPkZPUzogQ29tcHV0ZXIgYW5kIGluZm9ybWF0aW9uIHNjaWVuY2VzPC9zdWJqZWN0PgogIDwvc3ViamVjdHM+CiAgPGRhdGVzPgogICAgPGRhdGUgZGF0ZVR5cGU9IlN1Ym1pdHRlZCIgZGF0ZUluZm9ybWF0aW9uPSJ2MSI+MjAyMi0wNy0wOFQxNDo0MjozM1o8L2RhdGU+CiAgICA8ZGF0ZSBkYXRlVHlwZT0iVXBkYXRlZCIgZGF0ZUluZm9ybWF0aW9uPSJ2MSI+MjAyMi0wNy0xM1QwMDoxNDoyNFo8L2RhdGU+CiAgICA8ZGF0ZSBkYXRlVHlwZT0iQXZhaWxhYmxlIiBkYXRlSW5mb3JtYXRpb249InYxIj4yMDIyLTA3PC9kYXRlPgogIDwvZGF0ZXM+CiAgPHJlc291cmNlVHlwZSByZXNvdXJjZVR5cGVHZW5lcmFsPSJQcmVwcmludCI+QXJ0aWNsZTwvcmVzb3VyY2VUeXBlPgogIDx2ZXJzaW9uPjE8L3ZlcnNpb24+CiAgPHJpZ2h0c0xpc3Q+CiAgICA8cmlnaHRzIHJpZ2h0c1VSST0iaHR0cDovL2FyeGl2Lm9yZy9saWNlbnNlcy9ub25leGNsdXNpdmUtZGlzdHJpYi8xLjAvIj5hclhpdi5vcmcgcGVycGV0dWFsLCBub24tZXhjbHVzaXZlIGxpY2Vuc2U8L3JpZ2h0cz4KICA8L3JpZ2h0c0xpc3Q+CiAgPGRlc2NyaXB0aW9ucz4KICAgIDxkZXNjcmlwdGlvbiBkZXNjcmlwdGlvblR5cGU9IkFic3RyYWN0Ij5BIHByb29mIGlzIG9uZSBvZiB0aGUgbW9zdCBpbXBvcnRhbnQgY29uY2VwdHMgb2YgbWF0aGVtYXRpY3MuIEhvd2V2ZXIsIHRoZXJlIGlzIGEgc3RyaWtpbmcgZGlmZmVyZW5jZSBiZXR3ZWVuIGhvdyBhIHByb29mIGlzIGRlZmluZWQgaW4gdGhlb3J5IGFuZCBob3cgaXQgaXMgdXNlZCBpbiBwcmFjdGljZS4gVGhpcyBwdXRzIHRoZSB1bmlxdWUgc3RhdHVzIG9mIG1hdGhlbWF0aWNzIGFzIGV4YWN0IHNjaWVuY2UgaW50byBwZXJpbC4gTm93IG1heSBiZSB0aGUgdGltZSB0byByZWNvbmNpbGUgdGhlb3J5IGFuZCBwcmFjdGljZSwgaS5lLiBwcmVjaXNpb24gYW5kIGludHVpdGlvbiwgdGhyb3VnaCB0aGUgYWR2ZW50IG9mIGNvbXB1dGVyIHByb29mIGFzc2lzdGFudHMuIEZvciB0aGUgbW9zdCB0aW1lIHRoaXMgaGFzIGJlZW4gYSB0b3BpYyBmb3IgZXhwZXJ0cyBpbiBzcGVjaWFsaXplZCBjb21tdW5pdGllcy4gSG93ZXZlciwgbWF0aGVtYXRpY2FsIHByb29mcyBoYXZlIGJlY29tZSBpbmNyZWFzaW5nbHkgc29waGlzdGljYXRlZCwgc3RyZXRjaGluZyB0aGUgYm91bmRhcmllcyBvZiB3aGF0IGlzIGh1bWFubHkgY29tcHJlaGVuc2libGUsIHNvIHRoYXQgbGVhZGluZyBtYXRoZW1hdGljaWFucyBoYXZlIGFza2VkIGZvciBmb3JtYWwgdmVyaWZpY2F0aW9uIG9mIHRoZWlyIHByb29mcy4gQXQgdGhlIHNhbWUgdGltZSwgbWFqb3IgdGhlb3JlbXMgaW4gbWF0aGVtYXRpY3MgaGF2ZSByZWNlbnRseSBiZWVuIGNvbXB1dGVyLXZlcmlmaWVkIGJ5IHBlb3BsZSBmcm9tIG91dHNpZGUgb2YgdGhlc2UgY29tbXVuaXRpZXMsIGV2ZW4gYnkgYmVnaW5uaW5nIHN0dWRlbnRzLiBUaGlzIGFydGljbGUgaW52ZXN0aWdhdGVzIHRoZSBnYXAgYmV0d2VlbiB0aGUgZGlmZmVyZW50IGRlZmluaXRpb25zIG9mIGEgcHJvb2YgYW5kIHBvc3NpYmlsaXRpZXMgdG8gYnVpbGQgYnJpZGdlcy4gSXQgaXMgd3JpdHRlbiBhcyBhIHBvbGVtaWMgb3IgYSBjb2xsYWdlIGJ5IGRpZmZlcmVudCBtZW1iZXJzIG9mIHRoZSBjb21tdW5pdGllcyBpbiBtYXRoZW1hdGljcyBhbmQgY29tcHV0ZXIgc2NpZW5jZSBhdCBkaWZmZXJlbnQgc3RhZ2VzIG9mIHRoZWlyIGNhcmVlcnMsIGNoYWxsZW5naW5nIHdlbGwta25vd24gcHJlY29uY2VwdGlvbnMgYW5kIGV4cGxvcmluZyBuZXcgcGVyc3BlY3RpdmVzLjwvZGVzY3JpcHRpb24+CiAgICA8ZGVzY3JpcHRpb24gZGVzY3JpcHRpb25UeXBlPSJPdGhlciI+MTcgcGFnZXMsIDEgZmlndXJlPC9kZXNjcmlwdGlvbj4KICA8L2Rlc2NyaXB0aW9ucz4KPC9yZXNvdXJjZT4=","url":"https://arxiv.org/abs/2207.04779","contentUrl":null,"metadataVersion":1,"schemaVersion":"http://datacite.org/schema/kernel-4","source":"mds","isActive":true,"state":"findable","reason":null,"viewCount":0,"viewsOverTime":[],"downloadCount":0,"downloadsOverTime":[],"referenceCount":0,"citationCount":0,"citationsOverTime":[],"partCount":0,"partOfCount":0,"versionCount":0,"versionOfCount":0,"created":"2022-07-12T01:41:56.000Z","registered":"2022-07-12T01:41:57.000Z","published":"2022","updated":"2022-07-13T01:24:20.000Z"},"relationships":{"client":{"data":{"id":"arxiv.content","type":"clients"}},"provider":{"data":{"id":"arxiv","type":"providers"}},"media":{"data":{"id":"10.48550/arxiv.2207.04779","type":"media"}},"references":{"data":[]},"citations":{"data":[]},"parts":{"data":[]},"partOf":{"data":[]},"versions":{"data":[]},"versionOf":{"data":[]}}}} +{"data":[{"id":"10.48550/arxiv.2207.04779","type":"dois","attributes":{"doi":"10.48550/arxiv.2207.04779","identifiers":[{"identifier":"2207.04779","identifierType":"arXiv"}],"creators":[{"name":"Bayer, Jonas","nameType":"Personal","givenName":"Jonas","familyName":"Bayer","affiliation":[],"nameIdentifiers":[]},{"name":"Benzmüller, Christoph","nameType":"Personal","givenName":"Christoph","familyName":"Benzmüller","affiliation":[],"nameIdentifiers":[]},{"name":"Buzzard, Kevin","nameType":"Personal","givenName":"Kevin","familyName":"Buzzard","affiliation":[],"nameIdentifiers":[]},{"name":"David, Marco","nameType":"Personal","givenName":"Marco","familyName":"David","affiliation":[],"nameIdentifiers":[]},{"name":"Lamport, Leslie","nameType":"Personal","givenName":"Leslie","familyName":"Lamport","affiliation":[],"nameIdentifiers":[]},{"name":"Matiyasevich, Yuri","nameType":"Personal","givenName":"Yuri","familyName":"Matiyasevich","affiliation":[],"nameIdentifiers":[]},{"name":"Paulson, Lawrence","nameType":"Personal","givenName":"Lawrence","familyName":"Paulson","affiliation":[],"nameIdentifiers":[]},{"name":"Schleicher, Dierk","nameType":"Personal","givenName":"Dierk","familyName":"Schleicher","affiliation":[],"nameIdentifiers":[]},{"name":"Stock, Benedikt","nameType":"Personal","givenName":"Benedikt","familyName":"Stock","affiliation":[],"nameIdentifiers":[]},{"name":"Zelmanov, Efim","nameType":"Personal","givenName":"Efim","familyName":"Zelmanov","affiliation":[],"nameIdentifiers":[]}],"titles":[{"title":"Mathematical Proof Between Generations"}],"publisher":"arXiv","container":{},"publicationYear":2022,"subjects":[{"lang":"en","subject":"History and Overview (math.HO)","subjectScheme":"arXiv"},{"lang":"en","subject":"Logic in Computer Science (cs.LO)","subjectScheme":"arXiv"},{"subject":"FOS: Mathematics","subjectScheme":"Fields of Science and Technology (FOS)"},{"subject":"FOS: Mathematics","schemeUri":"http://www.oecd.org/science/inno/38235147.pdf","subjectScheme":"Fields of Science and Technology (FOS)"},{"subject":"FOS: Computer and information sciences","subjectScheme":"Fields of Science and Technology (FOS)"},{"subject":"FOS: Computer and information sciences","schemeUri":"http://www.oecd.org/science/inno/38235147.pdf","subjectScheme":"Fields of Science and Technology (FOS)"}],"contributors":[],"dates":[{"date":"2022-07-08T14:42:33Z","dateType":"Submitted","dateInformation":"v1"},{"date":"2024-03-05T01:15:18Z","dateType":"Updated","dateInformation":"v1"},{"date":"2022-07","dateType":"Available","dateInformation":"v1"},{"date":"2022","dateType":"Issued"}],"language":null,"types":{"ris":"RPRT","bibtex":"article","citeproc":"article-journal","schemaOrg":"ScholarlyArticle","resourceType":"Article","resourceTypeGeneral":"Text"},"relatedIdentifiers":[{"relationType":"IsVersionOf","relatedIdentifier":"10.1090/noti2860","relatedIdentifierType":"DOI"}],"relatedItems":[],"sizes":[],"formats":[],"version":"1","rightsList":[{"rights":"arXiv.org perpetual, non-exclusive license","rightsUri":"http://arxiv.org/licenses/nonexclusive-distrib/1.0/"}],"descriptions":[{"description":"A proof is one of the most important concepts of mathematics. However, there is a striking difference between how a proof is defined in theory and how it is used in practice. This puts the unique status of mathematics as exact science into peril. Now may be the time to reconcile theory and practice, i.e. precision and intuition, through the advent of computer proof assistants. For the most time this has been a topic for experts in specialized communities. However, mathematical proofs have become increasingly sophisticated, stretching the boundaries of what is humanly comprehensible, so that leading mathematicians have asked for formal verification of their proofs. At the same time, major theorems in mathematics have recently been computer-verified by people from outside of these communities, even by beginning students. This article investigates the gap between the different definitions of a proof and possibilities to build bridges. It is written as a polemic or a collage by different members of the communities in mathematics and computer science at different stages of their careers, challenging well-known preconceptions and exploring new perspectives.","descriptionType":"Abstract"},{"description":"17 pages, 1 figure","descriptionType":"Other"}],"geoLocations":[],"fundingReferences":[],"url":"https://arxiv.org/abs/2207.04779","contentUrl":null,"metadataVersion":2,"schemaVersion":"http://datacite.org/schema/kernel-4","source":"mds","isActive":true,"state":"findable","reason":null,"viewCount":0,"downloadCount":0,"referenceCount":0,"citationCount":0,"partCount":0,"partOfCount":0,"versionCount":0,"versionOfCount":1,"created":"2022-07-12T01:41:56Z","registered":"2022-07-12T01:41:57Z","published":null,"updated":"2024-03-05T11:33:47Z"},"relationships":{"client":{"data":{"id":"arxiv.content","type":"clients"}}}}],"meta":{"total":1,"totalPages":1,"page":1,"states":[{"id":"findable","title":"Findable","count":1}],"resourceTypes":[{"id":"text","title":"Text","count":1}],"created":[{"id":"2022","title":"2022","count":1}],"published":[{"id":"2022","title":"2022","count":1}],"registered":[{"id":"2022","title":"2022","count":1}],"providers":[{"id":"arxiv","title":"arXiv","count":1}],"clients":[{"id":"arxiv.content","title":"arXiv","count":1}],"affiliations":[],"prefixes":[{"id":"10.48550","title":"10.48550","count":1}],"certificates":[],"licenses":[],"schemaVersions":[{"id":"4","title":"Schema 4","count":1}],"linkChecksStatus":[],"subjects":[{"id":"FOS: Computer and information sciences","title":"Fos: Computer And Information Sciences","count":1},{"id":"FOS: Mathematics","title":"Fos: Mathematics","count":1},{"id":"History and Overview (math.HO)","title":"History And Overview (Math.Ho)","count":1},{"id":"Logic in Computer Science (cs.LO)","title":"Logic In Computer Science (Cs.Lo)","count":1}],"fieldsOfScience":[{"id":"computer_and_information_sciences","title":"Computer and information sciences","count":1},{"id":"mathematics","title":"Mathematics","count":1}],"citations":[],"views":[],"downloads":[]},"links":{"self":"https://api.datacite.org/dois?query=10.48550/arxiv.2207.04779"}} From e35f859e06def64f44de426be01a12f74e9b363f Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Tue, 16 Apr 2024 12:02:48 +0200 Subject: [PATCH 181/479] datacite api test for no results count (cherry picked from commit 9cd1a1093d08d74da6773112645dfd79b69dfdd0) --- ...DataCiteImportMetadataSourceServiceIT.java | 19 +++++++++++++++++++ .../dspace/app/rest/dataCite-noResults.json | 1 + 2 files changed, 20 insertions(+) create mode 100644 dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-noResults.json diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java index 83ebc40c7966..c1481f0573c4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java @@ -146,4 +146,23 @@ private ArrayList getRecords() { return records; } + @Test + public void dataCiteImportMetadataNoResultsTest() throws Exception { + context.turnOffAuthorisationSystem(); + CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient(); + CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class); + try (InputStream dataciteResp = getClass().getResourceAsStream("dataCite-noResults.json")) { + String dataciteTextResp = IOUtils.toString(dataciteResp, Charset.defaultCharset()); + liveImportClientImpl.setHttpClient(httpClient); + CloseableHttpResponse response = mockResponse(dataciteTextResp, 200, "OK"); + when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response); + context.restoreAuthSystemState(); + int tot = dataCiteServiceImpl.getRecordsCount("nocontent"); + assertEquals(0, tot); + Collection importRecords = dataCiteServiceImpl.getRecords("nocontent", 0 , -1); + assertEquals(0, importRecords.size()); + } finally { + liveImportClientImpl.setHttpClient(originalHttpClient); + } + } } diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-noResults.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-noResults.json new file mode 100644 index 000000000000..c54fbe3636da --- /dev/null +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/dataCite-noResults.json @@ -0,0 +1 @@ +{"data":[],"meta":{"total":0,"totalPages":0,"page":1},"links":{"self":"https://api.datacite.org/dois?query=nocontent"}} From 17c68323dbffea126add945ea18a1e2ad65cfe73 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Fri, 10 May 2024 18:08:19 +0200 Subject: [PATCH 182/479] avoid empty data to create phantom records for datacite import (cherry picked from commit 9667a3374d0aab18c91f53f59f8d6dc05e2df429) --- .../DataCiteImportMetadataSourceServiceImpl.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java index 298c30ec39a5..6c65d96b375d 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java @@ -143,12 +143,16 @@ public Collection getRecords(String query, int start, int count) t Iterator iterator = dataNode.iterator(); while (iterator.hasNext()) { JsonNode singleDoiNode = iterator.next(); - String json = singleDoiNode.at("/attributes").toString(); - records.add(transformSourceRecords(json)); + JsonNode singleDoiNodeAttribute = singleDoiNode.at("/attributes"); + if (!singleDoiNodeAttribute.isMissingNode()) { + records.add(transformSourceRecords(singleDoiNodeAttribute.toString())); + } } } else { - String json = dataNode.at("/attributes").toString(); - records.add(transformSourceRecords(json)); + JsonNode singleDoiNodeAttribute = dataNode.at("/attributes"); + if (!singleDoiNodeAttribute.isMissingNode()) { + records.add(transformSourceRecords(singleDoiNodeAttribute.toString())); + } } return records; From 1128b0eb10aba8170b1e9987eded3e75746eb0f5 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 13 May 2024 16:20:29 +0200 Subject: [PATCH 183/479] remove outdated comment as RSS feeds are supported in DS 7.3+ --- dspace/config/dspace.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 0b7cd6ee44ae..3063572514aa 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1276,8 +1276,6 @@ plugin.named.org.dspace.content.license.LicenseArgumentFormatter = \ org.dspace.content.license.SimpleDSpaceObjectLicenseFormatter = eperson #### Syndication Feed (RSS) Settings ###### -# TODO: UNSUPPORTED in DSpace 7.0. Will be added in a later release - # URLs returned by the feed will point at the global handle server # (e.g. https://hdl.handle.net/123456789/1). Set to true to use local server # URLs (i.e. https://myserver.myorg/handle/123456789/1) From 2309496f67f940ccdabd6a4b46b05bff3935004c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 19 Apr 2024 09:13:57 +0200 Subject: [PATCH 184/479] Correct relationship direction for persons in the project submission step. (cherry picked from commit ee2abfaf8801a252fd7033de7849220fb79ddab7) --- dspace/config/submission-forms.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index 39a4778356c0..07ebdbd1c281 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -516,7 +516,7 @@ - isProjectOfPerson + isPersonOfProject person true From b0995cdf6c7aa1df38cd9b230966240acc2c785d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 19 Apr 2024 09:15:43 +0200 Subject: [PATCH 185/479] Set correct metadata field for virtual metadata of persons in projects. (cherry picked from commit ff7d96b82ca832cc3a5342350eeccc29943e83b7) --- dspace/config/spring/api/virtual-metadata.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/virtual-metadata.xml b/dspace/config/spring/api/virtual-metadata.xml index efab52c1b7c3..dee96eca7c0c 100644 --- a/dspace/config/spring/api/virtual-metadata.xml +++ b/dspace/config/spring/api/virtual-metadata.xml @@ -77,7 +77,7 @@ - + - + + on the filters defined in item-filters.xml, eg. + Of course, you can use a filter on the VersionedDOIIdentifierProvider as well. + --> - From 2d150cb86cefbc84ed7e3de9a9c5230abe5c934a Mon Sep 17 00:00:00 2001 From: Pascal-Nicolas Becker Date: Tue, 4 Jun 2024 00:03:47 +0200 Subject: [PATCH 206/479] Cleanup doi filters We can set filters in identifier-service.xml. Setting them in modules/identifiers.cfg is just overidding the other one. To keep things simple, we should avoid having two different filters for the same issue. The filter configured in spring is working for any new DOI, while the one we take out here, is just working for items being run through the install item service. (cherry picked from commit 59aaf3e57cceca6cf6e5e878a94c2dc3889a7929) --- dspace/config/modules/identifiers.cfg | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/dspace/config/modules/identifiers.cfg b/dspace/config/modules/identifiers.cfg index 2660646af394..aff37c89c56b 100644 --- a/dspace/config/modules/identifiers.cfg +++ b/dspace/config/modules/identifiers.cfg @@ -15,17 +15,12 @@ # Default: false #identifiers.submission.register = true -# This configuration property can be set to a filter name to determine if a PENDING DOI for an item -# should be queued for registration. If the filter doesn't match, the DOI will stay in PENDING or MINTED status -# so that the identifier itself persists in case it is considered for registration in the future. -# See doi-filter and other example filters in item-filters.xml. -# Default (always_true_filter) -#identifiers.submission.filter.install = doi-filter - # This optional configuration property can be set to a filter name, in case there are some initial rules to apply # when first deciding whether a DOI should be be created for a new workspace item with a PENDING status. # This filter is only applied if identifiers.submission.register is true. # This filter is updated as submission data is saved. +# If you're looking for the filter that decides whether a DOI of an installed item should be queued for registration +# at DataCite, please check the filter in the identifier service spring configuration. # Default: (always_true_filter) #identifiers.submission.filter.workspace = always_true_filter From fe76579d04f0e88d0c6e3516066e81df133e0e34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 19:51:43 +0000 Subject: [PATCH 207/479] Bump org.apache.commons:commons-configuration2 from 2.9.0 to 2.10.1 Bumps org.apache.commons:commons-configuration2 from 2.9.0 to 2.10.1. --- updated-dependencies: - dependency-name: org.apache.commons:commons-configuration2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit df7220bd9814e2e31f1000ebc6074395c27a196c) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f7ce30b30df1..8f213db2a55c 100644 --- a/pom.xml +++ b/pom.xml @@ -1486,7 +1486,7 @@ org.apache.commons commons-configuration2 - 2.9.0 + 2.10.1 org.apache.commons From 0d4d065d457155ab6a3c7bfd479504af75c10a8f Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Thu, 23 May 2024 16:32:56 +0200 Subject: [PATCH 208/479] Create a temporary version of the ConfigurationPlaceholderConfigurer to overcome bu in 2.10 (cherry picked from commit d44d76ea03aca7490a11e9a1d9c52fdc669feb1c) --- .../util/DSpaceConfigurationInitializer.java | 6 +-- ...aceConfigurationPlaceholderConfigurer.java | 5 +- .../DSpaceConfigurationPropertySource.java | 54 +++++++++++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java diff --git a/dspace-api/src/test/java/org/dspace/util/DSpaceConfigurationInitializer.java b/dspace-api/src/test/java/org/dspace/util/DSpaceConfigurationInitializer.java index e2e0355f123a..e5a8adb2fdf7 100644 --- a/dspace-api/src/test/java/org/dspace/util/DSpaceConfigurationInitializer.java +++ b/dspace-api/src/test/java/org/dspace/util/DSpaceConfigurationInitializer.java @@ -8,7 +8,7 @@ package org.dspace.util; import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.spring.ConfigurationPropertySource; +import org.dspace.servicemanager.config.DSpaceConfigurationPropertySource; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.springframework.context.ApplicationContextInitializer; @@ -38,8 +38,8 @@ public void initialize(final ConfigurableApplicationContext applicationContext) Configuration configuration = configurationService.getConfiguration(); // Create an Apache Commons Configuration Property Source from our configuration - ConfigurationPropertySource apacheCommonsConfigPropertySource = - new ConfigurationPropertySource(configuration.getClass().getName(), configuration); + DSpaceConfigurationPropertySource apacheCommonsConfigPropertySource = + new DSpaceConfigurationPropertySource(configuration.getClass().getName(), configuration); // Prepend it to the Environment's list of PropertySources // NOTE: This is added *first* in the list so that settings in DSpace's diff --git a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPlaceholderConfigurer.java b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPlaceholderConfigurer.java index b85450dcd039..caa715e21bfb 100644 --- a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPlaceholderConfigurer.java +++ b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPlaceholderConfigurer.java @@ -8,7 +8,6 @@ package org.dspace.servicemanager.config; import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.spring.ConfigurationPropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.env.MutablePropertySources; @@ -27,8 +26,8 @@ public class DSpaceConfigurationPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer { public DSpaceConfigurationPlaceholderConfigurer(Configuration configuration) { - ConfigurationPropertySource apacheCommonsConfigPropertySource = - new ConfigurationPropertySource(configuration.getClass().getName(), configuration); + DSpaceConfigurationPropertySource apacheCommonsConfigPropertySource = + new DSpaceConfigurationPropertySource(configuration.getClass().getName(), configuration); MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addLast(apacheCommonsConfigPropertySource); setPropertySources(propertySources); diff --git a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java new file mode 100644 index 000000000000..9bbf821766da --- /dev/null +++ b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dspace.servicemanager.config; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.core.env.EnumerablePropertySource; + +/** + * Allow use of Apache Commons Configuration Objects as Spring PropertySources. + * This class is a copy of the ConfigurationPropertySource class in the Apache Commons Configuration + * project needed until to fix the issue https://issues.apache.org/jira/browse/CONFIGURATION-846 + */ +public class DSpaceConfigurationPropertySource extends EnumerablePropertySource { + + protected DSpaceConfigurationPropertySource(final String name) { + super(name); + } + + public DSpaceConfigurationPropertySource(final String name, final Configuration source) { + super(name, source); + } + + @Override + public Object getProperty(final String name) { + final String[] propValue = source.getStringArray(name); + return propValue != null && propValue.length == 1 ? propValue[0] : propValue; + } + + @Override + public String[] getPropertyNames() { + final List keys = new ArrayList<>(); + source.getKeys().forEachRemaining(keys::add); + return keys.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } +} From 4b5248fe1515fc39a5f42d206d3faf4c801ab340 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 27 May 2024 11:49:00 +0200 Subject: [PATCH 209/479] DURACOM-267 update commons-configuration2 fix according to the community feedback (cherry picked from commit f2d4ffc49c457a4f919a4bf2ef4171ea49246103) --- .../config/DSpaceConfigurationPropertySource.java | 10 ++++++++-- pom.xml | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java index 9bbf821766da..a904e04c5a08 100644 --- a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java +++ b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java @@ -26,7 +26,7 @@ /** * Allow use of Apache Commons Configuration Objects as Spring PropertySources. - * This class is a copy of the ConfigurationPropertySource class in the Apache Commons Configuration + * This class is a temporary copy of the ConfigurationPropertySource class in the Apache Commons Configuration * project needed until to fix the issue https://issues.apache.org/jira/browse/CONFIGURATION-846 */ public class DSpaceConfigurationPropertySource extends EnumerablePropertySource { @@ -42,7 +42,13 @@ public DSpaceConfigurationPropertySource(final String name, final Configuration @Override public Object getProperty(final String name) { final String[] propValue = source.getStringArray(name); - return propValue != null && propValue.length == 1 ? propValue[0] : propValue; + if (propValue == null || propValue.length == 0) { + return null; + } else if (propValue.length == 1) { + return propValue[0]; + } else { + return propValue; + } } @Override diff --git a/pom.xml b/pom.xml index 8f213db2a55c..ae83442235e0 100644 --- a/pom.xml +++ b/pom.xml @@ -432,6 +432,8 @@ + + **/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java **/src/test/resources/** **/src/test/data/** **/src/main/license/** From b9e6af675878c489b27a8171e18ea431d1d26170 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 31 May 2024 19:47:12 +0200 Subject: [PATCH 210/479] DURACOM-267 improve handling of empty configuration property (cherry picked from commit 0422b8786f9fb9d39f51403da94f69a77c20044c) --- .../DSpaceConfigurationPropertySource.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java index a904e04c5a08..d3394399301f 100644 --- a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java +++ b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java @@ -41,13 +41,17 @@ public DSpaceConfigurationPropertySource(final String name, final Configuration @Override public Object getProperty(final String name) { - final String[] propValue = source.getStringArray(name); - if (propValue == null || propValue.length == 0) { - return null; - } else if (propValue.length == 1) { - return propValue[0]; + if (source.getProperty(name) != null) { + final String[] propValue = source.getStringArray(name); + if (propValue == null || propValue.length == 0) { + return ""; + } else if (propValue.length == 1) { + return propValue[0]; + } else { + return propValue; + } } else { - return propValue; + return null; } } From 7fc74bdc567c91137484255979ee4f6f4387db22 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 3 Jun 2024 10:07:06 +0200 Subject: [PATCH 211/479] DURACOM-267 assure that our custom DSpaceConfigurationPropertySource is used also for the spring context created by the DSpace kernel (cherry picked from commit 4fcf995b699b287f7e773ad8ca420cd68cae1e12) --- .../app/rest/utils/DSpaceConfigurationInitializer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/DSpaceConfigurationInitializer.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/DSpaceConfigurationInitializer.java index 56b8ae32dce1..9291a436c787 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/DSpaceConfigurationInitializer.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/DSpaceConfigurationInitializer.java @@ -8,7 +8,7 @@ package org.dspace.app.rest.utils; import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.spring.ConfigurationPropertySource; +import org.dspace.servicemanager.config.DSpaceConfigurationPropertySource; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.slf4j.Logger; @@ -38,8 +38,8 @@ public void initialize(final ConfigurableApplicationContext applicationContext) Configuration configuration = configurationService.getConfiguration(); // Create an Apache Commons Configuration Property Source from our configuration - ConfigurationPropertySource apacheCommonsConfigPropertySource = - new ConfigurationPropertySource(configuration.getClass().getName(), configuration); + DSpaceConfigurationPropertySource apacheCommonsConfigPropertySource = + new DSpaceConfigurationPropertySource(configuration.getClass().getName(), configuration); // Prepend it to the Environment's list of PropertySources // NOTE: This is added *first* in the list so that settings in DSpace's ConfigurationService *override* From c5d139c9e1dc71482d1a9112b01ad718c317bfae Mon Sep 17 00:00:00 2001 From: nwoodward Date: Mon, 3 Jun 2024 12:53:44 -0500 Subject: [PATCH 212/479] updates creativecommons.org links to https --- .../impl/CCLicenseAddPatchOperation.java | 2 +- .../rest/CCLicenseAddPatchOperationIT.java | 4 +-- .../rest/CCLicenseRemovePatchOperationIT.java | 4 +-- .../org/dspace/app/rest/cinii-second.xml | 4 +-- .../org/dspace/license/cc-license-rdf.xml | 36 +++++++++---------- dspace/config/dspace.cfg | 2 +- dspace/config/submission-forms.xml | 12 +++---- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java index ec4dff9f6c51..602ae5e9690b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java @@ -26,7 +26,7 @@ * Example: * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/31599 -H "Content-Type: * application/json" -d '[{ "op": "add", "path": "/sections/cclicense/uri", - * "value":"http://creativecommons.org/licenses/by-nc-sa/3.0/us/"}]' + * "value":"https://creativecommons.org/licenses/by-nc-sa/3.0/us/"}]' * */ public class CCLicenseAddPatchOperation extends AddPatchOperation { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java index f07c816b9c98..eb49661259cc 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java @@ -63,7 +63,7 @@ public void patchSubmissionCCLicense() throws Exception { List ops = new ArrayList<>(); AddOperation addOperation = new AddOperation("/sections/cclicense/uri", - "http://creativecommons.org/licenses/by-nc-sa/4.0/"); + "https://creativecommons.org/licenses/by-nc-sa/4.0/"); ops.add(addOperation); String patchBody = getPatchContent(ops); @@ -74,7 +74,7 @@ public void patchSubmissionCCLicense() throws Exception { .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.sections.cclicense", allOf( - hasJsonPath("$.uri", is("http://creativecommons.org/licenses/by-nc-sa/4.0/")), + hasJsonPath("$.uri", is("https://creativecommons.org/licenses/by-nc-sa/4.0/")), hasJsonPath("$.rights", is("Attribution-NonCommercial-ShareAlike 4.0 International")), hasJsonPath("$.file.name", is("license_rdf")) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java index 8e01678899a5..dd2b99d158b9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java @@ -64,7 +64,7 @@ public void patchRemoveSubmissionCCLicense() throws Exception { // First add a license and verify it is added List ops = new ArrayList<>(); AddOperation addOperation = new AddOperation("/sections/cclicense/uri", - "http://creativecommons.org/licenses/by-nc-sa/4.0/"); + "https://creativecommons.org/licenses/by-nc-sa/4.0/"); ops.add(addOperation); String patchBody = getPatchContent(ops); @@ -75,7 +75,7 @@ public void patchRemoveSubmissionCCLicense() throws Exception { .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.sections.cclicense", allOf( - hasJsonPath("$.uri", is("http://creativecommons.org/licenses/by-nc-sa/4.0/")), + hasJsonPath("$.uri", is("https://creativecommons.org/licenses/by-nc-sa/4.0/")), hasJsonPath("$.rights", is("Attribution-NonCommercial-ShareAlike 4.0 International")), hasJsonPath("$.file.name", is("license_rdf")) diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/cinii-second.xml b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/cinii-second.xml index 8eb68ff047fc..cf1845ca5636 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/cinii-second.xml +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/cinii-second.xml @@ -57,7 +57,7 @@ 322 331 - © 2022 The Author(s). Published by National Institute for Materials Science in partnership with Taylor & Francis Group. This is an Open Access article distributed under the terms of the Creative Commons Attribution License (http://creativecommons.org/licenses/by/4.0/), which permits unrestricted use, distribution, and reproduction in any medium, provided the original work is properly cited. + © 2022 The Author(s). Published by National Institute for Materials Science in partnership with Taylor & Francis Group. This is an Open Access article distributed under the terms of the Creative Commons Attribution License (https://creativecommons.org/licenses/by/4.0/), which permits unrestricted use, distribution, and reproduction in any medium, provided the original work is properly cited. @@ -74,4 +74,4 @@ oai:irdb.nii.ac.jp:01257:0005348137 - \ No newline at end of file + diff --git a/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml b/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml index 5ff75ee4c747..2051406b550d 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml +++ b/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml @@ -1,31 +1,31 @@ - http://creativecommons.org/licenses/by-nc-sa/4.0/ + https://creativecommons.org/licenses/by-nc-sa/4.0/ Attribution-NonCommercial-ShareAlike 4.0 International false - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. + Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index fc0f516c3aa1..fc840ef00354 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1011,7 +1011,7 @@ cc.license.classfilter = publicdomain, recombo, mark # Jurisdiction of the creative commons license -- is it ported or not? # Use the key from the url seen in the response from the api call, -# http://api.creativecommons.org/rest/1.5/support/jurisdictions +# https://api.creativecommons.org/rest/1.5/support/jurisdictions # Commented out means the license is unported. # (e.g. nz = New Zealand, uk = England and Wales, jp = Japan) # or set value none for user-selected jurisdiction diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index 07ebdbd1c281..0b1e049c81ca 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -1549,27 +1549,27 @@ Attribution (CC-BY) - http://creativecommons.org/licenses/by/4.0/ + https://creativecommons.org/licenses/by/4.0/ Attribution, No Derivative Works (CC-BY-ND) - http://creativecommons.org/licenses/by-nd/4.0/ + https://creativecommons.org/licenses/by-nd/4.0/ Attribution, Share-alike (CC-BY-SA) - http://creativecommons.org/licenses/by-sa/4.0/ + https://creativecommons.org/licenses/by-sa/4.0/ Attribution, Non-commercial (CC-BY-NC) - http://creativecommons.org/licenses/by-nc/4.0/ + https://creativecommons.org/licenses/by-nc/4.0/ Attribution, Non-commercial, No Derivative Works (CC-BY-NC-ND) - http://creativecommons.org/licenses/by-nc-nd/4.0/ + https://creativecommons.org/licenses/by-nc-nd/4.0/ Attribution, Non-commercial, Share-alike (CC-BY-NC-SA) - http://creativecommons.org/licenses/by-nc-sa/4.0/ + https://creativecommons.org/licenses/by-nc-sa/4.0/ From 4cd3b75e67e04aae1dd25be13a4d532e74940642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Morin?= Date: Fri, 3 May 2024 14:08:50 -0400 Subject: [PATCH 213/479] Fixed classpath issue in test_database target (cherry picked from commit 6937c19973f0810a4b1eb646bb0eaefdded9cae6) --- dspace/src/main/config/build.xml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/dspace/src/main/config/build.xml b/dspace/src/main/config/build.xml index 8b33522d2b14..940287c9d166 100644 --- a/dspace/src/main/config/build.xml +++ b/dspace/src/main/config/build.xml @@ -764,8 +764,25 @@ Common usage: + + + + + + + + + + + + + + + + + Date: Tue, 4 Jun 2024 09:43:10 -0500 Subject: [PATCH 214/479] Ensure work directory is cleaned up whether zip file is created successfully or an error occurs. (cherry picked from commit b7f764746c8a88d8db3ec3cf989b6aa3eda8e087) --- .../app/itemexport/ItemExportServiceImpl.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java index a884f9b07564..7c80e1ea7dc6 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java @@ -490,7 +490,7 @@ public void exportAsZip(Context context, Iterator items, File wkDir = new File(workDir); if (!wkDir.exists() && !wkDir.mkdirs()) { - logError("Unable to create working direcory"); + logError("Unable to create working directory"); } File dnDir = new File(destDirName); @@ -498,11 +498,18 @@ public void exportAsZip(Context context, Iterator items, logError("Unable to create destination directory"); } - // export the items using normal export method - exportItem(context, items, workDir, seqStart, migrate, excludeBitstreams); + try { + // export the items using normal export method (this exports items to our workDir) + exportItem(context, items, workDir, seqStart, migrate, excludeBitstreams); - // now zip up the export directory created above - zip(workDir, destDirName + System.getProperty("file.separator") + zipFileName); + // now zip up the workDir directory created above + zip(workDir, destDirName + System.getProperty("file.separator") + zipFileName); + } finally { + // Cleanup workDir created above, if it still exists + if (wkDir.exists()) { + deleteDirectory(wkDir); + } + } } @Override From 26658b666517f9da71de5e1ff706dce692da6350 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 16 May 2024 16:36:35 +0200 Subject: [PATCH 215/479] evaluate surname and given-name if authname is not present (cherry picked from commit 8b5f0445373e5ce60be2b12fd1d487f54f74e350) --- .../contributor/AuthorMetadataContributor.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/AuthorMetadataContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/AuthorMetadataContributor.java index 26063dc7441d..62b7fe81e399 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/AuthorMetadataContributor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/AuthorMetadataContributor.java @@ -71,7 +71,7 @@ public Collection contributeMetadata(Element element) { } /** - * Retrieve the the ScopusID, orcid, author name and affiliationID + * Retrieve the ScopusID, orcid, author name and affiliationID * metadata associated with the given element object. * If the value retrieved from the element is empty * it is set PLACEHOLDER_PARENT_METADATA_VALUE @@ -82,11 +82,19 @@ public Collection contributeMetadata(Element element) { private List getMetadataOfAuthors(Element element) throws JaxenException { List metadatums = new ArrayList(); Element authname = element.getChild("authname", NAMESPACE); + Element surname = element.getChild("surname", NAMESPACE); + Element givenName = element.getChild("given-name", NAMESPACE); Element scopusId = element.getChild("authid", NAMESPACE); Element orcid = element.getChild("orcid", NAMESPACE); Element afid = element.getChild("afid", NAMESPACE); - addMetadatum(metadatums, getMetadata(getElementValue(authname), this.authname)); + if (authname != null) { + addMetadatum(metadatums, getMetadata(getElementValue(authname), this.authname)); + } else { + addMetadatum(metadatums, getMetadata(getElementValue(surname) + ", " + + getElementValue(givenName), this.authname)); + } + addMetadatum(metadatums, getMetadata(getElementValue(scopusId), this.scopusId)); addMetadatum(metadatums, getMetadata(getElementValue(orcid), this.orcid)); addMetadatum(metadatums, getMetadata(StringUtils.isNotBlank(afid.getValue()) @@ -170,4 +178,4 @@ public void setAffiliation(MetadataFieldConfig affiliation) { this.affiliation = affiliation; } -} \ No newline at end of file +} From 6e5c5089abe776b0e3b8b364ec10393855275429 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 16 May 2024 16:37:48 +0200 Subject: [PATCH 216/479] Update scopus-empty-resp.xml (cherry picked from commit 4b2ea66f19324d204058084434ff99a71fd6588e) --- .../resources/org/dspace/app/rest/scopus-empty-resp.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/scopus-empty-resp.xml b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/scopus-empty-resp.xml index b2b4264b5c34..3e0ecbead350 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/scopus-empty-resp.xml +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/scopus-empty-resp.xml @@ -3,9 +3,11 @@ 0 0 0 - - + + Result set was empty - \ No newline at end of file + From 2b1270197e2d4af1d7051a2d75c501d00d3fc318 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 16 May 2024 16:40:04 +0200 Subject: [PATCH 217/479] improve handling of 0 hits responses of Scopus API (cherry picked from commit b12bd6ce565bb95fcb470a4ea156abd0a6ca531f) --- ...ScopusImportMetadataSourceServiceImpl.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java index 944d467e3156..e61ca0528681 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,6 +25,8 @@ import javax.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -62,6 +65,8 @@ public class ScopusImportMetadataSourceServiceImpl extends AbstractImportMetadat @Autowired private LiveImportClient liveImportClient; + private final static Logger log = LogManager.getLogger(); + public LiveImportClient getLiveImportClient() { return liveImportClient; } @@ -200,6 +205,9 @@ public Integer call() throws Exception { Map requestParams = getRequestParameters(query, null, null, null); params.put(URI_PARAMETERS, requestParams); String response = liveImportClient.executeHttpGetRequest(timeout, url, params); + if (StringUtils.isEmpty(response)) { + return 0; + } SAXBuilder saxBuilder = new SAXBuilder(); // disallow DTD parsing to ensure no XXE attacks can occur @@ -245,6 +253,10 @@ public List call() throws Exception { Map requestParams = getRequestParameters(queryString, viewMode, null, null); params.put(URI_PARAMETERS, requestParams); String response = liveImportClient.executeHttpGetRequest(timeout, url, params); + if (StringUtils.isEmpty(response)) { + return results; + } + List elements = splitToRecords(response); for (Element record : elements) { results.add(transformSourceRecords(record)); @@ -304,6 +316,10 @@ public List call() throws Exception { Map requestParams = getRequestParameters(queryString, viewMode, start, count); params.put(URI_PARAMETERS, requestParams); String response = liveImportClient.executeHttpGetRequest(timeout, url, params); + if (StringUtils.isEmpty(response)) { + return results; + } + List elements = splitToRecords(response); for (Element record : elements) { results.add(transformSourceRecords(record)); @@ -349,6 +365,10 @@ public List call() throws Exception { Map requestParams = getRequestParameters(queryString, viewMode, start, count); params.put(URI_PARAMETERS, requestParams); String response = liveImportClient.executeHttpGetRequest(timeout, url, params); + if (StringUtils.isEmpty(response)) { + return results; + } + List elements = splitToRecords(response); for (Element record : elements) { results.add(transformSourceRecords(record)); @@ -383,10 +403,16 @@ private List splitToRecords(String recordsSrc) { saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); - List records = root.getChildren("entry",Namespace.getNamespace("http://www.w3.org/2005/Atom")); + String totalResults = root.getChildText("totalResults", Namespace.getNamespace("http://a9.com/-/spec/opensearch/1.1/")); + if (totalResults != null && "0".equals(totalResults)) { + log.debug("got Scopus API with empty response"); + return Collections.emptyList(); + } + List records = root.getChildren("entry", Namespace.getNamespace("http://www.w3.org/2005/Atom")); return records; } catch (JDOMException | IOException e) { - return new ArrayList(); + log.warn("got unexpected XML response from Scopus API: " + e.getMessage()); + return Collections.emptyList(); } } @@ -422,4 +448,4 @@ public void setInstKey(String instKey) { this.instKey = instKey; } -} \ No newline at end of file +} From 8e56fdd95b2054a49606103554f5bea1366fce21 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 16 May 2024 16:41:31 +0200 Subject: [PATCH 218/479] fixed failed test (cherry picked from commit 6989cb6f15761ac453ada2b5ce69b6d2dbbd3e78) --- .../app/rest/ScopusImportMetadataSourceServiceIT.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index b9310e3adba6..7f6cb53ce400 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -96,7 +96,7 @@ public void scopusImportMetadataGetRecordsCountTest() throws Exception { } @Test - public void scopusImportMetadataGetRecordsEmptyResponceTest() throws Exception { + public void scopusImportMetadataGetRecordsEmptyResponseTest() throws Exception { context.turnOffAuthorisationSystem(); String originApiKey = scopusServiceImpl.getApiKey(); if (StringUtils.isBlank(originApiKey)) { @@ -113,8 +113,7 @@ public void scopusImportMetadataGetRecordsEmptyResponceTest() throws Exception { context.restoreAuthSystemState(); Collection recordsImported = scopusServiceImpl.getRecords("roma", 0, 20); - ImportRecord importedRecord = recordsImported.iterator().next(); - assertTrue(importedRecord.getValueList().isEmpty()); + assertTrue(recordsImported.isEmpty()); } finally { liveImportClientImpl.setHttpClient(originalHttpClient); scopusServiceImpl.setApiKey(originApiKey); @@ -229,4 +228,4 @@ private ArrayList getRecords() { return records; } -} \ No newline at end of file +} From 8d82457eb7b3f5f96fa164c0ca5905bde34a4649 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Mon, 27 May 2024 00:17:53 +0200 Subject: [PATCH 219/479] 115434: Added relatedEntityType parameter to byLabel endpoint to differentiate relationships with same label and different entity types (cherry picked from commit 8512fab392c99d774197c026abbdfc941764ae9d) --- .../RelationshipRestRepository.java | 35 +++++++++--- .../rest/RelationshipRestRepositoryIT.java | 55 +++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RelationshipRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RelationshipRestRepository.java index 21f43ddacd63..6be4f390daa9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RelationshipRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RelationshipRestRepository.java @@ -29,9 +29,11 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.DSpaceObject; +import org.dspace.content.EntityType; import org.dspace.content.Item; import org.dspace.content.Relationship; import org.dspace.content.RelationshipType; +import org.dspace.content.service.EntityTypeService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; @@ -60,6 +62,9 @@ public class RelationshipRestRepository extends DSpaceRestRepository findByLabel(@Parameter(value = "label", required = true) String label, @Parameter(value = "dso", required = false) UUID dsoId, + @Parameter(value = "relatedEntityType") String relatedEntityType, Pageable pageable) throws SQLException { Context context = obtainContext(); @@ -352,14 +359,28 @@ public Page findByLabel(@Parameter(value = "label", required = if (item == null) { throw new ResourceNotFoundException("The request DSO with id: " + dsoId + " was not found"); } + + EntityType dsoEntityType = itemService.getEntityType(context, item); + + if (dsoEntityType == null) { + throw new UnprocessableEntityException(String.format( + "The request DSO with id: %s doesn't have an entity type", dsoId)); + } + for (RelationshipType relationshipType : relationshipTypeList) { - boolean isLeft = false; - if (relationshipType.getLeftwardType().equalsIgnoreCase(label)) { - isLeft = true; + if (relatedEntityType == null || + relationshipType.getRightType().getLabel().equals(dsoEntityType.getLabel()) && + relationshipType.getLeftType().getLabel().equals(relatedEntityType) || + relationshipType.getRightType().getLabel().equals(relatedEntityType) && + relationshipType.getLeftType().getLabel().equals(dsoEntityType.getLabel())) { + boolean isLeft = relationshipType.getLeftwardType().equalsIgnoreCase(label); + total += + relationshipService.countByItemAndRelationshipType(context, item, relationshipType, isLeft); + relationships.addAll( + relationshipService.findByItemAndRelationshipType(context, item, relationshipType, + isLeft, pageable.getPageSize(), + Math.toIntExact(pageable.getOffset()))); } - total += relationshipService.countByItemAndRelationshipType(context, item, relationshipType, isLeft); - relationships.addAll(relationshipService.findByItemAndRelationshipType(context, item, relationshipType, - isLeft, pageable.getPageSize(), Math.toIntExact(pageable.getOffset()))); } } else { for (RelationshipType relationshipType : relationshipTypeList) { @@ -377,7 +398,7 @@ public Page findByLabel(@Parameter(value = "label", required = * of potentially related items we need to know which of these other items * are already in a specific relationship with the focus item and, * by exclusion which ones are not yet related. - * + * * @param typeId The relationship type id to apply as a filter to the returned relationships * @param label The name of the relation as defined from the side of the 'focusItem' * @param focusUUID The uuid of the item to be checked on the side defined by 'relationshipLabel' diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java index d8e53c770c70..d126c0352d0c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java @@ -2323,6 +2323,61 @@ public void findRelationshipByLabelTest() throws Exception { ; } + @Test + public void findRelationshipByLabelWithRelatedEntityTypeTest() throws Exception { + context.turnOffAuthorisationSystem(); + RelationshipType isAuthorOfPublicationRelationshipTypePublication = relationshipTypeService + .findbyTypesAndTypeName(context, entityTypeService.findByEntityType(context, "Publication"), + entityTypeService.findByEntityType(context, "Person"), + "isAuthorOfPublication", "isPublicationOfAuthor"); + RelationshipType isAuthorOfPublicationRelationshipTypeOrgUnit = relationshipTypeService + .findbyTypesAndTypeName(context, entityTypeService.findByEntityType(context, "Publication"), + entityTypeService.findByEntityType(context, "OrgUnit"), + "isAuthorOfPublication", "isPublicationOfAuthor"); + + // We're creating a Relationship of type isAuthorOfPublication between a Publication and a Person + Relationship relationship1 = RelationshipBuilder + .createRelationshipBuilder(context, publication1, author1, isAuthorOfPublicationRelationshipTypePublication) + .build(); + + // We're creating a Relationship of type isAuthorOfPublication between a Publication and an OrgUnit + Relationship relationship2 = RelationshipBuilder + .createRelationshipBuilder(context, publication1, orgUnit1, isAuthorOfPublicationRelationshipTypeOrgUnit) + .build(); + context.restoreAuthSystemState(); + + // Perform a GET request to the searchByLabel endpoint, asking for Relationships of type isAuthorOfPublication + // With an extra parameter namely DSO which resolves to the publication used by both relationships. + // Both relationships should be returned if we don't specify the DSO's related entity type + getClient().perform(get("/api/core/relationships/search/byLabel") + .param("label", "isAuthorOfPublication") + .param("dso", publication1.getID().toString()) + .param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page", is(PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2)))) + .andExpect(jsonPath("$._embedded.relationships", containsInAnyOrder( + RelationshipMatcher.matchRelationship(relationship1), + RelationshipMatcher.matchRelationship(relationship2) + ))) + ; + + // Perform a GET request to the searchByLabel endpoint, asking for Relationships of type isAuthorOfPublication + // With an extra parameter namely DSO which resolves to the publication used by both relationships. + // Only the Person relationship should be returned if we specify the DSO's related entity type + getClient().perform(get("/api/core/relationships/search/byLabel") + .param("label", "isAuthorOfPublication") + .param("dso", publication1.getID().toString()) + .param("relatedEntityType", "Person") + .param("projection", "full")) + + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page", is(PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)))) + .andExpect(jsonPath("$._embedded.relationships", containsInAnyOrder( + RelationshipMatcher.matchRelationship(relationship1) + ))) + ; + } + @Test public void putRelationshipWithNonexistentID() throws Exception { context.turnOffAuthorisationSystem(); From 7789304ebe531fe16ddad7af798998249f0197ba Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 7 Jun 2024 20:53:48 +0200 Subject: [PATCH 220/479] 115434: Added test proving that different values for relatedEntityType return different results (cherry picked from commit bbae1fb0d110e2c1e7ad63bc01ac1084590ad62c) --- .../app/rest/RelationshipRestRepositoryIT.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java index d126c0352d0c..aad26ba2c11f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java @@ -2376,6 +2376,22 @@ public void findRelationshipByLabelWithRelatedEntityTypeTest() throws Exception RelationshipMatcher.matchRelationship(relationship1) ))) ; + + // Perform a GET request to the searchByLabel endpoint, asking for Relationships of type isAuthorOfPublication + // With an extra parameter namely DSO which resolves to the publication used by both relationships. + // Only the OrgUnit relationship should be returned if we specify the DSO's related entity type + getClient().perform(get("/api/core/relationships/search/byLabel") + .param("label", "isAuthorOfPublication") + .param("dso", publication1.getID().toString()) + .param("relatedEntityType", "OrgUnit") + .param("projection", "full")) + + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page", is(PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)))) + .andExpect(jsonPath("$._embedded.relationships", containsInAnyOrder( + RelationshipMatcher.matchRelationship(relationship2) + ))) + ; } @Test From 928dad1c5cbbbe95b69cf4b3d6f533e72a25d221 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 29 Apr 2024 11:21:08 -0500 Subject: [PATCH 221/479] Ensure List is not empty before returning first value (cherry picked from commit 068bcdf3af2a74218cfb77fdb944e7384de3db75) --- .../dspace/content/DSpaceObjectServiceImpl.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java index 2119959073f0..f47ed47d4c0d 100644 --- a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java @@ -314,20 +314,26 @@ public List addMetadata(Context context, T dso, MetadataField met @Override public MetadataValue addMetadata(Context context, T dso, MetadataField metadataField, String language, String value, String authority, int confidence) throws SQLException { - return addMetadata(context, dso, metadataField, language, Arrays.asList(value), Arrays.asList(authority), - Arrays.asList(confidence)).get(0); + List metadataValues = + addMetadata(context, dso, metadataField, language, Arrays.asList(value), Arrays.asList(authority), + Arrays.asList(confidence)); + return CollectionUtils.isNotEmpty(metadataValues) ? metadataValues.get(0) : null; } @Override public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang, String value) throws SQLException { - return addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value)).get(0); + List metadataValues = + addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value)); + return CollectionUtils.isNotEmpty(metadataValues) ? metadataValues.get(0) : null; } @Override public MetadataValue addMetadata(Context context, T dso, MetadataField metadataField, String language, String value) throws SQLException { - return addMetadata(context, dso, metadataField, language, Arrays.asList(value)).get(0); + List metadataValues = + addMetadata(context, dso, metadataField, language, Arrays.asList(value)); + return CollectionUtils.isNotEmpty(metadataValues) ? metadataValues.get(0) : null; } @Override From 8407c571db341d7620cfd1bf8a73dce1ed1be229 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 May 2024 09:19:25 -0500 Subject: [PATCH 222/479] Throw IllegalArgumentException if addMetadata() called without values. Add unit tests to prove it works (cherry picked from commit ed918a8d0c51e9c2071991b876f832fe85d1b8f1) --- .../content/DSpaceObjectServiceImpl.java | 21 ++++++++++++++++ .../java/org/dspace/content/ItemTest.java | 24 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java index f47ed47d4c0d..4e3fa42162ce 100644 --- a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java @@ -242,10 +242,31 @@ public List addMetadata(Context context, T dso, MetadataField met } + /** + * Add metadata value(s) to a MetadataField of a DSpace Object + * @param context current DSpace context + * @param dso DSpaceObject to modify + * @param metadataField MetadataField to add values to + * @param lang Language code to add + * @param values One or more metadata values to add + * @param authorities One or more authorities to add + * @param confidences One or more confidences to add (for authorities) + * @param placeSupplier Supplier of "place" for new metadata values + * @return List of newly added metadata values + * @throws SQLException if database error occurs + * @throws IllegalArgumentException for an empty list of values + */ public List addMetadata(Context context, T dso, MetadataField metadataField, String lang, List values, List authorities, List confidences, Supplier placeSupplier) throws SQLException { + // Throw an error if we are attempting to add empty values + if (values == null || values.isEmpty()) { + throw new IllegalArgumentException("Cannot add empty values to a new metadata field " + + metadataField.toString() + " on object with uuid = " + + dso.getID().toString() + " and type = " + getTypeText(dso)); + } + boolean authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField); boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField); List newMetadata = new ArrayList<>(); diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index ada9bbc15905..de9919f4e3d5 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -537,6 +537,17 @@ public void testAddMetadata_5args_1() throws SQLException { assertThat("testAddMetadata_5args_1 11", dc.get(1).getValue(), equalTo(values[1])); } + @Test(expected = IllegalArgumentException.class) + public void testAddMetadata_5args_no_values() throws Exception { + String schema = "dc"; + String element = "contributor"; + String qualifier = "author"; + String lang = Item.ANY; + String[] values = {}; + itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); + fail("IllegalArgumentException expected"); + } + /** * Test of addMetadata method, of class Item. */ @@ -614,6 +625,19 @@ public void testAddMetadata_7args_1_noauthority() throws SQLException { assertThat("testAddMetadata_7args_1 15", dc.get(1).getConfidence(), equalTo(-1)); } + @Test(expected = IllegalArgumentException.class) + public void testAddMetadata_7args_no_values() throws Exception { + String schema = "dc"; + String element = "contributor"; + String qualifier = "author"; + String lang = Item.ANY; + List values = new ArrayList(); + List authorities = new ArrayList(); + List confidences = new ArrayList(); + itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); + fail("IllegalArgumentException expected"); + } + /** * Test of addMetadata method, of class Item. */ From 30dafa09d0da90d55fa8898abdfb948cfcd74fa9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 May 2024 09:54:42 -0500 Subject: [PATCH 223/479] Fix testAddMetadata_5args_2 to no longer be *identical* to testAddMetadata_5args_1. It appears this second test was meant to test a different addMetadata() method which accepts a single Value instead of a List (cherry picked from commit 25f722ed980fe13a7244f90f3799efcaa9ba6f7c) --- .../test/java/org/dspace/content/ItemTest.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index de9919f4e3d5..e3337d9d92a7 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -639,7 +639,7 @@ public void testAddMetadata_7args_no_values() throws Exception { } /** - * Test of addMetadata method, of class Item. + * This is the same as testAddMetadata_5args_1 except it is adding a *single* value as a String, not a List. */ @Test public void testAddMetadata_5args_2() throws SQLException { @@ -647,24 +647,18 @@ public void testAddMetadata_5args_2() throws SQLException { String element = "contributor"; String qualifier = "author"; String lang = Item.ANY; - List values = Arrays.asList("value0", "value1"); - itemService.addMetadata(context, it, schema, element, qualifier, lang, values); + String value = "value0"; + itemService.addMetadata(context, it, schema, element, qualifier, lang, value); List dc = itemService.getMetadata(it, schema, element, qualifier, lang); assertThat("testAddMetadata_5args_2 0", dc, notNullValue()); - assertTrue("testAddMetadata_5args_2 1", dc.size() == 2); + assertTrue("testAddMetadata_5args_2 1", dc.size() == 1); assertThat("testAddMetadata_5args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), equalTo(schema)); assertThat("testAddMetadata_5args_2 3", dc.get(0).getMetadataField().getElement(), equalTo(element)); assertThat("testAddMetadata_5args_2 4", dc.get(0).getMetadataField().getQualifier(), equalTo(qualifier)); assertThat("testAddMetadata_5args_2 5", dc.get(0).getLanguage(), equalTo(lang)); - assertThat("testAddMetadata_5args_2 6", dc.get(0).getValue(), equalTo(values.get(0))); - assertThat("testAddMetadata_5args_2 7", dc.get(1).getMetadataField().getMetadataSchema().getName(), - equalTo(schema)); - assertThat("testAddMetadata_5args_2 8", dc.get(1).getMetadataField().getElement(), equalTo(element)); - assertThat("testAddMetadata_5args_2 9", dc.get(1).getMetadataField().getQualifier(), equalTo(qualifier)); - assertThat("testAddMetadata_5args_2 10", dc.get(1).getLanguage(), equalTo(lang)); - assertThat("testAddMetadata_5args_2 11", dc.get(1).getValue(), equalTo(values.get(1))); + assertThat("testAddMetadata_5args_2 6", dc.get(0).getValue(), equalTo(value)); } /** From 872af3315590a70ae72466ba8bdef7741b365489 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 May 2024 12:01:03 -0500 Subject: [PATCH 224/479] Fix bug in MetadataImport where it could call addMetadata() with empty values. Minor refactors to MetadataImportIT to make findItemByName more efficient. (cherry picked from commit f8ac8edc4970086c74e5d5f7b492199bd9bf3ba7) --- .../dspace/app/bulkedit/MetadataImport.java | 10 ++++--- .../dspace/app/bulkedit/MetadataImportIT.java | 27 ++++++++++--------- 2 files changed, 21 insertions(+), 16 deletions(-) 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 af6976acb14a..ad46cb95c353 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 @@ -825,8 +825,10 @@ protected void compareAndUpdate(Context c, Item item, String[] fromCSV, boolean addRelationships(c, item, element, values); } else { itemService.clearMetadata(c, item, schema, element, qualifier, language); - itemService.addMetadata(c, item, schema, element, qualifier, - language, values, authorities, confidences); + if (!values.isEmpty()) { + itemService.addMetadata(c, item, schema, element, qualifier, + language, values, authorities, confidences); + } itemService.update(c, item); } } @@ -1121,8 +1123,8 @@ protected void add(Context c, String[] fromCSV, String md, BulkEditChange change .getAuthoritySeparator() + dcv.getConfidence(); } - // Add it - if ((value != null) && (!"".equals(value))) { + // Add it, if value is not blank + if (value != null && StringUtils.isNotBlank(value)) { changes.registerAdd(dcv); } } 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 e50f7913ad70..ae079df560ed 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 @@ -9,12 +9,12 @@ import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; -import java.sql.SQLException; import java.util.List; import org.apache.commons.cli.ParseException; @@ -218,9 +218,10 @@ public void personMetadataImportTest() throws Exception { @Test public void metadataImportRemovingValueTest() throws Exception { - context.turnOffAuthorisationSystem(); - Item item = ItemBuilder.createItem(context,personCollection).withAuthor("TestAuthorToRemove").withTitle("title") + String itemTitle = "Testing removing author"; + Item item = ItemBuilder.createItem(context,personCollection).withAuthor("TestAuthorToRemove") + .withTitle(itemTitle) .build(); context.restoreAuthSystemState(); @@ -232,19 +233,21 @@ public void metadataImportRemovingValueTest() throws Exception { String[] csv = {"id,collection,dc.title,dc.contributor.author[*]", item.getID().toString() + "," + personCollection.getHandle() + "," + item.getName() + ","}; performImportScript(csv); - item = findItemByName("title"); + item = findItemByName(itemTitle); assertEquals(0, itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY).size()); } - private Item findItemByName(String name) throws SQLException { - Item importedItem = null; - List allItems = IteratorUtils.toList(itemService.findAll(context)); - for (Item item : allItems) { - if (item.getName().equals(name)) { - importedItem = item; - } + private Item findItemByName(String name) throws Exception { + List items = + IteratorUtils.toList(itemService.findByMetadataField(context, "dc", "title", null, name)); + + if (items != null && !items.isEmpty()) { + // Just return first matching Item. Tests should ensure name/title is unique. + return items.get(0); + } else { + fail("Could not find expected Item with dc.title = '" + name + "'"); + return null; } - return importedItem; } public void performImportScript(String[] csv) throws Exception { From c890199cfe314b2192fe098ad6ccb3ada7570687 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 May 2024 13:51:46 -0500 Subject: [PATCH 225/479] Add tests to verify behavior of addMetadata() when encountering virtual metadata (cherry picked from commit 2eb7dbca6ba349da70ccbf52f65a3b3ad70acbb2) --- .../java/org/dspace/content/ItemTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index e3337d9d92a7..aaa28769dca6 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -13,6 +13,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -638,6 +640,51 @@ public void testAddMetadata_7args_no_values() throws Exception { fail("IllegalArgumentException expected"); } + @Test + public void testAddMetadata_list_with_virtual_metadata() throws Exception { + String schema = "dc"; + String element = "contributor"; + String qualifier = "author"; + String lang = Item.ANY; + // Create two fake virtual metadata ("virtual::[relationship-id]") values + List values = new ArrayList<>(Arrays.asList("uuid-1", "uuid-2")); + List authorities = new ArrayList<>(Arrays.asList(Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1", + Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-2")); + List confidences = new ArrayList<>(Arrays.asList(-1, -1)); + + // Virtual metadata values will be IGNORED. No metadata should be added as we are calling addMetadata() + // with two virtual metadata values. + List valuesAdded = itemService.addMetadata(context, it, schema, element, qualifier, lang, + values, authorities, confidences); + assertNotNull(valuesAdded); + assertTrue(valuesAdded.isEmpty()); + + // Now, update tests values to append a third value which is NOT virtual metadata + String newValue = "new-metadata-value"; + String newAuthority = "auth0"; + Integer newConfidence = 0; + values.add(newValue); + authorities.add(newAuthority); + confidences.add(newConfidence); + + // Call addMetadata again, and this time only one value (the new, non-virtual metadata) should be added + valuesAdded = itemService.addMetadata(context, it, schema, element, qualifier, lang, + values, authorities, confidences); + assertNotNull(valuesAdded); + assertEquals(1, valuesAdded.size()); + + // Get metadata and ensure new value is the ONLY ONE for this metadata field + List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + assertNotNull(dc); + assertEquals(1, dc.size()); + assertEquals(schema, dc.get(0).getMetadataField().getMetadataSchema().getName()); + assertEquals(element, dc.get(0).getMetadataField().getElement()); + assertEquals(qualifier, dc.get(0).getMetadataField().getQualifier()); + assertEquals(newValue, dc.get(0).getValue()); + assertNull(dc.get(0).getAuthority()); + assertEquals(-1, dc.get(0).getConfidence()); + } + /** * This is the same as testAddMetadata_5args_1 except it is adding a *single* value as a String, not a List. */ @@ -720,6 +767,42 @@ public void testAddMetadata_7args_2_noauthority() throws SQLException { assertThat("testAddMetadata_7args_2 8", dc.get(0).getConfidence(), equalTo(-1)); } + @Test + public void testAddMetadata_single_virtual_metadata() throws Exception { + String schema = "dc"; + String element = "contributor"; + String qualifier = "author"; + String lang = Item.ANY; + // Create a single fake virtual metadata ("virtual::[relationship-id]") value + String value = "uuid-1"; + String authority = Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1"; + Integer confidence = -1; + + // Virtual metadata values will be IGNORED. No metadata should be added as we are calling addMetadata() + // with a virtual metadata value. + MetadataValue valuesAdded = itemService.addMetadata(context, it, schema, element, qualifier, lang, + value, authority, confidence); + // Returned object will be null when no metadata was added + assertNull(valuesAdded); + + // Verify this metadata field does NOT exist on the item + List mv = itemService.getMetadata(it, schema, element, qualifier, lang); + assertNotNull(mv); + assertTrue(mv.isEmpty()); + + // Also try calling addMetadata() with MetadataField object + MetadataField metadataField = metadataFieldService.findByElement(context, schema, element, qualifier); + valuesAdded = itemService.addMetadata(context, it, metadataField, lang, value, authority, confidence); + // Returned object should still be null + assertNull(valuesAdded); + + // Verify this metadata field does NOT exist on the item + mv = itemService.getMetadata(it, schema, element, qualifier, lang); + assertNotNull(mv); + assertTrue(mv.isEmpty()); + } + + /** * Test of clearMetadata method, of class Item. */ From 0f74cb22bf1e5eedec766cb9e8b85ff9d35d2f2e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 6 Jun 2024 11:44:28 -0500 Subject: [PATCH 226/479] Avoid inline display of HTML/JS bitstreams. Add JS to list of known formats so that it can be recognized by DSpace. (cherry picked from commit 356a0281867989cd1bf6345f9d369f9c992791c8) --- .../org/dspace/app/rest/BitstreamRestController.java | 8 +++++++- dspace/config/dspace.cfg | 5 +++++ dspace/config/registries/bitstream-formats.xml | 9 +++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index 9b5ede37c85f..e3d6076844fe 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -209,7 +209,13 @@ private boolean checkFormatForContentDisposition(BitstreamFormat format) { if (format == null) { return false; } - List formats = List.of((configurationService.getArrayProperty("webui.content_disposition_format"))); + // Default to always downloading HTML/JavaScript files. These formats can embed JavaScript which would be run + // in the user's browser when loaded inline. This could be the basis for an XSS attack. + // RTF is also added because most browsers attempt to display it as plain text. + String [] defaultFormats = { "text/html", "text/javascript", "text/richtext" }; + + List formats = List.of(configurationService.getArrayProperty("webui.content_disposition_format", + defaultFormats)); boolean download = formats.contains(format.getMIMEType()); if (!download) { for (String ext : format.getExtensions()) { diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index fc840ef00354..1d108d5c3345 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1373,6 +1373,11 @@ webui.content_disposition_threshold = 8388608 # Set which mimetypes, file extensions will NOT be opened inline # Files with these mimetypes/extensions will always be downloaded, # regardless of the threshold above +# We HIGHLY RECOMMEND forcing HTML / Javascript to always download. +# If a bitstream contained malicious Javascript, it would be executed in a user's browser when opened inline. +webui.content_disposition_format = text/html +webui.content_disposition_format = text/javascript +# RTF is always downloaded because most browsers attempt to display it as plain text. webui.content_disposition_format = text/richtext #### Multi-file HTML document/site settings ##### diff --git a/dspace/config/registries/bitstream-formats.xml b/dspace/config/registries/bitstream-formats.xml index 3515773fd742..fe0943e015e0 100644 --- a/dspace/config/registries/bitstream-formats.xml +++ b/dspace/config/registries/bitstream-formats.xml @@ -827,4 +827,13 @@ avif + + text/javascript + JavaScript + JavaScript + 1 + false + js + + From 7143c97248e89416d8b491593a546879394875a2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 6 Jun 2024 15:55:01 -0500 Subject: [PATCH 227/479] Fix failing IT by increasing number of formats by one (cherry picked from commit c61b7033f2ce63ceec6d867679216cccdaf10aec) --- .../org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java index fd128269308d..d28202af659b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java @@ -56,7 +56,7 @@ public class BitstreamFormatRestRepositoryIT extends AbstractControllerIntegrati @Autowired private BitstreamFormatConverter bitstreamFormatConverter; - private final int DEFAULT_AMOUNT_FORMATS = 85; + private final int DEFAULT_AMOUNT_FORMATS = 86; @Test public void findAllPaginationTest() throws Exception { From 7ba150f4fc8121b44587c642be71e7c37a553cc0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Jun 2024 09:58:52 -0500 Subject: [PATCH 228/479] Add XML to the list of formats to always download (cherry picked from commit 39975e45cfff8aa70c7c0896c6f1cdff8d3e6fbf) --- .../java/org/dspace/app/rest/BitstreamRestController.java | 4 ++-- dspace/config/dspace.cfg | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index e3d6076844fe..d70c0904b3c2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -209,10 +209,10 @@ private boolean checkFormatForContentDisposition(BitstreamFormat format) { if (format == null) { return false; } - // Default to always downloading HTML/JavaScript files. These formats can embed JavaScript which would be run + // Default to always downloading HTML/JavaScript/XML files. These formats can embed JavaScript which may be run // in the user's browser when loaded inline. This could be the basis for an XSS attack. // RTF is also added because most browsers attempt to display it as plain text. - String [] defaultFormats = { "text/html", "text/javascript", "text/richtext" }; + String [] defaultFormats = { "text/html", "text/javascript", "text/xml", "text/richtext" }; List formats = List.of(configurationService.getArrayProperty("webui.content_disposition_format", defaultFormats)); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 1d108d5c3345..0554d76866fd 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1373,10 +1373,11 @@ webui.content_disposition_threshold = 8388608 # Set which mimetypes, file extensions will NOT be opened inline # Files with these mimetypes/extensions will always be downloaded, # regardless of the threshold above -# We HIGHLY RECOMMEND forcing HTML / Javascript to always download. -# If a bitstream contained malicious Javascript, it would be executed in a user's browser when opened inline. +# We HIGHLY RECOMMEND forcing HTML / Javascript / XML to always download. +# If one of these bitstreams contains malicious Javascript, it may be executed in a user's browser when opened inline. webui.content_disposition_format = text/html webui.content_disposition_format = text/javascript +webui.content_disposition_format = text/xml # RTF is always downloaded because most browsers attempt to display it as plain text. webui.content_disposition_format = text/richtext From 7951c8e428819972a3c3d80fd8f1fb0ae2cd3284 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Jun 2024 10:09:33 -0500 Subject: [PATCH 229/479] Add a test to prove the default settings are to always download these formats (cherry picked from commit e6bfb833eed8228aa8f9ab66ec3277284d747b0d) --- .../app/rest/BitstreamRestControllerIT.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) 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 ae1b62c1b1b6..cef087c363f3 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 @@ -1247,7 +1247,6 @@ public void closeInputStreamsDownloadWithCoverPage() throws Exception { Mockito.verify(inputStreamSpy, times(1)).close(); } - @Test public void checkContentDispositionOfFormats() throws Exception { configurationService.setProperty("webui.content_disposition_format", new String[] { @@ -1285,6 +1284,35 @@ public void checkContentDispositionOfFormats() throws Exception { verifyBitstreamDownload(html, "text/html;charset=UTF-8", false); } + @Test + public void checkDefaultContentDispositionFormats() throws Exception { + // This test is similar to the above test, but it verifies that our *default settings* for + // webui.content_disposition_format are protecting us from loading specific formats *inline*. + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + Item item = ItemBuilder.createItem(context, collection).build(); + String content = "Test Content"; + Bitstream html; + Bitstream js; + Bitstream xml; + try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) { + html = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("text/html").build(); + js = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("text/javascript").build(); + xml = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("text/xml").build(); + } + context.restoreAuthSystemState(); + + // By default, HTML, JS & XML should all download. This protects us from possible XSS attacks, as + // each of these formats can embed JavaScript which may execute when the file is loaded *inline*. + verifyBitstreamDownload(html, "text/html;charset=UTF-8", true); + verifyBitstreamDownload(js, "text/javascript;charset=UTF-8", true); + verifyBitstreamDownload(xml, "text/xml;charset=UTF-8", true); + } + private void verifyBitstreamDownload(Bitstream file, String contentType, boolean shouldDownload) throws Exception { String token = getAuthToken(admin.getEmail(), password); String header = getClient(token).perform(get("/api/core/bitstreams/" + file.getID() + "/content") From c30ff35448fe2da77d922f3b9db20500066b343d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Jun 2024 11:51:11 -0500 Subject: [PATCH 230/479] For additional security, ensure "unknown" formats are always downloaded. Update test to prove behavior. (cherry picked from commit 6da072de9e43d52e43eb9b312446a9359638f3c7) --- .../app/rest/BitstreamRestController.java | 15 ++++++++++++--- .../app/rest/BitstreamRestControllerIT.java | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index d70c0904b3c2..b85c009c7be8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -204,10 +204,19 @@ private boolean isNotAnErrorResponse(HttpServletResponse response) { || responseCode.equals(Response.Status.Family.REDIRECTION); } + /** + * Check if a Bitstream of the specified format should always be downloaded (i.e. "content-disposition: attachment") + * or can be served inline (i.e. "content-disposition: inline"). + *

+ * NOTE that downloading via "attachment" is more secure, as the user's browser will not attempt to process or + * display the file. But, downloading via "inline" may be seen as more user-friendly for common formats. + * @param format BitstreamFormat + * @return true if always download ("attachment"). false if can be served inline ("inline") + */ private boolean checkFormatForContentDisposition(BitstreamFormat format) { - // never automatically download undefined formats - if (format == null) { - return false; + // Undefined or Unknown formats should ALWAYS be downloaded for additional security. + if (format == null || format.getSupportLevel() == BitstreamFormat.UNKNOWN) { + return true; } // Default to always downloading HTML/JavaScript/XML files. These formats can embed JavaScript which may be run // in the user's browser when loaded inline. This could be the basis for an XSS attack. 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 cef087c363f3..502429679f62 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 @@ -1296,6 +1296,7 @@ public void checkDefaultContentDispositionFormats() throws Exception { Bitstream html; Bitstream js; Bitstream xml; + Bitstream unknown; try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) { html = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("text/html").build(); @@ -1303,6 +1304,8 @@ public void checkDefaultContentDispositionFormats() throws Exception { .withMimeType("text/javascript").build(); xml = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("text/xml").build(); + unknown = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("application/octet-stream").build(); } context.restoreAuthSystemState(); @@ -1311,6 +1314,8 @@ public void checkDefaultContentDispositionFormats() throws Exception { verifyBitstreamDownload(html, "text/html;charset=UTF-8", true); verifyBitstreamDownload(js, "text/javascript;charset=UTF-8", true); verifyBitstreamDownload(xml, "text/xml;charset=UTF-8", true); + // Unknown file formats should also always download + verifyBitstreamDownload(unknown, "application/octet-stream;charset=UTF-8", true); } private void verifyBitstreamDownload(Bitstream file, String contentType, boolean shouldDownload) throws Exception { @@ -1321,11 +1326,15 @@ private void verifyBitstreamDownload(Bitstream file, String contentType, boolean .andExpect(content().contentType(contentType)) .andReturn().getResponse().getHeader("content-disposition"); if (shouldDownload) { - assertTrue(header.contains("attachment")); - assertFalse(header.contains("inline")); + assertTrue("Content-Disposition should contain 'attachment' for " + contentType, + header.contains("attachment")); + assertFalse("Content-Disposition should NOT contain 'inline' for " + contentType, + header.contains("inline")); } else { - assertTrue(header.contains("inline")); - assertFalse(header.contains("attachment")); + assertTrue("Content-Disposition should contain 'inline' for " + contentType, + header.contains("inline")); + assertFalse("Content-Disposition should NOT contain 'attachment' for " + contentType, + header.contains("attachment")); } } } From 3bcd33d92a8f3e1dab958d332b2030fc1b272d8a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Jun 2024 14:52:21 -0500 Subject: [PATCH 231/479] To avoid misconfiguration, hardcode HTML, XML, RDF, JS to download only. Add a new wildcard setting to allow sites to force all files to download only. (cherry picked from commit a091d343b9c1e491ca0fc005fd18bc1c75f8e830) --- .../app/rest/BitstreamRestController.java | 29 ++++++--- .../app/rest/BitstreamRestControllerIT.java | 60 ++++++++++++++++--- dspace/config/dspace.cfg | 17 +++--- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index b85c009c7be8..22b18724b90b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -20,6 +20,7 @@ import javax.ws.rs.core.Response; import org.apache.catalina.connector.ClientAbortException; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.converter.ConverterService; @@ -206,25 +207,37 @@ private boolean isNotAnErrorResponse(HttpServletResponse response) { /** * Check if a Bitstream of the specified format should always be downloaded (i.e. "content-disposition: attachment") - * or can be served inline (i.e. "content-disposition: inline"). + * or can be opened inline (i.e. "content-disposition: inline"). *

* NOTE that downloading via "attachment" is more secure, as the user's browser will not attempt to process or * display the file. But, downloading via "inline" may be seen as more user-friendly for common formats. * @param format BitstreamFormat - * @return true if always download ("attachment"). false if can be served inline ("inline") + * @return true if always download ("attachment"). false if can be opened inline ("inline") */ private boolean checkFormatForContentDisposition(BitstreamFormat format) { // Undefined or Unknown formats should ALWAYS be downloaded for additional security. if (format == null || format.getSupportLevel() == BitstreamFormat.UNKNOWN) { return true; } - // Default to always downloading HTML/JavaScript/XML files. These formats can embed JavaScript which may be run - // in the user's browser when loaded inline. This could be the basis for an XSS attack. - // RTF is also added because most browsers attempt to display it as plain text. - String [] defaultFormats = { "text/html", "text/javascript", "text/xml", "text/richtext" }; - List formats = List.of(configurationService.getArrayProperty("webui.content_disposition_format", - defaultFormats)); + // Load additional formats configured to require download + List configuredFormats = List.of(configurationService. + getArrayProperty("webui.content_disposition_format")); + + // If configuration includes "*", then all formats will always be downloaded. + if (configuredFormats.contains("*")) { + return true; + } + + // Define a download list of formats which DSpace forces to ALWAYS be downloaded. + // These formats can embed JavaScript which may be run in the user's browser if the file is opened inline. + // Therefore, DSpace blocks opening these formats inline as it could be used for an XSS attack. + List downloadOnlyFormats = List.of("text/html", "text/javascript", "text/xml", "rdf"); + + // Combine our two lists + List formats = ListUtils.union(downloadOnlyFormats, configuredFormats); + + // See if the passed in format's MIME type or file extension is listed. boolean download = formats.contains(format.getMIMEType()); if (!download) { for (String ext : format.getExtensions()) { 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 502429679f62..855fedbaa2ab 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 @@ -1263,7 +1263,7 @@ public void checkContentDispositionOfFormats() throws Exception { Bitstream rtf; Bitstream xml; Bitstream txt; - Bitstream html; + Bitstream csv; try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) { rtf = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("text/richtext").build(); @@ -1271,8 +1271,8 @@ public void checkContentDispositionOfFormats() throws Exception { .withMimeType("text/xml").build(); txt = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("text/plain").build(); - html = BitstreamBuilder.createBitstream(context, item, is) - .withMimeType("text/html").build(); + csv = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("text/csv").build(); } context.restoreAuthSystemState(); @@ -1281,12 +1281,12 @@ public void checkContentDispositionOfFormats() throws Exception { verifyBitstreamDownload(xml, "text/xml;charset=UTF-8", true); verifyBitstreamDownload(txt, "text/plain;charset=UTF-8", true); // this format is not configured and should open inline - verifyBitstreamDownload(html, "text/html;charset=UTF-8", false); + verifyBitstreamDownload(csv, "text/csv;charset=UTF-8", false); } @Test - public void checkDefaultContentDispositionFormats() throws Exception { - // This test is similar to the above test, but it verifies that our *default settings* for + public void checkHardcodedContentDispositionFormats() throws Exception { + // This test is similar to the above test, but it verifies that our *hardcoded settings* for // webui.content_disposition_format are protecting us from loading specific formats *inline*. context.turnOffAuthorisationSystem(); Community community = CommunityBuilder.createCommunity(context).build(); @@ -1295,17 +1295,25 @@ public void checkDefaultContentDispositionFormats() throws Exception { String content = "Test Content"; Bitstream html; Bitstream js; + Bitstream rdf; Bitstream xml; Bitstream unknown; + Bitstream svg; try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) { html = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("text/html").build(); js = BitstreamBuilder.createBitstream(context, item, is) - .withMimeType("text/javascript").build(); + .withMimeType("text/javascript").build(); + // NOTE: RDF has a MIME Type with a "charset" in our bitstream-formats.xml + rdf = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("application/rdf+xml; charset=utf-8").build(); xml = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("text/xml").build(); unknown = BitstreamBuilder.createBitstream(context, item, is) .withMimeType("application/octet-stream").build(); + svg = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("image/svg+xml").build(); + } context.restoreAuthSystemState(); @@ -1313,11 +1321,49 @@ public void checkDefaultContentDispositionFormats() throws Exception { // each of these formats can embed JavaScript which may execute when the file is loaded *inline*. verifyBitstreamDownload(html, "text/html;charset=UTF-8", true); verifyBitstreamDownload(js, "text/javascript;charset=UTF-8", true); + verifyBitstreamDownload(rdf, "application/rdf+xml;charset=UTF-8", true); verifyBitstreamDownload(xml, "text/xml;charset=UTF-8", true); // Unknown file formats should also always download verifyBitstreamDownload(unknown, "application/octet-stream;charset=UTF-8", true); + // SVG does NOT currently exist as a recognized format in DSpace's bitstream-formats.xml, but it's another + // format that allows embedded JavaScript. This test is a reminder that SVGs should NOT be opened inline. + verifyBitstreamDownload(svg, "application/octet-stream;charset=UTF-8", true); + } + + @Test + public void checkWildcardContentDispositionFormats() throws Exception { + // Setting "*" should result in all formats being downloaded (nothing will be opened inline) + configurationService.setProperty("webui.content_disposition_format", "*"); + + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + Item item = ItemBuilder.createItem(context, collection).build(); + String content = "Test Content"; + Bitstream csv; + Bitstream jpg; + Bitstream mpg; + Bitstream pdf; + try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) { + csv = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("text/csv").build(); + jpg = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("image/jpeg").build(); + mpg = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("video/mpeg").build(); + pdf = BitstreamBuilder.createBitstream(context, item, is) + .withMimeType("application/pdf").build(); + } + context.restoreAuthSystemState(); + + // All formats should be download only + verifyBitstreamDownload(csv, "text/csv;charset=UTF-8", true); + verifyBitstreamDownload(jpg, "image/jpeg;charset=UTF-8", true); + verifyBitstreamDownload(mpg, "video/mpeg;charset=UTF-8", true); + verifyBitstreamDownload(pdf, "application/pdf;charset=UTF-8", true); } + private void verifyBitstreamDownload(Bitstream file, String contentType, boolean shouldDownload) throws Exception { String token = getAuthToken(admin.getEmail(), password); String header = getClient(token).perform(get("/api/core/bitstreams/" + file.getID() + "/content") diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 0554d76866fd..c21fb54655b8 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1370,15 +1370,14 @@ webui.content_disposition_threshold = 8388608 #### Content Attachment Disposition Formats #### # -# Set which mimetypes, file extensions will NOT be opened inline -# Files with these mimetypes/extensions will always be downloaded, -# regardless of the threshold above -# We HIGHLY RECOMMEND forcing HTML / Javascript / XML to always download. -# If one of these bitstreams contains malicious Javascript, it may be executed in a user's browser when opened inline. -webui.content_disposition_format = text/html -webui.content_disposition_format = text/javascript -webui.content_disposition_format = text/xml -# RTF is always downloaded because most browsers attempt to display it as plain text. +# Set which mimetypes or file extensions will NOT be opened inline. +# Files with these mimetypes/extensions will always be downloaded, regardless of the threshold above. +# NOTE: For security reasons, some file formats (e.g. HTML, XML, RDF, JS) will always be downloaded regardless +# of the settings here. This blocks these formats from executing embedded JavaScript when opened inline. +# For additional security, you may choose to set this to "*" to force all formats to always be downloaded +# (i.e. disables all formats from opening inline within the user's browser). +# +# By default, RTF is always downloaded because most browsers attempt to display it as plain text. webui.content_disposition_format = text/richtext #### Multi-file HTML document/site settings ##### From 2cb8204d3c474ce9a7cb6946c43a0beab0ba92a2 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Mon, 8 Jul 2024 12:33:07 -0500 Subject: [PATCH 232/479] updated creative commons licenses version from 3.0 to 4.0 --- .../rest/submit/factory/impl/CCLicenseAddPatchOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java index 602ae5e9690b..80624257e6ce 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java @@ -26,7 +26,7 @@ * Example: * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/31599 -H "Content-Type: * application/json" -d '[{ "op": "add", "path": "/sections/cclicense/uri", - * "value":"https://creativecommons.org/licenses/by-nc-sa/3.0/us/"}]' + * "value":"https://creativecommons.org/licenses/by-nc-sa/4.0/us/"}]' * */ public class CCLicenseAddPatchOperation extends AddPatchOperation { From 12c3cc5b3eb6c4d0339595b0efa236e16075b0dd Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Jul 2024 13:35:16 -0500 Subject: [PATCH 233/479] Update LICENSES_THIRD_PARTY (and related configs) for 7.6.2 release --- LICENSES_THIRD_PARTY | 610 +++++++++--------- pom.xml | 6 +- .../license/LICENSES_THIRD_PARTY.properties | 85 +-- 3 files changed, 326 insertions(+), 375 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index e494c80c5d6e..791009e4c3ae 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -26,24 +26,23 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * AWS Java SDK for Amazon S3 (com.amazonaws:aws-java-sdk-s3:1.12.261 - https://aws.amazon.com/sdkforjava) * JMES Path Query library (com.amazonaws:jmespath-java:1.12.261 - https://aws.amazon.com/sdkforjava) * HPPC Collections (com.carrotsearch:hppc:0.8.1 - http://labs.carrotsearch.com/hppc.html/hppc) - * com.drewnoakes:metadata-extractor (com.drewnoakes:metadata-extractor:2.18.0 - https://drewnoakes.com/code/exif/) + * com.drewnoakes:metadata-extractor (com.drewnoakes:metadata-extractor:2.19.0 - https://drewnoakes.com/code/exif/) * parso (com.epam:parso:2.0.14 - https://github.com/epam/parso) - * Esri Geometry API for Java (com.esri.geometry:esri-geometry-api:2.2.0 - https://github.com/Esri/geometry-api-java) - * ClassMate (com.fasterxml:classmate:1.3.0 - http://github.com/cowtowncoder/java-classmate) - * Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.13.4 - http://github.com/FasterXML/jackson) - * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.13.4 - https://github.com/FasterXML/jackson-core) - * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.13.4.2 - http://github.com/FasterXML/jackson) + * ClassMate (com.fasterxml:classmate:1.6.0 - https://github.com/FasterXML/java-classmate) + * Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.16.0 - https://github.com/FasterXML/jackson) + * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.16.0 - https://github.com/FasterXML/jackson-core) + * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.16.0 - https://github.com/FasterXML/jackson) * Jackson dataformat: CBOR (com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.12.6 - http://github.com/FasterXML/jackson-dataformats-binary) - * Jackson dataformat: Smile (com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.13.3 - http://github.com/FasterXML/jackson-dataformats-binary) + * Jackson dataformat: Smile (com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.15.2 - https://github.com/FasterXML/jackson-dataformats-binary) * Jackson-dataformat-YAML (com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.1 - https://github.com/FasterXML/jackson-dataformats-text) * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.5 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.5 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.13.5 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * Java UUID Generator (com.fasterxml.uuid:java-uuid-generator:4.0.1 - https://github.com/cowtowncoder/java-uuid-generator) - * Woodstox (com.fasterxml.woodstox:woodstox-core:6.2.4 - https://github.com/FasterXML/woodstox) - * zjsonpatch (com.flipkart.zjsonpatch:zjsonpatch:0.4.6 - https://github.com/flipkart-incubator/zjsonpatch/) - * Caffeine cache (com.github.ben-manes.caffeine:caffeine:2.9.2 - https://github.com/ben-manes/caffeine) + * Woodstox (com.fasterxml.woodstox:woodstox-core:6.5.1 - https://github.com/FasterXML/woodstox) + * zjsonpatch (com.flipkart.zjsonpatch:zjsonpatch:0.4.16 - https://github.com/flipkart-incubator/zjsonpatch/) + * Caffeine cache (com.github.ben-manes.caffeine:caffeine:2.9.3 - https://github.com/ben-manes/caffeine) * btf (com.github.java-json-tools:btf:1.3 - https://github.com/java-json-tools/btf) * jackson-coreutils (com.github.java-json-tools:jackson-coreutils:2.0 - https://github.com/java-json-tools/jackson-coreutils) * jackson-coreutils-equivalence (com.github.java-json-tools:jackson-coreutils-equivalence:1.0 - https://github.com/java-json-tools/jackson-coreutils) @@ -54,7 +53,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * JCIP Annotations under Apache License (com.github.stephenc.jcip:jcip-annotations:1.0-1 - http://stephenc.github.com/jcip-annotations) * Google APIs Client Library for Java (com.google.api-client:google-api-client:1.23.0 - https://github.com/google/google-api-java-client/google-api-client) * Google Analytics API v3-rev145-1.23.0 (com.google.apis:google-api-services-analytics:v3-rev145-1.23.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-analytics) - * FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.1 - http://findbugs.sourceforge.net/) + * FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/) * Gson (com.google.code.gson:gson:2.9.0 - https://github.com/google/gson/gson) * error-prone annotations (com.google.errorprone:error_prone_annotations:2.18.0 - https://errorprone.info/error_prone_annotations) * Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess) @@ -64,25 +63,24 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Google HTTP Client Library for Java (com.google.http-client:google-http-client:1.23.0 - https://github.com/google/google-http-java-client/google-http-client) * GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:1.41.7 - https://github.com/googleapis/google-http-java-client/google-http-client-gson) * Jackson 2 extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-jackson2:1.23.0 - https://github.com/google/google-http-java-client/google-http-client-jackson2) + * J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/) * J2ObjC Annotations (com.google.j2objc:j2objc-annotations:2.8 - https://github.com/google/j2objc/) * Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.33.3 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client) * ConcurrentLinkedHashMap (com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2 - http://code.google.com/p/concurrentlinkedhashmap) * libphonenumber (com.googlecode.libphonenumber:libphonenumber:8.11.1 - https://github.com/google/libphonenumber/) - * Jackcess (com.healthmarketscience.jackcess:jackcess:4.0.2 - https://jackcess.sourceforge.io) - * Jackcess Encrypt (com.healthmarketscience.jackcess:jackcess-encrypt:4.0.1 - http://jackcessencrypt.sf.net) - * project ':json-path' (com.jayway.jsonpath:json-path:2.6.0 - https://github.com/jayway/JsonPath) - * project ':json-path-assert' (com.jayway.jsonpath:json-path-assert:2.6.0 - https://github.com/jayway/JsonPath) + * Jackcess (com.healthmarketscience.jackcess:jackcess:4.0.5 - https://jackcess.sourceforge.io) + * Jackcess Encrypt (com.healthmarketscience.jackcess:jackcess-encrypt:4.0.2 - http://jackcessencrypt.sf.net) + * json-path (com.jayway.jsonpath:json-path:2.9.0 - https://github.com/jayway/JsonPath) + * json-path-assert (com.jayway.jsonpath:json-path-assert:2.9.0 - https://github.com/jayway/JsonPath) * Disruptor Framework (com.lmax:disruptor:3.4.2 - http://lmax-exchange.github.com/disruptor) - * builder-commons (com.lyncode:builder-commons:1.0.2 - http://nexus.sonatype.org/oss-repository-hosting.html/builder-commons) - * MaxMind DB Reader (com.maxmind.db:maxmind-db:1.2.2 - http://dev.maxmind.com/) - * MaxMind GeoIP2 API (com.maxmind.geoip2:geoip2:2.11.0 - http://dev.maxmind.com/geoip/geoip2/web-services) + * MaxMind DB Reader (com.maxmind.db:maxmind-db:2.1.0 - http://dev.maxmind.com/) + * MaxMind GeoIP2 API (com.maxmind.geoip2:geoip2:2.17.0 - https://dev.maxmind.com/geoip?lang=en) * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:7.9 - https://bitbucket.org/connect2id/nimbus-jose-jwt) - * opencsv (com.opencsv:opencsv:5.6 - http://opencsv.sf.net) + * opencsv (com.opencsv:opencsv:5.9 - http://opencsv.sf.net) * java-libpst (com.pff:java-libpst:0.9.3 - https://github.com/rjohnsondev/java-libpst) * rome (com.rometools:rome:1.19.0 - http://rometools.com/rome) * rome-modules (com.rometools:rome-modules:1.19.0 - http://rometools.com/rome-modules) * rome-utils (com.rometools:rome-utils:1.19.0 - http://rometools.com/rome-utils) - * fastinfoset (com.sun.xml.fastinfoset:FastInfoset:1.2.15 - http://fi.java.net) * T-Digest (com.tdunning:t-digest:3.1 - https://github.com/tdunning/t-digest) * config (com.typesafe:config:1.3.3 - https://github.com/lightbend/config) * ssl-config-core (com.typesafe:ssl-config-core_2.13:0.3.8 - https://github.com/lightbend/ssl-config) @@ -94,17 +92,17 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * akka-stream (com.typesafe.akka:akka-stream_2.13:2.5.31 - https://akka.io/) * scala-logging (com.typesafe.scala-logging:scala-logging_2.13:3.9.2 - https://github.com/lightbend/scala-logging) * JSON library from Android SDK (com.vaadin.external.google:android-json:0.0.20131108.vaadin1 - http://developer.android.com/sdk) - * SparseBitSet (com.zaxxer:SparseBitSet:1.2 - https://github.com/brettwooldridge/SparseBitSet) + * SparseBitSet (com.zaxxer:SparseBitSet:1.3 - https://github.com/brettwooldridge/SparseBitSet) * Apache Commons BeanUtils (commons-beanutils:commons-beanutils:1.9.4 - https://commons.apache.org/proper/commons-beanutils/) - * Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/) - * Apache Commons Codec (commons-codec:commons-codec:1.10 - http://commons.apache.org/proper/commons-codec/) + * Apache Commons CLI (commons-cli:commons-cli:1.6.0 - https://commons.apache.org/proper/commons-cli/) + * Apache Commons Codec (commons-codec:commons-codec:1.16.0 - https://commons.apache.org/proper/commons-codec/) * Apache Commons Collections (commons-collections:commons-collections:3.2.2 - http://commons.apache.org/collections/) - * Commons Digester (commons-digester:commons-digester:1.8.1 - http://commons.apache.org/digester/) + * Commons Digester (commons-digester:commons-digester:2.1 - http://commons.apache.org/digester/) * Apache Commons FileUpload (commons-fileupload:commons-fileupload:1.5 - https://commons.apache.org/proper/commons-fileupload/) - * Apache Commons IO (commons-io:commons-io:2.7 - https://commons.apache.org/proper/commons-io/) + * Apache Commons IO (commons-io:commons-io:2.15.1 - https://commons.apache.org/proper/commons-io/) * Commons Lang (commons-lang:commons-lang:2.6 - http://commons.apache.org/lang/) - * Apache Commons Logging (commons-logging:commons-logging:1.2 - http://commons.apache.org/proper/commons-logging/) - * Apache Commons Validator (commons-validator:commons-validator:1.5.0 - http://commons.apache.org/proper/commons-validator/) + * Apache Commons Logging (commons-logging:commons-logging:1.3.0 - https://commons.apache.org/proper/commons-logging/) + * Apache Commons Validator (commons-validator:commons-validator:1.7 - http://commons.apache.org/proper/commons-validator/) * GeoJson POJOs for Jackson (de.grundid.opendatalab:geojson-jackson:1.14 - https://github.com/opendatalab-de/geojson-jackson) * OpenAIRE Funders Model (eu.openaire:funders-model:2.0.0 - https://api.openaire.eu) * Metrics Core (io.dropwizard.metrics:metrics-core:4.1.5 - https://metrics.dropwizard.io/metrics-core) @@ -112,18 +110,24 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Metrics Integration for Jetty 9.3 and higher (io.dropwizard.metrics:metrics-jetty9:4.1.5 - https://metrics.dropwizard.io/metrics-jetty9) * Metrics Integration with JMX (io.dropwizard.metrics:metrics-jmx:4.1.5 - https://metrics.dropwizard.io/metrics-jmx) * JVM Integration for Metrics (io.dropwizard.metrics:metrics-jvm:4.1.5 - https://metrics.dropwizard.io/metrics-jvm) - * micrometer-core (io.micrometer:micrometer-core:1.9.11 - https://github.com/micrometer-metrics/micrometer) - * Netty/Buffer (io.netty:netty-buffer:4.1.68.Final - https://netty.io/netty-buffer/) - * Netty/Codec (io.netty:netty-codec:4.1.68.Final - https://netty.io/netty-codec/) + * micrometer-core (io.micrometer:micrometer-core:1.9.17 - https://github.com/micrometer-metrics/micrometer) + * Netty/Buffer (io.netty:netty-buffer:4.1.106.Final - https://netty.io/netty-buffer/) + * Netty/Buffer (io.netty:netty-buffer:4.1.99.Final - https://netty.io/netty-buffer/) + * Netty/Codec (io.netty:netty-codec:4.1.106.Final - https://netty.io/netty-codec/) + * Netty/Codec (io.netty:netty-codec:4.1.99.Final - https://netty.io/netty-codec/) * Netty/Codec/HTTP (io.netty:netty-codec-http:4.1.53.Final - https://netty.io/netty-codec-http/) * Netty/Codec/Socks (io.netty:netty-codec-socks:4.1.53.Final - https://netty.io/netty-codec-socks/) - * Netty/Common (io.netty:netty-common:4.1.68.Final - https://netty.io/netty-common/) - * Netty/Handler (io.netty:netty-handler:4.1.68.Final - https://netty.io/netty-handler/) + * Netty/Common (io.netty:netty-common:4.1.106.Final - https://netty.io/netty-common/) + * Netty/Common (io.netty:netty-common:4.1.99.Final - https://netty.io/netty-common/) + * Netty/Handler (io.netty:netty-handler:4.1.106.Final - https://netty.io/netty-handler/) + * Netty/Handler (io.netty:netty-handler:4.1.99.Final - https://netty.io/netty-handler/) * Netty/Handler/Proxy (io.netty:netty-handler-proxy:4.1.53.Final - https://netty.io/netty-handler-proxy/) - * Netty/Resolver (io.netty:netty-resolver:4.1.68.Final - https://netty.io/netty-resolver/) - * Netty/Transport (io.netty:netty-transport:4.1.68.Final - https://netty.io/netty-transport/) - * Netty/Transport/Native/Epoll (io.netty:netty-transport-native-epoll:4.1.68.Final - https://netty.io/netty-transport-native-epoll/) - * Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.1.68.Final - https://netty.io/netty-transport-native-unix-common/) + * Netty/Resolver (io.netty:netty-resolver:4.1.99.Final - https://netty.io/netty-resolver/) + * Netty/Transport (io.netty:netty-transport:4.1.106.Final - https://netty.io/netty-transport/) + * Netty/Transport (io.netty:netty-transport:4.1.99.Final - https://netty.io/netty-transport/) + * Netty/Transport/Native/Epoll (io.netty:netty-transport-native-epoll:4.1.99.Final - https://netty.io/netty-transport-native-epoll/) + * Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.1.106.Final - https://netty.io/netty-transport-native-unix-common/) + * Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.1.99.Final - https://netty.io/netty-transport-native-unix-common/) * OpenTracing API (io.opentracing:opentracing-api:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-api) * OpenTracing-noop (io.opentracing:opentracing-noop:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-noop) * OpenTracing-util (io.opentracing:opentracing-util:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-util) @@ -141,52 +145,57 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * swagger-parser-v2-converter (io.swagger.parser.v3:swagger-parser-v2-converter:2.0.23 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-v2-converter) * swagger-parser-v3 (io.swagger.parser.v3:swagger-parser-v3:2.0.23 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-v3) * Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:2.0.2 - https://beanvalidation.org) - * JSR107 API and SPI (javax.cache:cache-api:1.1.0 - https://github.com/jsr107/jsr107spec) + * JSR107 API and SPI (javax.cache:cache-api:1.1.1 - https://github.com/jsr107/jsr107spec) * javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/) * Bean Validation API (javax.validation:validation-api:2.0.1.Final - http://beanvalidation.org) * jdbm (jdbm:jdbm:1.0 - no url defined) - * Joda-Time (joda-time:joda-time:2.9.2 - http://www.joda.org/joda-time/) + * Joda-Time (joda-time:joda-time:2.12.5 - https://www.joda.org/joda-time/) * Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.11.13 - https://bytebuddy.net/byte-buddy) * Byte Buddy agent (net.bytebuddy:byte-buddy-agent:1.11.13 - https://bytebuddy.net/byte-buddy-agent) * eigenbase-properties (net.hydromatic:eigenbase-properties:1.1.5 - http://github.com/julianhyde/eigenbase-properties) * json-unit-core (net.javacrumbs.json-unit:json-unit-core:2.19.0 - https://github.com/lukas-krecan/JsonUnit/json-unit-core) * "Java Concurrency in Practice" book annotations (net.jcip:jcip-annotations:1.0 - http://jcip.net/) * ASM based accessors helper used by json-smart (net.minidev:accessors-smart:1.2 - http://www.minidev.net/) - * ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.4.7 - https://urielch.github.io/) + * ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.5.0 - https://urielch.github.io/) * JSON Small and Fast Parser (net.minidev:json-smart:2.3 - http://www.minidev.net/) - * JSON Small and Fast Parser (net.minidev:json-smart:2.4.7 - https://urielch.github.io/) + * JSON Small and Fast Parser (net.minidev:json-smart:2.5.0 - https://urielch.github.io/) * Abdera Core (org.apache.abdera:abdera-core:1.1.3 - http://abdera.apache.org/abdera-core) * I18N Libraries (org.apache.abdera:abdera-i18n:1.1.3 - http://abdera.apache.org) - * Apache Ant Core (org.apache.ant:ant:1.10.11 - https://ant.apache.org/) - * Apache Ant Launcher (org.apache.ant:ant-launcher:1.10.11 - https://ant.apache.org/) - * Apache Commons BCEL (org.apache.bcel:bcel:6.6.0 - https://commons.apache.org/proper/commons-bcel) - * Calcite Core (org.apache.calcite:calcite-core:1.27.0 - https://calcite.apache.org) - * Calcite Linq4j (org.apache.calcite:calcite-linq4j:1.27.0 - https://calcite.apache.org) - * Apache Calcite Avatica (org.apache.calcite.avatica:avatica-core:1.18.0 - https://calcite.apache.org/avatica) - * Apache Commons Collections (org.apache.commons:commons-collections4:4.1 - http://commons.apache.org/proper/commons-collections/) - * Apache Commons Compress (org.apache.commons:commons-compress:1.21 - https://commons.apache.org/proper/commons-compress/) - * Apache Commons Configuration (org.apache.commons:commons-configuration2:2.8.0 - https://commons.apache.org/proper/commons-configuration/) - * Apache Commons CSV (org.apache.commons:commons-csv:1.9.0 - https://commons.apache.org/proper/commons-csv/) - * Apache Commons DBCP (org.apache.commons:commons-dbcp2:2.9.0 - https://commons.apache.org/dbcp/) + * Apache Ant Core (org.apache.ant:ant:1.10.14 - https://ant.apache.org/) + * Apache Ant Launcher (org.apache.ant:ant-launcher:1.10.14 - https://ant.apache.org/) + * Apache Commons BCEL (org.apache.bcel:bcel:6.7.0 - https://commons.apache.org/proper/commons-bcel) + * Calcite Core (org.apache.calcite:calcite-core:1.35.0 - https://calcite.apache.org) + * Calcite Linq4j (org.apache.calcite:calcite-linq4j:1.35.0 - https://calcite.apache.org) + * Apache Calcite Avatica (org.apache.calcite.avatica:avatica-core:1.23.0 - https://calcite.apache.org/avatica) + * Apache Calcite Avatica Metrics (org.apache.calcite.avatica:avatica-metrics:1.23.0 - https://calcite.apache.org/avatica) + * Apache Commons Collections (org.apache.commons:commons-collections4:4.4 - https://commons.apache.org/proper/commons-collections/) + * Apache Commons Compress (org.apache.commons:commons-compress:1.26.0 - https://commons.apache.org/proper/commons-compress/) + * Apache Commons Configuration (org.apache.commons:commons-configuration2:2.10.1 - https://commons.apache.org/proper/commons-configuration/) + * Apache Commons CSV (org.apache.commons:commons-csv:1.10.0 - https://commons.apache.org/proper/commons-csv/) + * Apache Commons DBCP (org.apache.commons:commons-dbcp2:2.11.0 - https://commons.apache.org/dbcp/) * Apache Commons Exec (org.apache.commons:commons-exec:1.3 - http://commons.apache.org/proper/commons-exec/) - * Apache Commons Lang (org.apache.commons:commons-lang3:3.12.0 - https://commons.apache.org/proper/commons-lang/) + * Apache Commons Exec (org.apache.commons:commons-exec:1.4.0 - https://commons.apache.org/proper/commons-exec/) + * Apache Commons Lang (org.apache.commons:commons-lang3:3.14.0 - https://commons.apache.org/proper/commons-lang/) * Apache Commons Math (org.apache.commons:commons-math3:3.6.1 - http://commons.apache.org/proper/commons-math/) - * Apache Commons Pool (org.apache.commons:commons-pool2:2.11.1 - https://commons.apache.org/proper/commons-pool/) + * Apache Commons Pool (org.apache.commons:commons-pool2:2.12.0 - https://commons.apache.org/proper/commons-pool/) * Apache Commons Text (org.apache.commons:commons-text:1.10.0 - https://commons.apache.org/proper/commons-text) * Curator Client (org.apache.curator:curator-client:2.13.0 - http://curator.apache.org/curator-client) * Curator Framework (org.apache.curator:curator-framework:2.13.0 - http://curator.apache.org/curator-framework) * Curator Recipes (org.apache.curator:curator-recipes:2.13.0 - http://curator.apache.org/curator-recipes) - * Apache Hadoop Annotations (org.apache.hadoop:hadoop-annotations:3.2.2 - no url defined) - * Apache Hadoop Auth (org.apache.hadoop:hadoop-auth:3.2.2 - no url defined) - * Apache Hadoop Common (org.apache.hadoop:hadoop-common:3.2.2 - no url defined) - * Apache Hadoop HDFS Client (org.apache.hadoop:hadoop-hdfs-client:3.2.2 - no url defined) + * Apache Hadoop Annotations (org.apache.hadoop:hadoop-annotations:3.2.4 - no url defined) + * Apache Hadoop Auth (org.apache.hadoop:hadoop-auth:3.2.4 - no url defined) + * Apache Hadoop Common (org.apache.hadoop:hadoop-common:3.2.4 - no url defined) + * Apache Hadoop HDFS Client (org.apache.hadoop:hadoop-hdfs-client:3.2.4 - no url defined) * htrace-core4 (org.apache.htrace:htrace-core4:4.1.0-incubating - http://incubator.apache.org/projects/htrace.html) - * Apache HttpClient (org.apache.httpcomponents:httpclient:4.5.13 - http://hc.apache.org/httpcomponents-client) + * Apache HttpClient (org.apache.httpcomponents:httpclient:4.5.14 - http://hc.apache.org/httpcomponents-client-ga) * Apache HttpClient Cache (org.apache.httpcomponents:httpclient-cache:4.2.6 - http://hc.apache.org/httpcomponents-client) - * Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.15 - http://hc.apache.org/httpcomponents-core-ga) - * Apache HttpClient Mime (org.apache.httpcomponents:httpmime:4.5.13 - http://hc.apache.org/httpcomponents-client) - * Apache James :: Mime4j :: Core (org.apache.james:apache-mime4j-core:0.8.4 - http://james.apache.org/mime4j/apache-mime4j-core) - * Apache James :: Mime4j :: DOM (org.apache.james:apache-mime4j-dom:0.8.4 - http://james.apache.org/mime4j/apache-mime4j-dom) + * Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga) + * Apache HttpClient Mime (org.apache.httpcomponents:httpmime:4.5.14 - http://hc.apache.org/httpcomponents-client-ga) + * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.1.3 - https://hc.apache.org/httpcomponents-client-5.0.x/5.1.3/httpclient5/) + * Apache HttpComponents Core HTTP/1.1 (org.apache.httpcomponents.core5:httpcore5:5.1.3 - https://hc.apache.org/httpcomponents-core-5.1.x/5.1.3/httpcore5/) + * Apache HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.1.3 - https://hc.apache.org/httpcomponents-core-5.1.x/5.1.3/httpcore5-h2/) + * Apache James :: Mime4j :: Core (org.apache.james:apache-mime4j-core:0.8.10 - http://james.apache.org/mime4j/apache-mime4j-core) + * Apache James :: Mime4j :: DOM (org.apache.james:apache-mime4j-dom:0.8.11 - http://james.apache.org/mime4j/apache-mime4j-dom) * Apache Jena - Libraries POM (org.apache.jena:apache-jena-libs:2.13.0 - http://jena.apache.org/apache-jena-libs/) * Apache Jena - ARQ (SPARQL 1.1 Query Engine) (org.apache.jena:jena-arq:2.13.0 - http://jena.apache.org/jena-arq/) * Apache Jena - Core (org.apache.jena:jena-core:2.13.0 - http://jena.apache.org/jena-core/) @@ -196,86 +205,86 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Kerby-kerb Util (org.apache.kerby:kerb-util:1.0.1 - http://directory.apache.org/kerby/kerby-kerb/kerb-util) * Kerby ASN1 Project (org.apache.kerby:kerby-asn1:1.0.1 - http://directory.apache.org/kerby/kerby-common/kerby-asn1) * Kerby PKIX Project (org.apache.kerby:kerby-pkix:1.0.1 - http://directory.apache.org/kerby/kerby-pkix) - * Apache Log4j 1.x Compatibility API (org.apache.logging.log4j:log4j-1.2-api:2.20.0 - https://logging.apache.org/log4j/2.x/log4j-1.2-api/) - * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.20.0 - https://logging.apache.org/log4j/2.x/log4j-api/) - * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.20.0 - https://logging.apache.org/log4j/2.x/log4j-core/) - * Apache Log4j JUL Adapter (org.apache.logging.log4j:log4j-jul:2.20.0 - https://logging.apache.org/log4j/2.x/log4j-jul/) - * Apache Log4j Layout for JSON template (org.apache.logging.log4j:log4j-layout-template-json:2.17.1 - https://logging.apache.org/log4j/2.x/log4j-layout-template-json/) - * Apache Log4j SLF4J Binding (org.apache.logging.log4j:log4j-slf4j-impl:2.20.0 - https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/) - * Apache Log4j Web (org.apache.logging.log4j:log4j-web:2.20.0 - https://logging.apache.org/log4j/2.x/log4j-web/) - * Lucene Common Analyzers (org.apache.lucene:lucene-analyzers-common:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-common) - * Lucene ICU Analysis Components (org.apache.lucene:lucene-analyzers-icu:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-icu) - * Lucene Kuromoji Japanese Morphological Analyzer (org.apache.lucene:lucene-analyzers-kuromoji:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-kuromoji) - * Lucene Nori Korean Morphological Analyzer (org.apache.lucene:lucene-analyzers-nori:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-nori) - * Lucene Phonetic Filters (org.apache.lucene:lucene-analyzers-phonetic:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-phonetic) - * Lucene Smart Chinese Analyzer (org.apache.lucene:lucene-analyzers-smartcn:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-smartcn) - * Lucene Stempel Analyzer (org.apache.lucene:lucene-analyzers-stempel:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-analyzers-stempel) - * Lucene Memory (org.apache.lucene:lucene-backward-codecs:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-backward-codecs) - * Lucene Classification (org.apache.lucene:lucene-classification:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-classification) - * Lucene codecs (org.apache.lucene:lucene-codecs:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-codecs) - * Lucene Core (org.apache.lucene:lucene-core:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-core) - * Lucene Expressions (org.apache.lucene:lucene-expressions:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-expressions) - * Lucene Grouping (org.apache.lucene:lucene-grouping:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-grouping) - * Lucene Highlighter (org.apache.lucene:lucene-highlighter:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-highlighter) - * Lucene Join (org.apache.lucene:lucene-join:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-join) - * Lucene Memory (org.apache.lucene:lucene-memory:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-memory) - * Lucene Miscellaneous (org.apache.lucene:lucene-misc:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-misc) - * Lucene Queries (org.apache.lucene:lucene-queries:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-queries) - * Lucene QueryParsers (org.apache.lucene:lucene-queryparser:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-queryparser) - * Lucene Sandbox (org.apache.lucene:lucene-sandbox:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-sandbox) - * Lucene Spatial Extras (org.apache.lucene:lucene-spatial-extras:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-spatial-extras) - * Lucene Spatial 3D (org.apache.lucene:lucene-spatial3d:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-spatial3d) - * Lucene Suggest (org.apache.lucene:lucene-suggest:8.11.2 - https://lucene.apache.org/lucene-parent/lucene-suggest) - * Apache FontBox (org.apache.pdfbox:fontbox:2.0.28 - http://pdfbox.apache.org/) + * Apache Log4j 1.x Compatibility API (org.apache.logging.log4j:log4j-1.2-api:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-1.2-api/) + * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-api/) + * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-core/) + * Apache Log4j JUL Adapter (org.apache.logging.log4j:log4j-jul:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-jul/) + * Apache Log4j Layout for JSON template (org.apache.logging.log4j:log4j-layout-template-json:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-layout-template-json/) + * Apache Log4j SLF4J Binding (org.apache.logging.log4j:log4j-slf4j-impl:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-slf4j-impl/) + * Apache Log4j Web (org.apache.logging.log4j:log4j-web:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-web/) + * Lucene Common Analyzers (org.apache.lucene:lucene-analyzers-common:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-common) + * Lucene ICU Analysis Components (org.apache.lucene:lucene-analyzers-icu:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-icu) + * Lucene Kuromoji Japanese Morphological Analyzer (org.apache.lucene:lucene-analyzers-kuromoji:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-kuromoji) + * Lucene Nori Korean Morphological Analyzer (org.apache.lucene:lucene-analyzers-nori:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-nori) + * Lucene Phonetic Filters (org.apache.lucene:lucene-analyzers-phonetic:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-phonetic) + * Lucene Smart Chinese Analyzer (org.apache.lucene:lucene-analyzers-smartcn:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-smartcn) + * Lucene Stempel Analyzer (org.apache.lucene:lucene-analyzers-stempel:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-stempel) + * Lucene Memory (org.apache.lucene:lucene-backward-codecs:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-backward-codecs) + * Lucene Classification (org.apache.lucene:lucene-classification:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-classification) + * Lucene codecs (org.apache.lucene:lucene-codecs:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-codecs) + * Lucene Core (org.apache.lucene:lucene-core:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-core) + * Lucene Expressions (org.apache.lucene:lucene-expressions:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-expressions) + * Lucene Grouping (org.apache.lucene:lucene-grouping:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-grouping) + * Lucene Highlighter (org.apache.lucene:lucene-highlighter:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-highlighter) + * Lucene Join (org.apache.lucene:lucene-join:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-join) + * Lucene Memory (org.apache.lucene:lucene-memory:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-memory) + * Lucene Miscellaneous (org.apache.lucene:lucene-misc:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-misc) + * Lucene Queries (org.apache.lucene:lucene-queries:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-queries) + * Lucene QueryParsers (org.apache.lucene:lucene-queryparser:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-queryparser) + * Lucene Sandbox (org.apache.lucene:lucene-sandbox:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-sandbox) + * Lucene Spatial Extras (org.apache.lucene:lucene-spatial-extras:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-spatial-extras) + * Lucene Spatial 3D (org.apache.lucene:lucene-spatial3d:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-spatial3d) + * Lucene Suggest (org.apache.lucene:lucene-suggest:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-suggest) + * Apache FontBox (org.apache.pdfbox:fontbox:2.0.31 - http://pdfbox.apache.org/) * PDFBox JBIG2 ImageIO plugin (org.apache.pdfbox:jbig2-imageio:3.0.4 - https://www.apache.org/jbig2-imageio/) * Apache JempBox (org.apache.pdfbox:jempbox:1.8.17 - http://www.apache.org/pdfbox-parent/jempbox/) - * Apache PDFBox (org.apache.pdfbox:pdfbox:2.0.28 - https://www.apache.org/pdfbox-parent/pdfbox/) - * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:2.0.27 - https://www.apache.org/pdfbox-parent/pdfbox-tools/) - * Apache XmpBox (org.apache.pdfbox:xmpbox:2.0.27 - https://www.apache.org/pdfbox-parent/xmpbox/) - * Apache POI - Common (org.apache.poi:poi:5.2.3 - https://poi.apache.org/) - * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.2.3 - https://poi.apache.org/) - * Apache POI (org.apache.poi:poi-ooxml-lite:5.2.3 - https://poi.apache.org/) - * Apache POI (org.apache.poi:poi-scratchpad:5.2.3 - https://poi.apache.org/) - * Apache Solr Core (org.apache.solr:solr-core:8.11.2 - https://lucene.apache.org/solr-parent/solr-core) - * Apache Solr Solrj (org.apache.solr:solr-solrj:8.11.2 - https://lucene.apache.org/solr-parent/solr-solrj) + * Apache PDFBox (org.apache.pdfbox:pdfbox:2.0.31 - https://www.apache.org/pdfbox-parent/pdfbox/) + * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:2.0.31 - https://www.apache.org/pdfbox-parent/pdfbox-tools/) + * Apache XmpBox (org.apache.pdfbox:xmpbox:2.0.31 - https://www.apache.org/pdfbox-parent/xmpbox/) + * Apache POI - Common (org.apache.poi:poi:5.2.5 - https://poi.apache.org/) + * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.2.5 - https://poi.apache.org/) + * Apache POI (org.apache.poi:poi-ooxml-lite:5.2.5 - https://poi.apache.org/) + * Apache POI (org.apache.poi:poi-scratchpad:5.2.5 - https://poi.apache.org/) + * Apache Solr Core (org.apache.solr:solr-core:8.11.3 - https://lucene.apache.org/solr-parent/solr-core) + * Apache Solr Solrj (org.apache.solr:solr-solrj:8.11.3 - https://lucene.apache.org/solr-parent/solr-solrj) * Apache Standard Taglib Implementation (org.apache.taglibs:taglibs-standard-impl:1.2.5 - http://tomcat.apache.org/taglibs/standard-1.2.5/taglibs-standard-impl) * Apache Standard Taglib Specification API (org.apache.taglibs:taglibs-standard-spec:1.2.5 - http://tomcat.apache.org/taglibs/standard-1.2.5/taglibs-standard-spec) * Apache Thrift (org.apache.thrift:libthrift:0.9.2 - http://thrift.apache.org) - * Apache Tika core (org.apache.tika:tika-core:2.5.0 - https://tika.apache.org/) - * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:2.5.0 - https://tika.apache.org/tika-parser-apple-module/) - * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:2.5.0 - https://tika.apache.org/tika-parser-audiovideo-module/) - * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:2.5.0 - https://tika.apache.org/tika-parser-cad-module/) - * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:2.5.0 - https://tika.apache.org/tika-parser-code-module/) - * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:2.5.0 - https://tika.apache.org/tika-parser-crypto-module/) - * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:2.5.0 - https://tika.apache.org/tika-parser-digest-commons/) - * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:2.5.0 - https://tika.apache.org/tika-parser-font-module/) - * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:2.5.0 - https://tika.apache.org/tika-parser-html-module/) - * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:2.5.0 - https://tika.apache.org/tika-parser-image-module/) - * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:2.5.0 - https://tika.apache.org/tika-parser-mail-commons/) - * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:2.5.0 - https://tika.apache.org/tika-parser-mail-module/) - * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:2.5.0 - https://tika.apache.org/tika-parser-microsoft-module/) - * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:2.5.0 - https://tika.apache.org/tika-parser-miscoffice-module/) - * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:2.5.0 - https://tika.apache.org/tika-parser-news-module/) - * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:2.5.0 - https://tika.apache.org/tika-parser-ocr-module/) - * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:2.5.0 - https://tika.apache.org/tika-parser-pdf-module/) - * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:2.5.0 - https://tika.apache.org/tika-parser-pkg-module/) - * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:2.5.0 - https://tika.apache.org/tika-parser-text-module/) - * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:2.5.0 - https://tika.apache.org/tika-parser-webarchive-module/) - * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:2.5.0 - https://tika.apache.org/tika-parser-xml-module/) - * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:2.5.0 - https://tika.apache.org/tika-parser-xmp-commons/) - * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:2.5.0 - https://tika.apache.org/tika-parser-zip-commons/) - * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:2.5.0 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/) - * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:9.0.75 - https://tomcat.apache.org/) - * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:9.0.75 - https://tomcat.apache.org/) - * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:9.0.75 - https://tomcat.apache.org/) + * Apache Tika core (org.apache.tika:tika-core:2.9.2 - https://tika.apache.org/) + * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:2.9.2 - https://tika.apache.org/tika-parser-apple-module/) + * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:2.9.2 - https://tika.apache.org/tika-parser-audiovideo-module/) + * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:2.9.2 - https://tika.apache.org/tika-parser-cad-module/) + * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:2.9.2 - https://tika.apache.org/tika-parser-code-module/) + * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:2.9.2 - https://tika.apache.org/tika-parser-crypto-module/) + * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:2.9.2 - https://tika.apache.org/tika-parser-digest-commons/) + * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:2.9.2 - https://tika.apache.org/tika-parser-font-module/) + * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:2.9.2 - https://tika.apache.org/tika-parser-html-module/) + * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:2.9.2 - https://tika.apache.org/tika-parser-image-module/) + * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:2.9.2 - https://tika.apache.org/tika-parser-mail-commons/) + * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:2.9.2 - https://tika.apache.org/tika-parser-mail-module/) + * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:2.9.2 - https://tika.apache.org/tika-parser-microsoft-module/) + * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:2.9.2 - https://tika.apache.org/tika-parser-miscoffice-module/) + * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:2.9.2 - https://tika.apache.org/tika-parser-news-module/) + * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:2.9.2 - https://tika.apache.org/tika-parser-ocr-module/) + * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:2.9.2 - https://tika.apache.org/tika-parser-pdf-module/) + * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:2.9.2 - https://tika.apache.org/tika-parser-pkg-module/) + * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:2.9.2 - https://tika.apache.org/tika-parser-text-module/) + * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:2.9.2 - https://tika.apache.org/tika-parser-webarchive-module/) + * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:2.9.2 - https://tika.apache.org/tika-parser-xml-module/) + * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:2.9.2 - https://tika.apache.org/tika-parser-xmp-commons/) + * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:2.9.2 - https://tika.apache.org/tika-parser-zip-commons/) + * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:2.9.2 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/) + * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:9.0.83 - https://tomcat.apache.org/) + * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:9.0.83 - https://tomcat.apache.org/) + * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:9.0.83 - https://tomcat.apache.org/) * Apache Velocity - Engine (org.apache.velocity:velocity-engine-core:2.3 - http://velocity.apache.org/engine/devel/velocity-engine-core/) * Apache Velocity - JSR 223 Scripting (org.apache.velocity:velocity-engine-scripting:2.2 - http://velocity.apache.org/engine/devel/velocity-engine-scripting/) * Axiom API (org.apache.ws.commons.axiom:axiom-api:1.2.22 - http://ws.apache.org/axiom/) * Abdera Model (FOM) Implementation (org.apache.ws.commons.axiom:fom-impl:1.2.22 - http://ws.apache.org/axiom/implementations/fom-impl/) - * XmlBeans (org.apache.xmlbeans:xmlbeans:5.1.1 - https://xmlbeans.apache.org/) + * XmlBeans (org.apache.xmlbeans:xmlbeans:5.2.0 - https://xmlbeans.apache.org/) * Apache ZooKeeper - Server (org.apache.zookeeper:zookeeper:3.6.2 - http://zookeeper.apache.org/zookeeper) * Apache ZooKeeper - Jute (org.apache.zookeeper:zookeeper-jute:3.6.2 - http://zookeeper.apache.org/zookeeper-jute) - * org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.1.0 - https://github.com/apiguardian-team/apiguardian) + * org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.1.2 - https://github.com/apiguardian-team/apiguardian) * AssertJ fluent assertions (org.assertj:assertj-core:3.22.0 - https://assertj.github.io/doc/assertj-core/) * Evo Inflector (org.atteo:evo-inflector:1.3 - http://atteo.org/static/evo-inflector) * jose4j (org.bitbucket.b_c:jose4j:0.6.5 - https://bitbucket.org/b_c/jose4j/) @@ -284,49 +293,50 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * rfc3986-uri (org.dmfs:rfc3986-uri:0.8.1 - https://github.com/dmfs/uri-toolkit) * Jetty :: Apache JSP Implementation (org.eclipse.jetty:apache-jsp:9.4.15.v20190215 - http://www.eclipse.org/jetty) * Apache :: JSTL module (org.eclipse.jetty:apache-jstl:9.4.15.v20190215 - http://tomcat.apache.org/taglibs/standard/) - * Jetty :: ALPN :: Client (org.eclipse.jetty:jetty-alpn-client:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-client) - * Jetty :: ALPN :: JDK9 Client Implementation (org.eclipse.jetty:jetty-alpn-java-client:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-client) - * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-server) - * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) - * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) + * Jetty :: ALPN :: Client (org.eclipse.jetty:jetty-alpn-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-client) + * Jetty :: ALPN :: JDK9 Client Implementation (org.eclipse.jetty:jetty-alpn-java-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-client) + * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-server) + * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) + * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) * Jetty :: Servlet Annotations (org.eclipse.jetty:jetty-annotations:9.4.15.v20190215 - http://www.eclipse.org/jetty) - * Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-client) - * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-continuation) - * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-continuation) - * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-deploy) - * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-http) - * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-io) - * Jetty :: JMX Management (org.eclipse.jetty:jetty-jmx:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-jmx) + * Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-client) + * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-continuation) + * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-continuation) + * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-deploy) + * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-http) + * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-io) + * Jetty :: JMX Management (org.eclipse.jetty:jetty-jmx:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-jmx) * Jetty :: JNDI Naming (org.eclipse.jetty:jetty-jndi:9.4.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Plus (org.eclipse.jetty:jetty-plus:9.4.15.v20190215 - http://www.eclipse.org/jetty) - * Jetty :: Rewrite Handler (org.eclipse.jetty:jetty-rewrite:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-rewrite) - * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-security) - * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-security) - * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-server) - * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-servlet) - * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-servlets) - * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-util) - * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-util-ajax) - * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-webapp) - * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-xml) - * Jetty :: HTTP2 :: Client (org.eclipse.jetty.http2:http2-client:9.4.44.v20210927 - https://eclipse.org/jetty/http2-parent/http2-client) - * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.51.v20230217 - https://eclipse.org/jetty/http2-parent/http2-common) - * Jetty :: HTTP2 :: HPACK (org.eclipse.jetty.http2:http2-hpack:9.4.44.v20210927 - https://eclipse.org/jetty/http2-parent/http2-hpack) - * Jetty :: HTTP2 :: HTTP Client Transport (org.eclipse.jetty.http2:http2-http-client-transport:9.4.44.v20210927 - https://eclipse.org/jetty/http2-parent/http2-http-client-transport) - * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.51.v20230217 - https://eclipse.org/jetty/http2-parent/http2-server) + * Jetty :: Rewrite Handler (org.eclipse.jetty:jetty-rewrite:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-rewrite) + * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-security) + * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-security) + * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-server) + * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-servlet) + * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-servlets) + * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util) + * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util-ajax) + * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-webapp) + * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-xml) + * Jetty :: HTTP2 :: Client (org.eclipse.jetty.http2:http2-client:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-client) + * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.54.v20240208 - https://eclipse.org/jetty/http2-parent/http2-common) + * Jetty :: HTTP2 :: HPACK (org.eclipse.jetty.http2:http2-hpack:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-hpack) + * Jetty :: HTTP2 :: HTTP Client Transport (org.eclipse.jetty.http2:http2-http-client-transport:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-http-client-transport) + * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.54.v20240208 - https://eclipse.org/jetty/http2-parent/http2-server) * Jetty :: Schemas (org.eclipse.jetty.toolchain:jetty-schemas:3.1.2 - https://eclipse.org/jetty/jetty-schemas) - * Ehcache (org.ehcache:ehcache:3.4.0 - http://ehcache.org) - * flyway-core (org.flywaydb:flyway-core:8.4.4 - https://flywaydb.org/flyway-core) + * Ehcache (org.ehcache:ehcache:3.10.8 - http://ehcache.org) + * flyway-core (org.flywaydb:flyway-core:8.5.13 - https://flywaydb.org/flyway-core) * Ogg and Vorbis for Java, Core (org.gagravarr:vorbis-java-core:0.8 - https://github.com/Gagravarr/VorbisJava) * Apache Tika plugin for Ogg, Vorbis and FLAC (org.gagravarr:vorbis-java-tika:0.8 - https://github.com/Gagravarr/VorbisJava) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:6.2.5.Final - http://hibernate.org/validator/hibernate-validator) * Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:6.2.5.Final - http://hibernate.org/validator/hibernate-validator-cdi) + * org.immutables.value-annotations (org.immutables:value-annotations:2.9.2 - http://immutables.org/value-annotations) * leveldb (org.iq80.leveldb:leveldb:0.12 - http://github.com/dain/leveldb/leveldb) * leveldb-api (org.iq80.leveldb:leveldb-api:0.12 - http://github.com/dain/leveldb/leveldb-api) - * Javassist (org.javassist:javassist:3.25.0-GA - http://www.javassist.org/) + * Javassist (org.javassist:javassist:3.29.0-GA - http://www.javassist.org/) * Java Annotation Indexer (org.jboss:jandex:2.4.2.Final - http://www.jboss.org/jandex) * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.4.3.Final - http://www.jboss.org) * JDOM (org.jdom:jdom2:2.0.6.1 - http://www.jdom.org) @@ -335,6 +345,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * jtwig-spring (org.jtwig:jtwig-spring:5.87.0.RELEASE - http://jtwig.org) * jtwig-spring-boot-starter (org.jtwig:jtwig-spring-boot-starter:5.87.0.RELEASE - http://jtwig.org) * jtwig-web (org.jtwig:jtwig-web:5.87.0.RELEASE - http://jtwig.org) + * Proj4J (org.locationtech.proj4j:proj4j:1.1.5 - https://github.com/locationtech/proj4j) * Spatial4J (org.locationtech.spatial4j:spatial4j:0.7 - https://projects.eclipse.org/projects/locationtech.spatial4j) * MockServer Java Client (org.mock-server:mockserver-client-java:5.11.2 - http://www.mock-server.com) * MockServer Core (org.mock-server:mockserver-core:5.11.2 - http://www.mock-server.com) @@ -346,12 +357,12 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Jetty Servlet Tester (org.mortbay.jetty:jetty-servlet-tester:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/jetty-servlet-tester) * Jetty Utilities (org.mortbay.jetty:jetty-util:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/jetty-util) * Servlet Specification API (org.mortbay.jetty:servlet-api:2.5-20081211 - http://jetty.mortbay.org/servlet-api) - * jwarc (org.netpreserve:jwarc:0.19.0 - https://github.com/iipc/jwarc) + * jwarc (org.netpreserve:jwarc:0.29.0 - https://github.com/iipc/jwarc) * Objenesis (org.objenesis:objenesis:3.2 - http://objenesis.org/objenesis) * parboiled-core (org.parboiled:parboiled-core:1.3.1 - http://parboiled.org) * parboiled-java (org.parboiled:parboiled-java:1.3.1 - http://parboiled.org) * RRD4J (org.rrd4j:rrd4j:3.5 - https://github.com/rrd4j/rrd4j/) - * Scala Library (org.scala-lang:scala-library:2.13.9 - https://www.scala-lang.org/) + * Scala Library (org.scala-lang:scala-library:2.13.11 - https://www.scala-lang.org/) * Scala Compiler (org.scala-lang:scala-reflect:2.13.0 - https://www.scala-lang.org/) * scala-collection-compat (org.scala-lang.modules:scala-collection-compat_2.13:2.1.6 - http://www.scala-lang.org/) * scala-java8-compat (org.scala-lang.modules:scala-java8-compat_2.13:0.9.0 - http://www.scala-lang.org/) @@ -359,58 +370,55 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * scala-xml (org.scala-lang.modules:scala-xml_2.13:1.3.0 - http://www.scala-lang.org/) * JSONassert (org.skyscreamer:jsonassert:1.5.1 - https://github.com/skyscreamer/JSONassert) * JCL 1.2 implemented over SLF4J (org.slf4j:jcl-over-slf4j:1.7.36 - http://www.slf4j.org) - * Spring AOP (org.springframework:spring-aop:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Beans (org.springframework:spring-beans:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Context (org.springframework:spring-context:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Context Support (org.springframework:spring-context-support:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Core (org.springframework:spring-core:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Expression Language (SpEL) (org.springframework:spring-expression:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Commons Logging Bridge (org.springframework:spring-jcl:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring JDBC (org.springframework:spring-jdbc:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Object/Relational Mapping (org.springframework:spring-orm:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring TestContext Framework (org.springframework:spring-test:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Transaction (org.springframework:spring-tx:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Web (org.springframework:spring-web:5.3.27 - https://github.com/spring-projects/spring-framework) - * Spring Web MVC (org.springframework:spring-webmvc:5.3.27 - https://github.com/spring-projects/spring-framework) - * spring-boot (org.springframework.boot:spring-boot:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:2.7.12 - https://spring.io/projects/spring-boot) + * Spring AOP (org.springframework:spring-aop:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Beans (org.springframework:spring-beans:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Context (org.springframework:spring-context:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Context Support (org.springframework:spring-context-support:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Core (org.springframework:spring-core:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Expression Language (SpEL) (org.springframework:spring-expression:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Commons Logging Bridge (org.springframework:spring-jcl:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring JDBC (org.springframework:spring-jdbc:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Object/Relational Mapping (org.springframework:spring-orm:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring TestContext Framework (org.springframework:spring-test:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Transaction (org.springframework:spring-tx:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Web (org.springframework:spring-web:5.3.34 - https://github.com/spring-projects/spring-framework) + * Spring Web MVC (org.springframework:spring-webmvc:5.3.34 - https://github.com/spring-projects/spring-framework) + * spring-boot (org.springframework.boot:spring-boot:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:2.7.18 - https://spring.io/projects/spring-boot) * Spring Boot Configuration Processor (org.springframework.boot:spring-boot-configuration-processor:2.0.0.RELEASE - https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-tools/spring-boot-configuration-processor) - * spring-boot-starter (org.springframework.boot:spring-boot-starter:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-test (org.springframework.boot:spring-boot-test:2.7.12 - https://spring.io/projects/spring-boot) - * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:2.7.12 - https://spring.io/projects/spring-boot) - * Spring Data Core (org.springframework.data:spring-data-commons:2.7.12 - https://www.spring.io/spring-data/spring-data-commons) - * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:3.7.12 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) - * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:3.7.12 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) - * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:1.5.4 - https://github.com/spring-projects/spring-hateoas) + * spring-boot-starter (org.springframework.boot:spring-boot-starter:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-test (org.springframework.boot:spring-boot-test:2.7.18 - https://spring.io/projects/spring-boot) + * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:2.7.18 - https://spring.io/projects/spring-boot) + * Spring Data Core (org.springframework.data:spring-data-commons:2.7.18 - https://www.spring.io/spring-data/spring-data-commons) + * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:3.7.18 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) + * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:3.7.18 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) + * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:1.5.6 - https://github.com/spring-projects/spring-hateoas) * Spring Plugin - Core (org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE - https://github.com/spring-projects/spring-plugin/spring-plugin-core) - * spring-security-config (org.springframework.security:spring-security-config:5.7.8 - https://spring.io/projects/spring-security) - * spring-security-core (org.springframework.security:spring-security-core:5.7.8 - https://spring.io/projects/spring-security) - * spring-security-crypto (org.springframework.security:spring-security-crypto:5.7.8 - https://spring.io/projects/spring-security) - * spring-security-test (org.springframework.security:spring-security-test:5.7.8 - https://spring.io/projects/spring-security) - * spring-security-web (org.springframework.security:spring-security-web:5.7.8 - https://spring.io/projects/spring-security) + * spring-security-config (org.springframework.security:spring-security-config:5.7.11 - https://spring.io/projects/spring-security) + * spring-security-core (org.springframework.security:spring-security-core:5.7.11 - https://spring.io/projects/spring-security) + * spring-security-crypto (org.springframework.security:spring-security-crypto:5.7.11 - https://spring.io/projects/spring-security) + * spring-security-test (org.springframework.security:spring-security-test:5.7.11 - https://spring.io/projects/spring-security) + * spring-security-web (org.springframework.security:spring-security-web:5.7.11 - https://spring.io/projects/spring-security) * SWORD v2 :: Common Server Library (org.swordapp:sword2-server:1.0 - http://www.swordapp.org/) - * snappy-java (org.xerial.snappy:snappy-java:1.1.7.6 - https://github.com/xerial/snappy-java) + * snappy-java (org.xerial.snappy:snappy-java:1.1.10.1 - https://github.com/xerial/snappy-java) * xml-matchers (org.xmlmatchers:xml-matchers:0.10 - http://code.google.com/p/xml-matchers/) - * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.8.0 - https://www.xmlunit.org/) + * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.10.0 - https://www.xmlunit.org/) * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.9.1 - https://www.xmlunit.org/) * org.xmlunit:xmlunit-placeholders (org.xmlunit:xmlunit-placeholders:2.8.0 - https://www.xmlunit.org/xmlunit-placeholders/) * SnakeYAML (org.yaml:snakeyaml:1.30 - https://bitbucket.org/snakeyaml/snakeyaml) * software.amazon.ion:ion-java (software.amazon.ion:ion-java:1.0.2 - https://github.com/amznlabs/ion-java/) - * Xalan Java Serializer (xalan:serializer:2.7.2 - http://xml.apache.org/xalan-j/) - * xalan (xalan:xalan:2.7.0 - no url defined) - * Xalan Java (xalan:xalan:2.7.2 - http://xml.apache.org/xalan-j/) * Xerces2-j (xerces:xercesImpl:2.12.2 - https://xerces.apache.org/xerces2-j/) * XML Commons External Components XML APIs (xml-apis:xml-apis:1.4.01 - http://xml.apache.org/commons/components/external/) @@ -421,29 +429,28 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * coverity-escapers (com.coverity.security:coverity-escapers:1.1.1 - http://coverity.com/security) * Java Advanced Imaging Image I/O Tools API core (standalone) (com.github.jai-imageio:jai-imageio-core:1.4.0 - https://github.com/jai-imageio/jai-imageio-core) * JSONLD Java :: Core (com.github.jsonld-java:jsonld-java:0.5.1 - http://github.com/jsonld-java/jsonld-java/jsonld-java/) - * curvesapi (com.github.virtuald:curvesapi:1.07 - https://github.com/virtuald/curvesapi) - * Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.11.0 - https://developers.google.com/protocol-buffers/protobuf-java/) + * curvesapi (com.github.virtuald:curvesapi:1.08 - https://github.com/virtuald/curvesapi) + * Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.15.0 - https://developers.google.com/protocol-buffers/protobuf-java/) * JZlib (com.jcraft:jzlib:1.1.3 - http://www.jcraft.com/jzlib/) - * dnsjava (dnsjava:dnsjava:2.1.7 - http://www.dnsjava.org) - * jaxen (jaxen:jaxen:1.1.6 - http://jaxen.codehaus.org/) + * dnsjava (dnsjava:dnsjava:2.1.9 - http://www.dnsjava.org) + * jaxen (jaxen:jaxen:2.0.0 - http://www.cafeconleche.org/jaxen/jaxen) * ANTLR 4 Runtime (org.antlr:antlr4-runtime:4.5.1-1 - http://www.antlr.org/antlr4-runtime) - * commons-compiler (org.codehaus.janino:commons-compiler:3.0.9 - http://janino-compiler.github.io/commons-compiler/) - * janino (org.codehaus.janino:janino:3.0.9 - http://janino-compiler.github.io/janino/) + * commons-compiler (org.codehaus.janino:commons-compiler:3.1.8 - http://janino-compiler.github.io/commons-compiler/) + * janino (org.codehaus.janino:janino:3.1.8 - http://janino-compiler.github.io/janino/) * Stax2 API (org.codehaus.woodstox:stax2-api:4.2.1 - http://github.com/FasterXML/stax2-api) - * Hamcrest Date (org.exparity:hamcrest-date:2.0.7 - https://github.com/exparity/hamcrest-date) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * Hamcrest Date (org.exparity:hamcrest-date:2.0.8 - https://github.com/exparity/hamcrest-date) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * Hamcrest (org.hamcrest:hamcrest:2.2 - http://hamcrest.org/JavaHamcrest/) - * Hamcrest All (org.hamcrest:hamcrest-all:1.3 - https://github.com/hamcrest/JavaHamcrest/hamcrest-all) - * Hamcrest Core (org.hamcrest:hamcrest-core:1.3 - https://github.com/hamcrest/JavaHamcrest/hamcrest-core) + * Hamcrest Core (org.hamcrest:hamcrest-core:2.2 - http://hamcrest.org/JavaHamcrest/) * HdrHistogram (org.hdrhistogram:HdrHistogram:2.1.12 - http://hdrhistogram.github.io/HdrHistogram/) * JBibTeX (org.jbibtex:jbibtex:1.0.20 - http://www.jbibtex.org) * asm (org.ow2.asm:asm:8.0.1 - http://asm.ow2.io/) * asm-analysis (org.ow2.asm:asm-analysis:7.1 - http://asm.ow2.org/) - * asm-commons (org.ow2.asm:asm-commons:8.0.1 - http://asm.ow2.io/) + * asm-commons (org.ow2.asm:asm-commons:9.3 - http://asm.ow2.io/) * asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/) * asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/) - * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.6.0 - https://jdbc.postgresql.org) + * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.3 - https://jdbc.postgresql.org) * Reflections (org.reflections:reflections:0.9.12 - http://github.com/ronmamo/reflections) * JMatIO (org.tallison:jmatio:1.5 - https://github.com/tballison/jmatio) * XMLUnit for Java (xmlunit:xmlunit:1.3 - http://xmlunit.sourceforge.net/) @@ -454,12 +461,12 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines Common Development and Distribution License (CDDL): - * istack common utility code runtime (com.sun.istack:istack-commons-runtime:3.0.7 - http://java.net/istack-commons/istack-commons-runtime/) * JavaMail API (com.sun.mail:javax.mail:1.6.2 - http://javaee.github.io/javamail/javax.mail) * JavaMail API (no providers) (com.sun.mail:mailapi:1.6.2 - http://javaee.github.io/javamail/mailapi) * Old JAXB Core (com.sun.xml.bind:jaxb-core:2.3.0.1 - http://jaxb.java.net/jaxb-bundles/jaxb-core) * Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:2.3.1 - http://jaxb.java.net/jaxb-bundles/jaxb-impl) * Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:1.3.5 - https://projects.eclipse.org/projects/ee4j.ca) + * javax.transaction API (jakarta.transaction:jakarta.transaction-api:1.3.3 - https://projects.eclipse.org/projects/ee4j.jta) * jakarta.ws.rs-api (jakarta.ws.rs:jakarta.ws.rs-api:2.1.6 - https://github.com/eclipse-ee4j/jaxrs-api) * JavaBeans Activation Framework (JAF) (javax.activation:activation:1.1 - http://java.sun.com/products/javabeans/jaf/index.jsp) * JavaBeans Activation Framework API jar (javax.activation:javax.activation-api:1.2.0 - http://java.net/all/javax.activation-api/) @@ -474,12 +481,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * OSGi resource locator (org.glassfish.hk2:osgi-resource-locator:1.0.3 - https://projects.eclipse.org/projects/ee4j/osgi-resource-locator) * aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) * javax.inject:1 as OSGi bundle (org.glassfish.hk2.external:jakarta.inject:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/jakarta.inject) - * JAXB Runtime (org.glassfish.jaxb:jaxb-runtime:2.3.1 - http://jaxb.java.net/jaxb-runtime-parent/jaxb-runtime) - * TXW2 Runtime (org.glassfish.jaxb:txw2:2.3.1 - http://jaxb.java.net/jaxb-txw-parent/txw2) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * Java Transaction API (org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final - http://www.jboss.org/jboss-transaction-api_1.2_spec) - * Extended StAX API (org.jvnet.staxex:stax-ex:1.8 - http://stax-ex.java.net/) Cordra (Version 2) License Agreement: @@ -489,55 +493,62 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines Eclipse Distribution License, Version 1.0: + * Jakarta Activation (com.sun.activation:jakarta.activation:1.2.2 - https://github.com/eclipse-ee4j/jaf/jakarta.activation) + * istack common utility code runtime (com.sun.istack:istack-commons-runtime:3.0.12 - https://projects.eclipse.org/projects/ee4j/istack-commons/istack-commons-runtime) * Jakarta Activation API jar (jakarta.activation:jakarta.activation-api:1.2.2 - https://github.com/eclipse-ee4j/jaf/jakarta.activation-api) * Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:2.3.3 - https://github.com/eclipse-ee4j/jaxb-api/jakarta.xml.bind-api) * javax.persistence-api (javax.persistence:javax.persistence-api:2.2 - https://github.com/javaee/jpa-spec) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * JAXB Runtime (org.glassfish.jaxb:jaxb-runtime:2.3.9 - https://eclipse-ee4j.github.io/jaxb-ri/) + * TXW2 Runtime (org.glassfish.jaxb:txw2:2.3.9 - https://eclipse-ee4j.github.io/jaxb-ri/) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * Java Persistence API, Version 2.1 (org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final - http://hibernate.org) + * org.locationtech.jts:jts-core (org.locationtech.jts:jts-core:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core) + * org.locationtech.jts.io:jts-io-common (org.locationtech.jts.io:jts-io-common:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io/jts-io-common) Eclipse Public License: * System Rules (com.github.stefanbirkner:system-rules:1.19.0 - http://stefanbirkner.github.io/system-rules/) - * H2 Database Engine (com.h2database:h2:2.1.210 - https://h2database.com) + * H2 Database Engine (com.h2database:h2:2.2.224 - https://h2database.com) * Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:1.3.5 - https://projects.eclipse.org/projects/ee4j.ca) + * javax.transaction API (jakarta.transaction:jakarta.transaction-api:1.3.3 - https://projects.eclipse.org/projects/ee4j.jta) * jakarta.ws.rs-api (jakarta.ws.rs:jakarta.ws.rs-api:2.1.6 - https://github.com/eclipse-ee4j/jaxrs-api) * javax.persistence-api (javax.persistence:javax.persistence-api:2.2 - https://github.com/javaee/jpa-spec) - * JUnit (junit:junit:4.13.1 - http://junit.org) + * JUnit (junit:junit:4.13.2 - http://junit.org) * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.7 - https://www.eclipse.org/aspectj/) * Eclipse Compiler for Java(TM) (org.eclipse.jdt:ecj:3.14.0 - http://www.eclipse.org/jdt) * Jetty :: Apache JSP Implementation (org.eclipse.jetty:apache-jsp:9.4.15.v20190215 - http://www.eclipse.org/jetty) * Apache :: JSTL module (org.eclipse.jetty:apache-jstl:9.4.15.v20190215 - http://tomcat.apache.org/taglibs/standard/) - * Jetty :: ALPN :: Client (org.eclipse.jetty:jetty-alpn-client:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-client) - * Jetty :: ALPN :: JDK9 Client Implementation (org.eclipse.jetty:jetty-alpn-java-client:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-client) - * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-server) - * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) - * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) + * Jetty :: ALPN :: Client (org.eclipse.jetty:jetty-alpn-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-client) + * Jetty :: ALPN :: JDK9 Client Implementation (org.eclipse.jetty:jetty-alpn-java-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-client) + * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-server) + * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) + * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server) * Jetty :: Servlet Annotations (org.eclipse.jetty:jetty-annotations:9.4.15.v20190215 - http://www.eclipse.org/jetty) - * Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-client) - * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-continuation) - * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-continuation) - * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-deploy) - * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-http) - * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-io) - * Jetty :: JMX Management (org.eclipse.jetty:jetty-jmx:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-jmx) + * Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-client) + * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-continuation) + * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-continuation) + * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-deploy) + * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-http) + * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-io) + * Jetty :: JMX Management (org.eclipse.jetty:jetty-jmx:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-jmx) * Jetty :: JNDI Naming (org.eclipse.jetty:jetty-jndi:9.4.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Plus (org.eclipse.jetty:jetty-plus:9.4.15.v20190215 - http://www.eclipse.org/jetty) - * Jetty :: Rewrite Handler (org.eclipse.jetty:jetty-rewrite:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-rewrite) - * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.44.v20210927 - https://eclipse.org/jetty/jetty-security) - * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-security) - * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-server) - * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-servlet) - * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-servlets) - * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-util) - * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-util-ajax) - * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-webapp) - * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.51.v20230217 - https://eclipse.org/jetty/jetty-xml) - * Jetty :: HTTP2 :: Client (org.eclipse.jetty.http2:http2-client:9.4.44.v20210927 - https://eclipse.org/jetty/http2-parent/http2-client) - * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.51.v20230217 - https://eclipse.org/jetty/http2-parent/http2-common) - * Jetty :: HTTP2 :: HPACK (org.eclipse.jetty.http2:http2-hpack:9.4.44.v20210927 - https://eclipse.org/jetty/http2-parent/http2-hpack) - * Jetty :: HTTP2 :: HTTP Client Transport (org.eclipse.jetty.http2:http2-http-client-transport:9.4.44.v20210927 - https://eclipse.org/jetty/http2-parent/http2-http-client-transport) - * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.51.v20230217 - https://eclipse.org/jetty/http2-parent/http2-server) + * Jetty :: Rewrite Handler (org.eclipse.jetty:jetty-rewrite:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-rewrite) + * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-security) + * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-security) + * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-server) + * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-servlet) + * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-servlets) + * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util) + * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util-ajax) + * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-webapp) + * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-xml) + * Jetty :: HTTP2 :: Client (org.eclipse.jetty.http2:http2-client:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-client) + * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.54.v20240208 - https://eclipse.org/jetty/http2-parent/http2-common) + * Jetty :: HTTP2 :: HPACK (org.eclipse.jetty.http2:http2-hpack:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-hpack) + * Jetty :: HTTP2 :: HTTP Client Transport (org.eclipse.jetty.http2:http2-http-client-transport:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-http-client-transport) + * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.54.v20240208 - https://eclipse.org/jetty/http2-parent/http2-server) * Jetty :: Schemas (org.eclipse.jetty.toolchain:jetty-schemas:3.1.2 - https://eclipse.org/jetty/jetty-schemas) * HK2 API module (org.glassfish.hk2:hk2-api:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api) * ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator) @@ -545,14 +556,24 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * OSGi resource locator (org.glassfish.hk2:osgi-resource-locator:1.0.3 - https://projects.eclipse.org/projects/ee4j/osgi-resource-locator) * aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) * javax.inject:1 as OSGi bundle (org.glassfish.hk2.external:jakarta.inject:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/jakarta.inject) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * Java Persistence API, Version 2.1 (org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final - http://hibernate.org) + * org.locationtech.jts:jts-core (org.locationtech.jts:jts-core:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core) + * org.locationtech.jts.io:jts-io-common (org.locationtech.jts.io:jts-io-common:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io/jts-io-common) * Jetty Server (org.mortbay.jetty:jetty:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/modules/jetty) * Jetty Servlet Tester (org.mortbay.jetty:jetty-servlet-tester:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/jetty-servlet-tester) * Jetty Utilities (org.mortbay.jetty:jetty-util:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/jetty-util) + GENERAL PUBLIC LICENSE, version 3 (GPL-3.0): + + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + + GNU LESSER GENERAL PUBLIC LICENSE, version 3 (LGPL-3.0): + + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + GNU Lesser General Public License (LGPL): * btf (com.github.java-json-tools:btf:1.3 - https://github.com/java-json-tools/btf) @@ -569,9 +590,8 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Hibernate ORM - hibernate-jpamodelgen (org.hibernate:hibernate-jpamodelgen:5.6.15.Final - https://hibernate.org/orm) * Hibernate Commons Annotations (org.hibernate.common:hibernate-commons-annotations:5.1.2.Final - http://hibernate.org) * im4java (org.im4java:im4java:1.4.0 - http://sourceforge.net/projects/im4java/) - * Javassist (org.javassist:javassist:3.25.0-GA - http://www.javassist.org/) - * XOM (xom:xom:1.2.5 - http://xom.nu) - * XOM (xom:xom:1.3.7 - https://xom.nu) + * Javassist (org.javassist:javassist:3.29.0-GA - http://www.javassist.org/) + * XOM (xom:xom:1.3.9 - https://xom.nu) Go License: @@ -579,25 +599,29 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines Handle.Net Public License Agreement (Ver.2): - * Handle Server (net.handle:handle:9.3.0 - https://www.handle.net) + * Handle Server (net.handle:handle:9.3.1 - https://www.handle.net) + + ISC License: + + * Simple Magic (com.j256.simplemagic:simplemagic:1.17 - https://256stuff.com/sources/simplemagic/) MIT License: * better-files (com.github.pathikrit:better-files_2.13:3.9.1 - https://github.com/pathikrit/better-files) * Java SemVer (com.github.zafarkhaja:java-semver:0.9.0 - https://github.com/zafarkhaja/jsemver) - * dd-plist (com.googlecode.plist:dd-plist:1.25 - http://www.github.com/3breadt/dd-plist) - * DigitalCollections: IIIF API Library (de.digitalcollections.iiif:iiif-apis:0.3.9 - https://github.com/dbmdz/iiif-apis) + * dd-plist (com.googlecode.plist:dd-plist:1.28 - http://www.github.com/3breadt/dd-plist) + * DigitalCollections: IIIF API Library (de.digitalcollections.iiif:iiif-apis:0.3.10 - https://github.com/dbmdz/iiif-apis) * s3mock (io.findify:s3mock_2.13:0.2.6 - https://github.com/findify/s3mock) * JOpt Simple (net.sf.jopt-simple:jopt-simple:5.0.4 - http://jopt-simple.github.io/jopt-simple) - * Bouncy Castle S/MIME API (org.bouncycastle:bcmail-jdk15on:1.70 - https://www.bouncycastle.org/java.html) - * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk15on:1.70 - https://www.bouncycastle.org/java.html) - * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk15on:1.70 - https://www.bouncycastle.org/java.html) - * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk15on:1.70 - https://www.bouncycastle.org/java.html) + * Bouncy Castle S/MIME API (org.bouncycastle:bcmail-jdk18on:1.77 - https://www.bouncycastle.org/java.html) + * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html) + * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html) + * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html) * org.brotli:dec (org.brotli:dec:0.1.2 - http://brotli.org/dec) - * Checker Qual (org.checkerframework:checker-qual:3.10.0 - https://checkerframework.org) - * Checker Qual (org.checkerframework:checker-qual:3.31.0 - https://checkerframework.org) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * Checker Qual (org.checkerframework:checker-qual:3.23.0 - https://checkerframework.org) + * Checker Qual (org.checkerframework:checker-qual:3.42.0 - https://checkerframework.org/) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * mockito-core (org.mockito:mockito-core:3.12.4 - https://github.com/mockito/mockito) * mockito-inline (org.mockito:mockito-inline:3.12.4 - https://github.com/mockito/mockito) * ORCID - Model (org.orcid:orcid-model:3.0.2 - http://github.com/ORCID/orcid-model) @@ -608,34 +632,34 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * toastr (org.webjars.bowergithub.codeseven:toastr:2.1.4 - http://webjars.org) * backbone (org.webjars.bowergithub.jashkenas:backbone:1.4.1 - https://www.webjars.org) * underscore (org.webjars.bowergithub.jashkenas:underscore:1.13.2 - https://www.webjars.org) - * jquery (org.webjars.bowergithub.jquery:jquery-dist:3.6.0 - https://www.webjars.org) - * urijs (org.webjars.bowergithub.medialize:uri.js:1.19.10 - https://www.webjars.org) - * bootstrap (org.webjars.bowergithub.twbs:bootstrap:4.6.1 - https://www.webjars.org) - * core-js (org.webjars.npm:core-js:3.30.1 - https://www.webjars.org) + * jquery (org.webjars.bowergithub.jquery:jquery-dist:3.7.1 - https://www.webjars.org) + * urijs (org.webjars.bowergithub.medialize:uri.js:1.19.11 - https://www.webjars.org) + * bootstrap (org.webjars.bowergithub.twbs:bootstrap:4.6.2 - https://www.webjars.org) + * core-js (org.webjars.npm:core-js:3.37.1 - https://www.webjars.org) * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.6.1 - https://www.webjars.org) Mozilla Public License: - * juniversalchardet (com.googlecode.juniversalchardet:juniversalchardet:1.0.3 - http://juniversalchardet.googlecode.com/) - * H2 Database Engine (com.h2database:h2:2.1.210 - https://h2database.com) + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * H2 Database Engine (com.h2database:h2:2.2.224 - https://h2database.com) * Saxon-HE (net.sf.saxon:Saxon-HE:9.8.0-14 - http://www.saxonica.com/) - * Javassist (org.javassist:javassist:3.25.0-GA - http://www.javassist.org/) + * Javassist (org.javassist:javassist:3.29.0-GA - http://www.javassist.org/) * Mozilla Rhino (org.mozilla:rhino:1.7.7.2 - https://developer.mozilla.org/en/Rhino) Public Domain: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * HdrHistogram (org.hdrhistogram:HdrHistogram:2.1.12 - http://hdrhistogram.github.io/HdrHistogram/) - * JSON in Java (org.json:json:20230227 - https://github.com/douglascrockford/JSON-java) + * JSON in Java (org.json:json:20231013 - https://github.com/douglascrockford/JSON-java) * LatencyUtils (org.latencyutils:LatencyUtils:2.0.3 - http://latencyutils.github.io/LatencyUtils/) * Reflections (org.reflections:reflections:0.9.12 - http://github.com/ronmamo/reflections) * XZ for Java (org.tukaani:xz:1.9 - https://tukaani.org/xz/java.html) UnRar License: - * Java Unrar (com.github.junrar:junrar:7.5.3 - https://github.com/junrar/junrar) + * Java Unrar (com.github.junrar:junrar:7.5.5 - https://github.com/junrar/junrar) Unicode/ICU License: @@ -643,10 +667,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines W3C license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) jQuery license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.35 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.39.1 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) diff --git a/pom.xml b/pom.xml index ae83442235e0..3419757b56dc 100644 --- a/pom.xml +++ b/pom.xml @@ -705,7 +705,7 @@ Apache Software License, Version 2.0|The SAX License|The W3C License Apache Software License, Version 2.0|Similar to Apache License but with the acknowledgment clause removed - BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3 + BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0 BSD License|DSpace BSD License|DSpace Sourcecode License @@ -726,7 +726,7 @@ Common Development and Distribution License (CDDL)|GNU General Public License, Version 2 with the Classpath Exception Eclipse Distribution License, Version 1.0|Eclipse Distribution License (EDL), Version 1.0|Eclipse Distribution License - v 1.0|Eclipse Distribution License v. 1.0|EDL 1.0 - Eclipse Public License|Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0|EPL 1.0 license|Eclipse Public License (EPL), Version 1.0|Eclipse Public License 1.0|Eclipse Public License v1.0|Eclipse Public License, Version 1.0|EPL 1.0|EPL 2.0|Eclipse Public License - v 2.0 + Eclipse Public License|Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0|EPL 1.0 license|Eclipse Public License (EPL), Version 1.0|Eclipse Public License 1.0|Eclipse Public License v1.0|Eclipse Public License, Version 1.0|EPL 1.0|EPL 2.0|Eclipse Public License - v 2.0|EPL-2.0|Eclipse Public License 2.0|Eclipse Public License v. 2.0|Eclipse Public License, Version 2.0 Eclipse Public License|Common Public License Version 1.0 @@ -739,7 +739,7 @@ MIT License|Bouncy Castle Licence MIT License|(MIT-style) netCDF C library license - Mozilla Public License|Mozilla Public License version 1.1|Mozilla Public License 1.1 (MPL 1.1)|MPL 1.1|Mozilla Public License Version 2.0|Mozilla Public License, Version 2.0 + Mozilla Public License|Mozilla Public License version 1.1|Mozilla Public License 1.1 (MPL 1.1)|MPL 1.1|Mozilla Public License Version 2.0|Mozilla Public License, Version 2.0|Mozilla Public License Version 1.1 Mozilla Public License|MPL 2.0, and EPL 1.0|MPL 2.0 diff --git a/src/main/license/LICENSES_THIRD_PARTY.properties b/src/main/license/LICENSES_THIRD_PARTY.properties index e893f9b85e31..0a50bdb011b0 100644 --- a/src/main/license/LICENSES_THIRD_PARTY.properties +++ b/src/main/license/LICENSES_THIRD_PARTY.properties @@ -13,89 +13,16 @@ # 1) PLEASE CHECK THE "[src]/LICENSES_THIRD_PARTY" FILE FOR SPECIFIC LICENSE NAMES! # 2) Also please add a link/URL to where you looked up the license -# http://asm.ow2.org/ -asm--asm--3.1=BSD License - -# http://www.h2database.com/html/license.html -com.h2database--h2--1.4.187=Mozilla Public License - -# https://projects.apache.org/projects/commons_jexl.html -commons-jexl--commons-jexl--1.0=Apache Software License, Version 2.0 - -concurrent--concurrent--1.3.4=Public Domain - -# http://dom4j.sourceforge.net/dom4j-1.6.1/license.html -dom4j--dom4j--1.6.1=BSD License - -# http://jakarta.apache.org/regexp/ -jakarta-regexp--jakarta-regexp--1.4=Apache Software License, Version 2.0 - -# https://java.net/projects/servlet-spec/ -javax.servlet--jsp-api--2.0=Common Development and Distribution License (CDDL) -javax.servlet--jstl--1.2=Common Development and Distribution License (CDDL) -javax.servlet--servlet-api--2.5=Common Development and Distribution License (CDDL) +# https://github.com/albfernandez/juniversalchardet/blob/main/LICENSE +com.github.albfernandez--juniversalchardet--2.4.0=Mozilla Public License # https://github.com/jankotek/JDBM3 jdbm--jdbm--1.0=Apache Software License, Version 2.0 -# https://github.com/hunterhacker/jdom/blob/master/LICENSE.txt -# http://www.jdom.org/docs/faq.html#a0030 -jdom--jdom--1.0=JDOM License (Apache-style license) -jdom--jdom--1.1.3=JDOM License (Apache-style license) - # https://github.com/stephenc/jcip-annotations/blob/master/LICENSE.txt net.jcip--jcip-annotations--1.0=Apache Software License, Version 2.0 -# http://ant.apache.org/ -org.apache.ant--ant--1.7.0=Apache Software License, Version 2.0 -org.apache.ant--ant-launcher--1.7.0=Apache Software License, Version 2.0 - -# http://zookeeper.apache.org/ -org.apache.zookeeper--zookeeper--3.4.6=Apache Software License, Version 2.0 - -# http://jettison.codehaus.org/ -org.codehaus.jettison--jettison--1.1=Apache Software License, Version 2.0 - -# DSpace Licenses are all BSD -org.dspace--handle--6.2=BSD License -org.dspace--jargon--1.4.25=BSD License -org.dspace--mets--1.5.2=BSD License -org.dspace--oclc-harvester2--0.1.12=BSD License - -# https://github.com/hibernate/hibernate-jpa-api -org.hibernate.javax.persistence--hibernate-jpa-2.0-api--1.0.1.Final=Eclipse Public License - -# Swingworker: https://java.net/projects/swingworker -org.jdesktop--swing-worker--1.1=GNU Lesser General Public License (LGPL) - -# https://java.net/projects/tiger-types -org.jvnet--tiger-types--1.4=Common Development and Distribution License (CDDL) - -# https://maven-repository.com/ -org.restlet.jee--org.restlet--2.1.1=Apache Software License, Version 2.0 -org.restlet.jee--org.restlet.ext.servlet--2.1.1=Apache Software License, Version 2.0 - -# http://swordapp.org/ -org.swordapp--sword-common--1.1=Apache Software License, Version 2.0 - -# Apache Jakarta ORO: http://svn.apache.org/repos/asf/jakarta/oro/trunk/LICENSE -oro--oro--2.0.8=Apache Software License, Version 2.0 - -# http://rometools.org/ -rome--rome--1.0=Apache Software License, Version 2.0 - -# https://tomcat.apache.org/taglibs/license.html -taglibs--standard--1.1.2=Apache Software License, Version 2.0 - -# Apache XML (xalan): http://xml.apache.org/xalan-j/#license -xalan--xalan--2.7.0=Apache Software License, Version 2.0 - -# Apache Xerces: http://xerces.apache.org/ -xerces--xmlParserAPIs--2.6.2=Apache Software License, Version 2.0 - -# Apache XML Commons: http://xerces.apache.org/xml-commons/licenses.html -xml-apis--xml-apis--1.4.01=Apache Software License, Version 2.0 -xml-apis--xmlParserAPIs--2.0.2=Apache Software License, Version 2.0 - -# http://www.xom.nu/ -xom--xom--1.1=GNU Lesser General Public License (LGPL) +# Jersey is dual licensed EPL and GPL. We use EPL +# https://eclipse-ee4j.github.io/jersey.github.io/license.html +org.glassfish.jersey.core--jersey-client--2.39.1=Eclipse Public License +org.glassfish.jersey.inject--jersey-hk2--2.39.1=Eclipse Public License From 98c661d8d07b3e3ae68d513252ab7f90ae72faf6 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 3 Jul 2024 14:17:05 -0500 Subject: [PATCH 234/479] Update redeployment of demo.dspace.org to use dspace-8_x branch. --- .github/workflows/reusable-docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 687474caed18..12aa0cfe2864 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -70,7 +70,7 @@ env: REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} # Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org # (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch) - DEPLOY_DEMO_BRANCH: 'dspace-7_x' + DEPLOY_DEMO_BRANCH: 'dspace-8_x' DEPLOY_ARCH: 'linux/amd64' jobs: From 08947bc34e60c93b2b0771b3fdb5347915cac28b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Jul 2024 14:10:01 -0500 Subject: [PATCH 235/479] [maven-release-plugin] prepare release dspace-7.6.2 --- 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 4c881cbd2465..a15a21e6bc07 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 8cba90552481..58ea58687e05 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 748f6ad2deef..a76a878454e8 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index f118ebddb378..43e637587947 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 5688424a1c26..1192f9ed7cdd 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.2-SNAPSHOT + 7.6.2 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.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 5395169950bc..feb8c2af6e4b 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 98e0253a498d..20110a814459 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 52783e3ec571..d9fd8843a542 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 8d618bdf5f81..2b6c75901e56 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 7e60e982ec45..0a4f43d84bf9 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 5819158505fd..d4f47af66a00 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 7797346c51db..3f4e99b482aa 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 564f81593777..d5882c9270c1 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.2-SNAPSHOT + 7.6.2 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index f9dc6126e60b..cc2968fab99f 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.2-SNAPSHOT + 7.6.2 ../pom.xml diff --git a/pom.xml b/pom.xml index 3419757b56dc..0670e2cf4832 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.2-SNAPSHOT + 7.6.2 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -874,14 +874,14 @@ org.dspace dspace-rest - 7.6.2-SNAPSHOT + 7.6.2 jar classes org.dspace dspace-rest - 7.6.2-SNAPSHOT + 7.6.2 war @@ -1032,69 +1032,69 @@ org.dspace dspace-api - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-api test-jar - 7.6.2-SNAPSHOT + 7.6.2 test org.dspace.modules additions - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-sword - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-swordv2 - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-oai - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-services - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-server-webapp test-jar - 7.6.2-SNAPSHOT + 7.6.2 test org.dspace dspace-rdf - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-iiif - 7.6.2-SNAPSHOT + 7.6.2 org.dspace dspace-server-webapp - 7.6.2-SNAPSHOT + 7.6.2 jar classes org.dspace dspace-server-webapp - 7.6.2-SNAPSHOT + 7.6.2 war @@ -1940,7 +1940,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 + dspace-7.6.2 From 86b28bf927d1614aa3d5b162c736fb834d25495d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Jul 2024 14:10:05 -0500 Subject: [PATCH 236/479] [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 a15a21e6bc07..16b151b092e5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 58ea58687e05..64dd5106f0af 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index a76a878454e8..28b610e996c1 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 43e637587947..e656a920a1e6 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 1192f9ed7cdd..d1f3b95aafb0 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.2 + 7.6.3-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.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index feb8c2af6e4b..e0ec7ef5ed76 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 20110a814459..51c6dea62413 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index d9fd8843a542..1403c779e720 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 2b6c75901e56..470388883126 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 0a4f43d84bf9..db9343804013 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index d4f47af66a00..3f15bdd01f4f 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 3f4e99b482aa..e2e0041cb298 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index d5882c9270c1..5134ffb94733 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.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index cc2968fab99f..b3b474bfbcbc 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 0670e2cf4832..b662e333225d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.2 + 7.6.3-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -874,14 +874,14 @@ org.dspace dspace-rest - 7.6.2 + 7.6.3-SNAPSHOT jar classes org.dspace dspace-rest - 7.6.2 + 7.6.3-SNAPSHOT war @@ -1032,69 +1032,69 @@ org.dspace dspace-api - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-api test-jar - 7.6.2 + 7.6.3-SNAPSHOT test org.dspace.modules additions - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-sword - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-swordv2 - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-oai - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-services - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6.2 + 7.6.3-SNAPSHOT test org.dspace dspace-rdf - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-iiif - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-server-webapp - 7.6.2 + 7.6.3-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6.2 + 7.6.3-SNAPSHOT war @@ -1940,7 +1940,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.2 + dspace-7_x From 17a46a2fb6c2cb63bedc475caf9bf3953d9f76ce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 10 Jul 2024 12:24:25 -0500 Subject: [PATCH 237/479] Remove unused services & unnecessary cache cleanup. This can result in random failures if these services are not yet loaded by another test. --- .../controller/LinksetRestControllerIT.java | 33 ------------------- 1 file changed, 33 deletions(-) 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 6d1d242cad7f..a65357f97bfe 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 @@ -40,15 +40,12 @@ import org.dspace.content.RelationshipType; import org.dspace.content.WorkspaceItem; import org.dspace.content.authority.Choices; -import org.dspace.content.authority.service.ChoiceAuthorityService; -import org.dspace.content.authority.service.MetadataAuthorityService; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.util.SimpleMapConverter; import org.hamcrest.Matchers; import org.junit.Before; @@ -67,12 +64,6 @@ public class LinksetRestControllerIT extends AbstractControllerIntegrationTest { @Autowired private ConfigurationService configurationService; - @Autowired - private MetadataAuthorityService metadataAuthorityService; - - @Autowired - private ChoiceAuthorityService choiceAuthorityService; - @Autowired private ItemService itemService; @@ -735,10 +726,6 @@ public void findTypedLinkForBitstream() throws Exception { .andExpect(jsonPath("$[?(@.href == '" + uiUrl + "/signposting/linksets/" + item.getID() + "/json" + "' && @.rel == 'linkset' " + "&& @.type == 'application/linkset+json')]").exists()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -780,10 +767,6 @@ public void findTypedLinkForBitstreamWithType() throws Exception { "&& @.type == 'application/linkset+json')]").exists()) .andExpect(jsonPath("$[?(@.href == 'https://schema.org/ScholarlyArticle' " + "&& @.rel == 'type')]").exists()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -813,10 +796,6 @@ public void findTypedLinkForRestrictedBitstream() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -844,10 +823,6 @@ public void findTypedLinkForBitstreamUnderEmbargo() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -874,10 +849,6 @@ public void findTypedLinkForBitstreamOfWorkspaceItem() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -890,10 +861,6 @@ public void findTypedLinkForUnDiscoverableItem() throws Exception { getClient().perform(get("/signposting/links/" + item.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test From 3bb93be79310fc7931359d0cc82bbfa87f9cec85 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 10 Jul 2024 14:21:59 -0500 Subject: [PATCH 238/479] Fix random pagination failures in ManageGroupsFeatureIT by using the "feature" param to filter for the feature we are looking for. If this feature appeared on page 2, then the tests would fail. --- .../authorization/ManageGroupsFeatureIT.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java index a42f219909aa..1997a6147f3a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java @@ -176,7 +176,7 @@ public void testSubGroupOfAdminGroup() throws Exception { // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -285,7 +285,7 @@ public void testSubSubGroupOfAdminGroup() throws Exception { // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -502,7 +502,7 @@ public void testSubGroupOfAdminGroupNoCommunityGroupPermission() throws Exceptio // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -636,7 +636,7 @@ public void testSubSubGroupOfAdminGroupNoCommunityGroupPermission() throws Excep // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -897,7 +897,7 @@ public void testSubGroupOfAdminGroupNoCollectionGroupPermission() throws Excepti // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1051,7 +1051,7 @@ public void testSubSubGroupOfAdminGroupNoCollectionGroupPermission() throws Exce // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1352,7 +1352,7 @@ public void testSubGroupOfAdminGroupNoComColGroupPermission() throws Exception { // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1526,7 +1526,7 @@ public void testSubSubGroupOfAdminGroupNoComColGroupPermission() throws Exceptio // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") From 83f3ea4d052859994f66b881e4bd692dd552e576 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:33:38 +0200 Subject: [PATCH 239/479] fix FromAsCasing warning --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ee48dec5083c..9d89710fe11c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:dspace-7_x AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -28,7 +28,7 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From f340dfadbec9143009fbb670296c500e05ee00e9 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:34:52 +0200 Subject: [PATCH 240/479] fix FromAsCasing warning --- Dockerfile.cli | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.cli b/Dockerfile.cli index 53040a2fad89..8a69b32e2dc6 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -8,7 +8,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:dspace-7_x AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -24,7 +24,7 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From 9b6b7ded97d542f034f95159ce402ecb527a2e7a Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:35:21 +0200 Subject: [PATCH 241/479] fix FromAsCasing warning --- Dockerfile.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.test b/Dockerfile.test index f6f8c1a290f9..031394ad256c 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -10,7 +10,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:dspace-7_x AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -27,7 +27,7 @@ RUN mvn --no-transfer-progress package -Pdspace-rest && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From e4209d5d0b6ac0a301ccf080136e05005e68af11 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:35:54 +0200 Subject: [PATCH 242/479] fix FromAsCasing warning --- Dockerfile.dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 1400b356d418..123206ea5887 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM maven:3-eclipse-temurin-${JDK_VERSION} as build +FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory From 03cff072c30888979e89a8c2f8adc4900f269686 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 22 Jul 2024 15:08:59 +0200 Subject: [PATCH 243/479] 116542: fix issues with CSV importing and the Any language being set on metadata values --- .../org/dspace/administer/StructBuilder.java | 2 +- .../org/dspace/app/bulkedit/DSpaceCSV.java | 18 ++++++ .../java/org/dspace/content/Collection.java | 2 +- .../org/dspace/content/MetadataValue.java | 4 ++ .../dspace/app/bulkedit/MetadataImportIT.java | 27 +++++++- .../builder/AbstractDSpaceObjectBuilder.java | 3 +- .../dspace/content/ItemComparatorTest.java | 64 +++++++++---------- .../java/org/dspace/content/ItemTest.java | 38 +++++------ .../content/dao/RelationshipDAOImplTest.java | 4 +- .../dao/RelationshipTypeDAOImplTest.java | 4 +- .../org/dspace/app/rest/PatchMetadataIT.java | 2 +- .../rest/RelationshipRestRepositoryIT.java | 26 ++++---- 12 files changed, 120 insertions(+), 74 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index 13a1b3b5bbf8..8bbcfe0ff753 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -802,7 +802,7 @@ private static Element[] handleCollections(Context context, // default the short description to the empty string collectionService.setMetadataSingleValue(context, collection, - MD_SHORT_DESCRIPTION, Item.ANY, " "); + MD_SHORT_DESCRIPTION, null, " "); // import the rest of the metadata for (Map.Entry entry : collectionMap.entrySet()) { diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java index cbc052b5573f..3533a2397b3d 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java @@ -188,6 +188,15 @@ public DSpaceCSV(InputStream inputStream, Context c) throws Exception { // Verify that the heading is valid in the metadata registry String[] clean = element.split("\\["); String[] parts = clean[0].split("\\."); + // Check language if present, if it's ANY then throw an exception + if (clean.length > 1 && clean[1].equals(Item.ANY + "]")) { + throw new MetadataImportInvalidHeadingException("Language ANY (*) was found in the heading " + + "of the metadata value to import, " + + "this should never be the case", + MetadataImportInvalidHeadingException.ENTRY, + columnCounter); + + } if (parts.length < 2) { throw new MetadataImportInvalidHeadingException(element, @@ -223,6 +232,15 @@ public DSpaceCSV(InputStream inputStream, Context c) throws Exception { } } + // Verify there isn’t already a header that is the same; if it already exists, + // throw MetadataImportInvalidHeadingException + String header = authorityPrefix + element; + if (headings.contains(header)) { + throw new MetadataImportInvalidHeadingException("Duplicate heading found: " + header, + MetadataImportInvalidHeadingException.ENTRY, + columnCounter); + } + // Store the heading headings.add(authorityPrefix + element); } diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java index ffec3b45cc87..0036230a8b9d 100644 --- a/dspace-api/src/main/java/org/dspace/content/Collection.java +++ b/dspace-api/src/main/java/org/dspace/content/Collection.java @@ -230,7 +230,7 @@ public String getLicenseCollection() { * @throws SQLException if database error */ public void setLicense(Context context, String license) throws SQLException { - getCollectionService().setMetadataSingleValue(context, this, MD_LICENSE, Item.ANY, license); + getCollectionService().setMetadataSingleValue(context, this, MD_LICENSE, null, license); } /** diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java index 9ff3cb9ec2af..56ed81ecbac8 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java @@ -20,6 +20,7 @@ import javax.persistence.SequenceGenerator; import javax.persistence.Table; +import org.apache.commons.lang3.StringUtils; import org.dspace.core.Context; import org.dspace.core.ReloadableEntity; import org.hibernate.annotations.Type; @@ -142,6 +143,9 @@ public String getLanguage() { * @param language new language */ public void setLanguage(String language) { + if (StringUtils.equals(language, Item.ANY)) { + language = null; + } this.language = language; } 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 ac5e1e6ae6b9..481aa09e531f 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 @@ -75,6 +75,31 @@ public void setUp() throws Exception { context.restoreAuthSystemState(); } + @Test + public void metadataImportTestWithDuplicateHeader() { + String[] csv = {"id,collection,dc.title,dc.title,dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\",\"Test Import 2\"," + "\"Donald, SmithImported\"," + + "+," + collection.getHandle() + ",\"Test Import 3\",\"Test Import 4\"," + "\"Donald, SmithImported\""}; + // Should throw an exception because of duplicate header + try { + performImportScript(csv); + } catch (Exception e) { + assertTrue(e instanceof MetadataImportInvalidHeadingException); + } + } + + @Test + public void metadataImportTestWithAnyLanguage() { + String[] csv = {"id,collection,dc.title[*],dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\"," + "\"Donald, SmithImported\""}; + // Should throw an exception because of invalid ANY language (*) in metadata field + try { + performImportScript(csv); + } catch (Exception e) { + assertTrue(e instanceof MetadataImportInvalidHeadingException); + } + } + @Test public void metadataImportTest() throws Exception { String[] csv = {"id,collection,dc.title,dc.contributor.author", @@ -228,7 +253,7 @@ public void metadataImportRemovingValueTest() throws Exception { itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY).get(0).getValue(), "TestAuthorToRemove")); - String[] csv = {"id,collection,dc.title,dc.contributor.author[*]", + String[] csv = {"id,collection,dc.title,dc.contributor.author", item.getID().toString() + "," + personCollection.getHandle() + "," + item.getName() + ","}; performImportScript(csv); item = findItemByName("title"); 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 ff1083d318d9..c6106c3a75e3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java @@ -14,7 +14,6 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.content.DSpaceObject; -import org.dspace.content.Item; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -103,7 +102,7 @@ protected > B setMetadataSingleValue(fi final String qualifier, final String value) { try { - getService().setMetadataSingleValue(context, dso, schema, element, qualifier, Item.ANY, value); + getService().setMetadataSingleValue(context, dso, schema, element, qualifier, null, value); } catch (Exception e) { return handleException(e); } diff --git a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java index 54ff9ce02624..be670d9b5097 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java @@ -141,37 +141,37 @@ public void testCompare() throws SQLException { assertTrue("testCompare 0", result == 0); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 1", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 2", result <= -1); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); //value in both items ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); @@ -179,60 +179,60 @@ public void testCompare() throws SQLException { //multiple values (min, max) ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "0"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "3"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "0"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "3"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "0"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "-1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "0"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "-1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "-1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "-1"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "3"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "3"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "5"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "5"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "3"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "4"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "3"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "4"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index 15e425e23a2a..24d6e515bbdf 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -516,11 +516,11 @@ public void testAddMetadata_5args_1() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String[] values = {"value0", "value1"}; itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_5args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_5args_1 1", dc.size() == 2); assertThat("testAddMetadata_5args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -550,13 +550,13 @@ public void testAddMetadata_7args_1_authority() String schema = "dc"; String element = "language"; String qualifier = "iso"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("en_US", "en"); List authorities = Arrays.asList("accepted", "uncertain"); List confidences = Arrays.asList(0, 0); itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_1 1", dc.size() == 2); assertThat("testAddMetadata_7args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -587,13 +587,13 @@ public void testAddMetadata_7args_1_noauthority() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("value0", "value1"); List authorities = Arrays.asList("auth0", "auth2"); List confidences = Arrays.asList(0, 0); itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_1 1", dc.size() == 2); assertThat("testAddMetadata_7args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -622,11 +622,11 @@ public void testAddMetadata_5args_2() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("value0", "value1"); itemService.addMetadata(context, it, schema, element, qualifier, lang, values); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_5args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_5args_2 1", dc.size() == 2); assertThat("testAddMetadata_5args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -654,13 +654,13 @@ public void testAddMetadata_7args_2_authority() throws SQLException { String schema = "dc"; String element = "language"; String qualifier = "iso"; - String lang = Item.ANY; + String lang = null; String values = "en"; String authorities = "accepted"; int confidences = 0; itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_2 1", dc.size() == 1); assertThat("testAddMetadata_7args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -683,13 +683,13 @@ public void testAddMetadata_7args_2_noauthority() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "editor"; - String lang = Item.ANY; + String lang = null; String values = "value0"; String authorities = "auth0"; int confidences = 0; itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_2 1", dc.size() == 1); assertThat("testAddMetadata_7args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -710,13 +710,13 @@ public void testClearMetadata() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String values = "value0"; itemService.addMetadata(context, it, schema, element, qualifier, lang, values); - itemService.clearMetadata(context, it, schema, element, qualifier, lang); + itemService.clearMetadata(context, it, schema, element, qualifier, Item.ANY); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testClearMetadata 0", dc, notNullValue()); assertTrue("testClearMetadata 1", dc.size() == 0); } @@ -758,11 +758,11 @@ public void testGetCollections() throws Exception { context.turnOffAuthorisationSystem(); Collection collection = collectionService.create(context, owningCommunity); collectionService.setMetadataSingleValue(context, collection, MetadataSchemaEnum.DC.getName(), - "title", null, Item.ANY, "collection B"); + "title", null, null, "collection B"); it.addCollection(collection); collection = collectionService.create(context, owningCommunity); collectionService.setMetadataSingleValue(context, collection, MetadataSchemaEnum.DC.getName(), - "title", null, Item.ANY, "collection A"); + "title", null, null, "collection A"); it.addCollection(collection); context.restoreAuthSystemState(); assertThat("testGetCollections 0", it.getCollections(), notNullValue()); @@ -1602,7 +1602,7 @@ public void testFindByMetadataField() throws Exception { // add new metadata to item context.turnOffAuthorisationSystem(); - itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value); + itemService.addMetadata(context, it, schema, element, qualifier, null, value); itemService.update(context, it); context.restoreAuthSystemState(); @@ -1667,7 +1667,7 @@ public void testFindByAuthorityValue() throws Exception { // add new metadata (with authority) to item context.turnOffAuthorisationSystem(); - itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value, authority, confidence); + itemService.addMetadata(context, it, schema, element, qualifier, null, value, authority, confidence); itemService.update(context, it); context.restoreAuthSystemState(); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java index b6f5da6be065..05e5ba0e1162 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java @@ -87,8 +87,8 @@ public void init() { WorkspaceItem workspaceItemTwo = workspaceItemService.create(context, collection, false); itemOne = installItemService.installItem(context, workspaceItem); itemTwo = installItemService.installItem(context, workspaceItemTwo); - itemService.addMetadata(context, itemOne, "dspace", "entity", "type", Item.ANY, "Publication"); - itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", Item.ANY, "Person"); + itemService.addMetadata(context, itemOne, "dspace", "entity", "type", null, "Publication"); + itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", null, "Person"); itemService.update(context, itemOne); itemService.update(context, itemTwo); entityTypeOne = entityTypeService.create(context, "Person"); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java index 3fff6fec4762..4d405c6a27e7 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java @@ -82,8 +82,8 @@ public void init() { WorkspaceItem workspaceItemTwo = workspaceItemService.create(context, collection, false); itemOne = installItemService.installItem(context, workspaceItem); itemTwo = installItemService.installItem(context, workspaceItemTwo); - itemService.addMetadata(context, itemOne, "dspace", "entity", "type", Item.ANY, "Publication"); - itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", Item.ANY, "Person"); + itemService.addMetadata(context, itemOne, "dspace", "entity", "type", null, "Publication"); + itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", null, "Person"); itemService.update(context, itemOne); itemService.update(context, itemTwo); entityTypeOne = entityTypeService.create(context, "Person"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index b78436f1fb38..f3ccfe9fc10e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -247,7 +247,7 @@ private void initSimplePublicationItem() throws Exception { for (String author : authorsOriginalOrder) { itemService.addMetadata( - context, publicationItem, "dc", "contributor", "author", Item.ANY, author + context, publicationItem, "dc", "contributor", "author", null, author ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java index d8e53c770c70..264fb0d246ad 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java @@ -819,7 +819,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { // Make sure we grab the latest instance of the Item from the database publication1 = itemService.find(context, publication1.getID()); // Add a plain text dc.contributor.author value - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -885,7 +885,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { // Ensure we have the latest instance of the Item from the database publication1 = itemService.find(context, publication1.getID()); // Add a fourth dc.contributor.author mdv - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -954,7 +954,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { context.turnOffAuthorisationSystem(); // The following additions of Metadata will perform the same sequence of logic and tests as described above publication1 = itemService.find(context, publication1.getID()); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -984,10 +984,10 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text four"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text five"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text six"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text seven"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text four"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text five"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text six"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text seven"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -1115,7 +1115,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { publication1 = itemService.find(context, publication1.getID()); // Add a plain text metadatavalue to the publication // After this addition, the list of authors should like like "Donald Smith", "plain text" - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); context.restoreAuthSystemState(); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1158,7 +1158,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { // Get the publication from the DB again to ensure that we have the latest object publication1 = itemService.find(context, publication1.getID()); // Add a fourth metadata value to the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1196,7 +1196,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); // Create another plain text metadata value on the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1324,7 +1324,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { publication1 = itemService.find(context, publication1.getID()); // Add a plain text metadatavalue to the publication // After this addition, the list of authors should like like "Donald Smith", "plain text" - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); context.restoreAuthSystemState(); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1368,7 +1368,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { // Get the publication from the DB again to ensure that we have the latest object publication1 = itemService.find(context, publication1.getID()); // Add a fourth metadata value to the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1406,7 +1406,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); // Create another plain text metadata value on the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); From 7082e3259595dbcbd31f339c53a15c17cd47110b Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 22 Jul 2024 15:18:44 +0200 Subject: [PATCH 244/479] 116542: resolve issues after merge with latest 7-x branch --- .../test/java/org/dspace/content/ItemTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index 87f42cf42935..00dbf2994d98 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -544,7 +544,7 @@ public void testAddMetadata_5args_no_values() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String[] values = {}; itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); fail("IllegalArgumentException expected"); @@ -632,7 +632,7 @@ public void testAddMetadata_7args_no_values() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = new ArrayList(); List authorities = new ArrayList(); List confidences = new ArrayList(); @@ -645,7 +645,7 @@ public void testAddMetadata_list_with_virtual_metadata() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; // Create two fake virtual metadata ("virtual::[relationship-id]") values List values = new ArrayList<>(Arrays.asList("uuid-1", "uuid-2")); List authorities = new ArrayList<>(Arrays.asList(Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1", @@ -674,7 +674,7 @@ public void testAddMetadata_list_with_virtual_metadata() throws Exception { assertEquals(1, valuesAdded.size()); // Get metadata and ensure new value is the ONLY ONE for this metadata field - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(dc); assertEquals(1, dc.size()); assertEquals(schema, dc.get(0).getMetadataField().getMetadataSchema().getName()); @@ -693,7 +693,7 @@ public void testAddMetadata_5args_2() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String value = "value0"; itemService.addMetadata(context, it, schema, element, qualifier, lang, value); @@ -772,7 +772,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; // Create a single fake virtual metadata ("virtual::[relationship-id]") value String value = "uuid-1"; String authority = Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1"; @@ -786,7 +786,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { assertNull(valuesAdded); // Verify this metadata field does NOT exist on the item - List mv = itemService.getMetadata(it, schema, element, qualifier, lang); + List mv = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(mv); assertTrue(mv.isEmpty()); @@ -797,7 +797,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { assertNull(valuesAdded); // Verify this metadata field does NOT exist on the item - mv = itemService.getMetadata(it, schema, element, qualifier, lang); + mv = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(mv); assertTrue(mv.isEmpty()); } From fff8cc3cb4f05ace42b035b3eb2c3a9b46f3b5f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:34:41 +0000 Subject: [PATCH 245/479] Bump dnsjava:dnsjava from 2.1.9 to 3.6.0 in /dspace-api Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 2.1.9 to 3.6.0. - [Release notes](https://github.com/dnsjava/dnsjava/releases) - [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog) - [Commits](https://github.com/dnsjava/dnsjava/compare/v2.1.9...v3.6.0) --- updated-dependencies: - dependency-name: dnsjava:dnsjava dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit 1775c88919cacf063788e4a6a0b67cdf1ef23fec) --- 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 16b151b092e5..bdf035179bee 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -629,7 +629,7 @@ dnsjava dnsjava - 2.1.9 + 3.6.0 From 30b6eddeb66c0e89c951090d8c2b2d6d0f3a31ab Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 23 Jul 2024 12:17:07 +0200 Subject: [PATCH 246/479] Update spider list URLs to satisfy cloudflare redirects Update spider list URLs to satisfy cloudflare redirects Update spider list URLs to satisfy cloudflare redirects (cherry picked from commit d22ea117ca6970fd52b296ac18d3f71878c1a857) --- dspace/config/modules/solr-statistics.cfg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dspace/config/modules/solr-statistics.cfg b/dspace/config/modules/solr-statistics.cfg index 073850ca232e..632ff5078b48 100644 --- a/dspace/config/modules/solr-statistics.cfg +++ b/dspace/config/modules/solr-statistics.cfg @@ -32,10 +32,10 @@ solr-statistics.configset = statistics solr-statistics.autoCommit = true # URLs to download IP addresses of search engine spiders from -solr-statistics.spiderips.urls = http://iplists.com/google.txt, \ - http://iplists.com/inktomi.txt, \ - http://iplists.com/lycos.txt, \ - http://iplists.com/infoseek.txt, \ - http://iplists.com/altavista.txt, \ - http://iplists.com/excite.txt, \ - http://iplists.com/misc.txt +solr-statistics.spiderips.urls = https://www.iplists.com/google.txt, \ + https://www.iplists.com/inktomi.txt, \ + https://www.iplists.com/lycos.txt, \ + https://www.iplists.com/infoseek.txt, \ + https://www.iplists.com/altavista.txt, \ + https://www.iplists.com/excite.txt, \ + https://www.iplists.com/misc.txt From affa4b00ff59cffa69d47de6a3a7d4137008dd40 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 24 Jul 2024 19:07:44 +0200 Subject: [PATCH 247/479] [CST-14905] Orcid revoke token feature --- .../org/dspace/orcid/client/OrcidClient.java | 8 + .../dspace/orcid/client/OrcidClientImpl.java | 37 +++++ .../orcid/client/OrcidConfiguration.java | 9 ++ .../impl/OrcidSynchronizationServiceImpl.java | 14 ++ .../ResearcherProfileRestRepositoryIT.java | 146 ++++++++++++++++-- dspace/config/modules/orcid.cfg | 1 + dspace/config/spring/api/orcid-services.xml | 1 + 7 files changed, 200 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java index 99d1920aa53a..d21f61a922f5 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Optional; +import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.orcid.jaxb.model.v3.release.record.Person; @@ -161,4 +162,11 @@ public interface OrcidClient { */ OrcidResponse deleteByPutCode(String accessToken, String orcid, String putCode, String path); + /** + * Revokes the given {@param accessToken} with a POST method. + * @param orcidToken the access token to revoke + * @throws OrcidClientException if some error occurs during the search + */ + void revokeToken(OrcidToken orcidToken); + } diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 3e7ca7b21029..1532abe63412 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -42,6 +42,7 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; +import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidEntityType; import org.dspace.orcid.model.OrcidProfileSectionType; @@ -178,6 +179,16 @@ public OrcidResponse deleteByPutCode(String accessToken, String orcid, String pu return execute(buildDeleteUriRequest(accessToken, "/" + orcid + path + "/" + putCode), true); } + @Override + public void revokeToken(OrcidToken orcidToken) { + List params = new ArrayList<>(); + params.add(new BasicNameValuePair("client_id", orcidConfiguration.getClientId())); + params.add(new BasicNameValuePair("client_secret", orcidConfiguration.getClientSecret())); + params.add(new BasicNameValuePair("token", orcidToken.getAccessToken())); + + executeSuccessful(buildPostForRevokeToken(new UrlEncodedFormEntity(params, Charset.defaultCharset()))); + } + @Override public OrcidTokenResponseDTO getReadPublicAccessToken() { return getClientCredentialsAccessToken("/read-public"); @@ -220,6 +231,14 @@ private HttpUriRequest buildPostUriRequest(String accessToken, String relativePa .build(); } + private HttpUriRequest buildPostForRevokeToken(HttpEntity entity) { + return post(orcidConfiguration.getRevokeUrl()) + .addHeader("Accept", "application/json") + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .setEntity(entity) + .build(); + } + private HttpUriRequest buildPutUriRequest(String accessToken, String relativePath, Object object) { return put(orcidConfiguration.getApiUrl() + relativePath.trim()) .addHeader("Content-Type", "application/vnd.orcid+xml") @@ -234,6 +253,24 @@ private HttpUriRequest buildDeleteUriRequest(String accessToken, String relative .build(); } + private void executeSuccessful(HttpUriRequest httpUriRequest) { + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpResponse response = client.execute(httpUriRequest); + + if (isNotSuccessfull(response)) { + throw new OrcidClientException( + getStatusCode(response), + "Operation " + httpUriRequest.getMethod() + " for the resource " + httpUriRequest.getURI() + + " was not successful: " + new String(response.getEntity().getContent().readAllBytes(), + StandardCharsets.UTF_8) + ); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { HttpClient client = HttpClientBuilder.create().build(); diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java index 550b0215c435..dfa90fcae03a 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java @@ -35,6 +35,8 @@ public final class OrcidConfiguration { private String scopes; + private String revokeUrl; + public String getApiUrl() { return apiUrl; } @@ -111,4 +113,11 @@ public boolean isApiConfigured() { return !StringUtils.isAnyBlank(clientId, clientSecret); } + public String getRevokeUrl() { + return revokeUrl; + } + + public void setRevokeUrl(String revokeUrl) { + this.revokeUrl = revokeUrl; + } } diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java index 97d832d3de82..a302c58f2c5e 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java @@ -37,6 +37,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.service.EPersonService; import org.dspace.orcid.OrcidToken; +import org.dspace.orcid.client.OrcidClient; import org.dspace.orcid.model.OrcidEntityType; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.dspace.orcid.service.OrcidSynchronizationService; @@ -47,6 +48,8 @@ import org.dspace.profile.OrcidSynchronizationMode; import org.dspace.profile.service.ResearcherProfileService; import org.dspace.services.ConfigurationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; /** @@ -57,6 +60,7 @@ */ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationService { + private static final Logger log = LoggerFactory.getLogger(OrcidSynchronizationServiceImpl.class); @Autowired private ItemService itemService; @@ -75,6 +79,9 @@ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationServ @Autowired private ResearcherProfileService researcherProfileService; + @Autowired + private OrcidClient orcidClient; + @Override public void linkProfile(Context context, Item profile, OrcidTokenResponseDTO token) throws SQLException { @@ -118,7 +125,14 @@ public void unlinkProfile(Context context, Item profile) throws SQLException { itemService.clearMetadata(context, profile, "dspace", "orcid", "scope", Item.ANY); itemService.clearMetadata(context, profile, "dspace", "orcid", "authenticated", Item.ANY); + OrcidToken profileToken = orcidTokenService.findByProfileItem(context, profile); + if (profileToken == null) { + log.warn("Cannot find any token related to the user profile: {}", profile.getID()); + return; + } + orcidTokenService.deleteByProfileItem(context, profile); + orcidClient.revokeToken(profileToken); updateItem(context, profile); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java index 70009d049fc5..0d46f4268cae 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java @@ -30,7 +30,10 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -44,6 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; import java.sql.SQLException; import java.util.List; import java.util.UUID; @@ -79,11 +83,13 @@ import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.dspace.orcid.service.OrcidQueueService; +import org.dspace.orcid.service.OrcidSynchronizationService; import org.dspace.orcid.service.OrcidTokenService; import org.dspace.services.ConfigurationService; import org.dspace.util.UUIDUtils; import org.junit.After; import org.junit.Test; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; @@ -114,7 +120,11 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra @Autowired private OrcidClient orcidClient; - private OrcidClient orcidClientMock = mock(OrcidClient.class); + @Mock + private OrcidClient orcidClientMock; + + @Autowired + private OrcidSynchronizationService orcidSynchronizationService; private EPerson user; @@ -158,16 +168,36 @@ public void setUp() throws Exception { context.restoreAuthSystemState(); - researcherProfileAddOrcidOperation.setOrcidClient(orcidClientMock); - + useInstanceForBean(orcidSynchronizationService, orcidClientMock); + useInstanceForBean(researcherProfileAddOrcidOperation, orcidClientMock); } @After public void after() { orcidTokenService.deleteAll(context); - researcherProfileAddOrcidOperation.setOrcidClient(orcidClient); + useInstanceForBean(orcidSynchronizationService, orcidClient); + useInstanceForBean(researcherProfileAddOrcidOperation, orcidClient); + } + + private void useInstanceForBean(B bean, I instance) { + Field[] fields = bean.getClass().getDeclaredFields(); + + for (Field field : fields) { + if (field.getType().isAssignableFrom(instance.getClass())) { + boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + field.set(bean, instance); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + field.setAccessible(accessible); + } + } + } } + /** * Verify that the findById endpoint returns the own profile. * @@ -608,30 +638,38 @@ public void testDeleteWithProfileLinkedWithOrcid() throws Exception { .withOrcidAuthenticated("authenticated") .build(); + context.restoreAuthSystemState(); + String id = user.getID().toString(); String authToken = getAuthToken(user.getEmail(), password); + OrcidToken orcidToken = orcidTokenService.findByProfileItem(context, profileItem); - context.restoreAuthSystemState(); - - getClient(authToken).perform(get("/api/eperson/profiles/{id}", id)) + getClient(authToken) + .perform(get("/api/eperson/profiles/{id}", id)) .andExpect(status().isOk()); assertThat(profileItem.getMetadata(), hasItem(with("person.identifier.orcid", "0000-1111-2222-3333"))); assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.authenticated", "authenticated"))); - assertThat(getOrcidAccessToken(profileItem), notNullValue()); + assertThat(orcidToken.getAccessToken(), notNullValue()); getClient(authToken).perform(get("/api/eperson/profiles/{id}/item", id)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasJsonPath("$.metadata", matchMetadataNotEmpty("dspace.object.owner")))); + getClient(authToken).perform(delete("/api/eperson/profiles/{id}", id)) .andExpect(status().isNoContent()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profileItem = context.reloadEntity(profileItem); + orcidToken = orcidTokenService.findByProfileItem(context, profileItem); + assertThat(profileItem.getMetadata(), not(hasItem(with("person.identifier.orcid", "0000-1111-2222-3333")))); assertThat(profileItem.getMetadata(), not(hasItem(with("dspace.orcid.authenticated", "authenticated")))); - assertThat(getOrcidAccessToken(profileItem), nullValue()); + assertThat(orcidToken, nullValue()); } @@ -1850,7 +1888,8 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -1872,6 +1911,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -1880,6 +1922,54 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration assertThat(getOrcidAccessToken(profile), nullValue()); } + @Test + public void testPatchToDisconnectProfileFromOrcidDoesntRevokeOrcidToken() throws Exception { + + configurationService.setProperty("orcid.disconnection.allowed-users", "admin_and_owner"); + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); + + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + + Item profile = createProfile(ePerson); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + + context.restoreAuthSystemState(); + + doThrow(new OrcidClientException(403, "")).when(orcidClientMock).revokeToken(any(OrcidToken.class)); + + getClient(getAuthToken(ePerson.getEmail(), password)) + .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) + .content(getPatchContent(asList(new RemoveOperation("/orcid")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isInternalServerError()); + + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + + profile = context.reloadEntity(profile); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + } + @Test public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration() throws Exception { @@ -2023,7 +2113,8 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2034,6 +2125,7 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration context.restoreAuthSystemState(); + getClient(getAuthToken(admin.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) @@ -2045,6 +2137,9 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2111,17 +2206,18 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .withPassword(password) .withNameInMetadata("Test", "User") .build(); - - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); + context.restoreAuthSystemState(); + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); - context.restoreAuthSystemState(); getClient(getAuthToken(ePerson.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) @@ -2134,6 +2230,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2159,7 +2258,8 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2170,6 +2270,7 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura context.restoreAuthSystemState(); + getClient(getAuthToken(admin.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) @@ -2181,6 +2282,10 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2627,4 +2732,13 @@ private OrcidTokenResponseDTO buildOrcidTokenResponse(String orcid, String acces return token; } + private static OrcidToken matchesToken(OrcidToken orcidToken) { + return argThat( + token -> + token != null && + orcidToken.getAccessToken().equals(token.getAccessToken()) && + orcidToken.getID().equals(token.getID()) + ); + } + } diff --git a/dspace/config/modules/orcid.cfg b/dspace/config/modules/orcid.cfg index cde819677447..93da0a5f4cb9 100644 --- a/dspace/config/modules/orcid.cfg +++ b/dspace/config/modules/orcid.cfg @@ -14,6 +14,7 @@ orcid.disconnection.allowed-users = admin_and_owner # ORCID API (https://github.com/ORCID/ORCID-Source/tree/master/orcid-api-web#endpoints) orcid.domain-url= https://sandbox.orcid.org orcid.authorize-url = ${orcid.domain-url}/oauth/authorize +orcid.revoke-url = ${orcid.domain-url}/oauth/revoke orcid.token-url = ${orcid.domain-url}/oauth/token orcid.api-url = https://api.sandbox.orcid.org/v3.0 orcid.public-url = https://pub.sandbox.orcid.org/v3.0 diff --git a/dspace/config/spring/api/orcid-services.xml b/dspace/config/spring/api/orcid-services.xml index e5fb002c314f..eb31acb29c4d 100644 --- a/dspace/config/spring/api/orcid-services.xml +++ b/dspace/config/spring/api/orcid-services.xml @@ -30,6 +30,7 @@ + From 26905b7d45f058f1bd8476f9e915af9a5be1547f Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Tue, 21 Nov 2023 11:51:25 +0100 Subject: [PATCH 248/479] [CST-14901][DSC-1357][#8662] Handles versioning for ORCID publications. feat: - ORCID publications waiting to be published are removed whenever a new version is created - ORCID publications already published will be updated with the ref to the last item version - ORCID consumer will process only latest item versions, ignoring all the other ones --- .../org/dspace/content/ItemServiceImpl.java | 42 ++++ .../dspace/content/service/ItemService.java | 10 + .../orcid/consumer/OrcidQueueConsumer.java | 51 +++-- .../org/dspace/orcid/dao/OrcidQueueDAO.java | 10 + .../orcid/dao/impl/OrcidQueueDAOImpl.java | 7 + .../orcid/service/OrcidQueueService.java | 10 + .../service/impl/OrcidQueueServiceImpl.java | 5 + .../dspace/versioning/VersioningConsumer.java | 35 +++- .../dspace/orcid/OrcidQueueConsumerIT.java | 185 ++++++++++++++++++ 9 files changed, 337 insertions(+), 18 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 ac6c0d43d710..ae7081f73a37 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -79,6 +79,9 @@ import org.dspace.orcid.service.OrcidTokenService; import org.dspace.profile.service.ResearcherProfileService; import org.dspace.services.ConfigurationService; +import org.dspace.versioning.Version; +import org.dspace.versioning.VersionHistory; +import org.dspace.versioning.service.VersionHistoryService; import org.dspace.versioning.service.VersioningService; import org.dspace.workflow.WorkflowItemService; import org.dspace.workflow.factory.WorkflowServiceFactory; @@ -171,6 +174,9 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Autowired(required = true) protected SubscribeService subscribeService; + @Autowired + private VersionHistoryService versionHistoryService; + protected ItemServiceImpl() { super(); } @@ -1920,4 +1926,40 @@ private void deleteOrcidQueueRecords(Context context, Item item) throws SQLExcep } } + @Override + public boolean isLatestVersion(Context context, Item item) throws SQLException { + + VersionHistory history = versionHistoryService.findByItem(context, item); + if (history == null) { + // not all items have a version history + // if an item does not have a version history, it is by definition the latest + // version + return true; + } + + // start with the very latest version of the given item (may still be in + // workspace) + Version latestVersion = versionHistoryService.getLatestVersion(context, history); + + // find the latest version of the given item that is archived + while (latestVersion != null && !latestVersion.getItem().isArchived()) { + latestVersion = versionHistoryService.getPrevious(context, history, latestVersion); + } + + // could not find an archived version of the given item + if (latestVersion == null) { + // this scenario should never happen, but let's err on the side of showing too + // many items vs. to little + // (see discovery.xml, a lot of discovery configs filter out all items that are + // not the latest version) + return true; + } + + // sanity check + assert latestVersion.getItem().isArchived(); + + return item.equals(latestVersion.getItem()); + + } + } 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 12867ad18c3f..a307e7ceffad 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 @@ -983,4 +983,14 @@ public List getMetadata(Item item, String schema, String element, */ public EntityType getEntityType(Context context, Item item) throws SQLException; + + /** + * Check whether the given item is the latest version. If the latest item cannot + * be determined, because either the version history or the latest version is + * not present, assume the item is latest. + * @param context the DSpace context. + * @param item the item that should be checked. + * @return true if the item is the latest version, false otherwise. + */ + public boolean isLatestVersion(Context context, Item item) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java b/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java index d177e61607f1..ae989e1dd8f8 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java +++ b/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java @@ -14,9 +14,10 @@ import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import java.sql.SQLException; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,7 +57,7 @@ * be synchronized (based on the preferences set by the user) *

  • are publications/fundings related to profile items linked to orcid (based * on the preferences set by the user)
  • - * + * * * * @author Luca Giamminonni (luca.giamminonni at 4science.it) @@ -82,7 +83,7 @@ public class OrcidQueueConsumer implements Consumer { private RelationshipService relationshipService; - private List alreadyConsumedItems = new ArrayList<>(); + private final Set itemsToConsume = new HashSet<>(); @Override public void initialize() throws Exception { @@ -117,17 +118,26 @@ public void consume(Context context, Event event) throws Exception { return; } - if (alreadyConsumedItems.contains(item.getID())) { - return; - } + itemsToConsume.add(item.getID()); + } + + @Override + public void end(Context context) throws Exception { + + for (UUID itemId : itemsToConsume) { + + Item item = itemService.find(context, itemId); + + context.turnOffAuthorisationSystem(); + try { + consumeItem(context, item); + } finally { + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - try { - consumeItem(context, item); - } finally { - context.restoreAuthSystemState(); } + itemsToConsume.clear(); } /** @@ -146,7 +156,7 @@ private void consumeItem(Context context, Item item) throws SQLException { consumeProfile(context, item); } - alreadyConsumedItems.add(item.getID()); + itemsToConsume.add(item.getID()); } @@ -169,6 +179,10 @@ private void consumeEntity(Context context, Item entity) throws SQLException { continue; } + if (isNotLatestVersion(context, entity)) { + continue; + } + orcidQueueService.create(context, relatedItem, entity); } @@ -329,6 +343,14 @@ private boolean isNotProfileItem(Item profileItemItem) { return !getProfileType().equals(itemService.getEntityTypeLabel(profileItemItem)); } + private boolean isNotLatestVersion(Context context, Item entity) { + try { + return !itemService.isLatestVersion(context, entity); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + private String getMetadataValue(Item item, String metadataField) { return itemService.getMetadataFirstValue(item, new MetadataFieldName(metadataField), Item.ANY); } @@ -345,11 +367,6 @@ private boolean isOrcidSynchronizationDisabled() { return !configurationService.getBooleanProperty("orcid.synchronization-enabled", true); } - @Override - public void end(Context context) throws Exception { - alreadyConsumedItems.clear(); - } - @Override public void finish(Context context) throws Exception { // nothing to do diff --git a/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java b/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java index 235443b15033..b7e0b1ed2a85 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java +++ b/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java @@ -74,6 +74,16 @@ public List findByProfileItemAndEntity(Context context, Item profile */ public List findByProfileItemOrEntity(Context context, Item item) throws SQLException; + /** + * Get the OrcidQueue records where the given item is the entity. + * + * @param context DSpace context object + * @param item the item to search for + * @return the found OrcidQueue entities + * @throws SQLException if database error + */ + public List findByEntity(Context context, Item item) throws SQLException; + /** * Find all the OrcidQueue records with the given entity and record type. * diff --git a/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java b/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java index 2114b2535759..8e941b056535 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java @@ -63,6 +63,13 @@ public List findByProfileItemOrEntity(Context context, Item item) th return query.getResultList(); } + @Override + public List findByEntity(Context context, Item item) throws SQLException { + Query query = createQuery(context, "FROM OrcidQueue WHERE entity.id = :itemId"); + query.setParameter("itemId", item.getID()); + return query.getResultList(); + } + @Override public List findByEntityAndRecordType(Context context, Item entity, String type) throws SQLException { Query query = createQuery(context, "FROM OrcidQueue WHERE entity = :entity AND recordType = :type"); diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java b/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java index 8de25e9caf1e..b667088eabb4 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java @@ -164,6 +164,16 @@ public List findByProfileItemAndEntity(Context context, Item profile */ public List findByProfileItemOrEntity(Context context, Item item) throws SQLException; + /** + * Get the OrcidQueue records where the given item is the entity. + * + * @param context DSpace context object + * @param item the item to search for + * @return the found OrcidQueue records + * @throws SQLException if database error + */ + public List findByEntity(Context context, Item item) throws SQLException; + /** * Get all the OrcidQueue records with attempts less than the given attempts. * diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java index d3300fea6606..261f8ef9a9f7 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java @@ -70,6 +70,11 @@ public List findByProfileItemOrEntity(Context context, Item item) th return orcidQueueDAO.findByProfileItemOrEntity(context, item); } + @Override + public List findByEntity(Context context, Item item) throws SQLException { + return orcidQueueDAO.findByEntity(context, item); + } + @Override public long countByProfileItemId(Context context, UUID profileItemId) throws SQLException { return orcidQueueDAO.countByProfileItemId(context, profileItemId); diff --git a/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java b/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java index 63b5391d0a28..27a81a157917 100644 --- a/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java +++ b/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java @@ -33,6 +33,11 @@ import org.dspace.discovery.IndexEventConsumer; import org.dspace.event.Consumer; import org.dspace.event.Event; +import org.dspace.orcid.OrcidHistory; +import org.dspace.orcid.OrcidQueue; +import org.dspace.orcid.factory.OrcidServiceFactory; +import org.dspace.orcid.service.OrcidHistoryService; +import org.dspace.orcid.service.OrcidQueueService; import org.dspace.versioning.factory.VersionServiceFactory; import org.dspace.versioning.service.VersionHistoryService; import org.dspace.versioning.utils.RelationshipVersioningUtils; @@ -58,6 +63,8 @@ public class VersioningConsumer implements Consumer { private RelationshipTypeService relationshipTypeService; private RelationshipService relationshipService; private RelationshipVersioningUtils relationshipVersioningUtils; + private OrcidQueueService orcidQueueService; + private OrcidHistoryService orcidHistoryService; @Override public void initialize() throws Exception { @@ -67,6 +74,8 @@ public void initialize() throws Exception { relationshipTypeService = ContentServiceFactory.getInstance().getRelationshipTypeService(); relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); relationshipVersioningUtils = VersionServiceFactory.getInstance().getRelationshipVersioningUtils(); + this.orcidQueueService = OrcidServiceFactory.getInstance().getOrcidQueueService(); + this.orcidHistoryService = OrcidServiceFactory.getInstance().getOrcidHistoryService(); } @Override @@ -132,7 +141,8 @@ public void consume(Context ctx, Event event) throws Exception { // unarchive previous item unarchiveItem(ctx, previousItem); - + // handles versions for ORCID publications waiting to be shipped, or already published (history-queue). + handleOrcidSynchronization(ctx, previousItem, latestItem); // update relationships updateRelationships(ctx, latestItem, previousItem); } @@ -148,6 +158,29 @@ protected void unarchiveItem(Context ctx, Item item) { )); } + private void handleOrcidSynchronization(Context ctx, Item previousItem, Item latestItem) { + try { + replaceOrcidHistoryEntities(ctx, previousItem, latestItem); + removeOrcidQueueEntries(ctx, previousItem); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private void removeOrcidQueueEntries(Context ctx, Item previousItem) throws SQLException { + List queueEntries = orcidQueueService.findByEntity(ctx, previousItem); + for (OrcidQueue queueEntry : queueEntries) { + orcidQueueService.delete(ctx, queueEntry); + } + } + + private void replaceOrcidHistoryEntities(Context ctx, Item previousItem, Item latestItem) throws SQLException { + List entries = orcidHistoryService.findByEntity(ctx, previousItem); + for (OrcidHistory entry : entries) { + entry.setEntity(latestItem); + } + } + /** * Update {@link Relationship#latestVersionStatus} of the relationships of both the old version and the new version * of the item. diff --git a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java index f2e528d78cd6..e17fd0072efa 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java @@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import java.sql.SQLException; import java.time.Instant; @@ -41,13 +42,19 @@ import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.RelationshipType; +import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; +import org.dspace.content.service.WorkspaceItemService; import org.dspace.orcid.consumer.OrcidQueueConsumer; import org.dspace.orcid.factory.OrcidServiceFactory; import org.dspace.orcid.service.OrcidQueueService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.utils.DSpace; +import org.dspace.versioning.Version; +import org.dspace.versioning.service.VersioningService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -64,8 +71,15 @@ public class OrcidQueueConsumerIT extends AbstractIntegrationTestWithDatabase { private ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + private WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); + + private InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService(); + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private VersioningService versioningService = new DSpace().getServiceManager() + .getServicesByType(VersioningService.class).get(0); + private Collection profileCollection; @Before @@ -763,6 +777,177 @@ public void testWithManyInsertionAndDeletionOfSameMetadataValue() throws Excepti } + @Test + public void testOrcidQueueRecordCreationForPublicationWithNotFoundAuthority() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("First User") + .withAuthor("Test User") + .build(); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = createRelationshipTypeBuilder(context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + } + + @Test + public void testOrcidQueueWithItemVersioning() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("Test User") + .build(); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = createRelationshipTypeBuilder(context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + + context.turnOffAuthorisationSystem(); + Version newVersion = versioningService.createNewVersion(context, publication); + context.restoreAuthSystemState(); + Item newPublication = newVersion.getItem(); + assertThat(newPublication.isArchived(), is(false)); + + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + + WorkspaceItem workspaceItem = workspaceItemService.findByItem(context, newVersion.getItem()); + context.turnOffAuthorisationSystem(); + + installItemService.installItem(context, workspaceItem); + + context.restoreAuthSystemState(); + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, newPublication, "Publication", INSERT)); + } + + @Test + public void testOrcidQueueUpdateWithItemVersioning() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .build(); + + OrcidHistory orcidHistory = OrcidHistoryBuilder.createOrcidHistory(context, profile, publication) + .withDescription("Test publication") + .withOperation(OrcidOperation.INSERT) + .withPutCode("12345") + .withStatus(201) + .build(); + + addMetadata(publication, "dc", "contributor", "author", "Test User", null); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = + createRelationshipTypeBuilder( + context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null + ).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", "12345", UPDATE)); + + Version newVersion = versioningService.createNewVersion(context, publication); + Item newPublication = newVersion.getItem(); + assertThat(newPublication.isArchived(), is(false)); + + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", "12345", UPDATE)); + + WorkspaceItem workspaceItem = workspaceItemService.findByItem(context, newVersion.getItem()); + installItemService.installItem(context, workspaceItem); + + context.commit(); + + context.restoreAuthSystemState(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, newPublication, "Publication", "12345", UPDATE)); + + orcidHistory = context.reloadEntity(orcidHistory); + assertThat(orcidHistory.getEntity(), is(newPublication)); + + } + private void addMetadata(Item item, String schema, String element, String qualifier, String value, String authority) throws Exception { context.turnOffAuthorisationSystem(); From 24318e01b84ec7ef3c2b7081bc5a85ab10295209 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 14:12:53 +0200 Subject: [PATCH 249/479] fix invalid usage of == operator (cherry picked from commit fa0fb14a185ba8a9c593a3653df302f0446a397c) --- .../src/main/java/org/dspace/content/ItemServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ac6c0d43d710..b2cc3e939d87 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -1788,7 +1788,7 @@ protected void moveSingleMetadataValue(Context context, Item dso, int place, Met //Retrieve the applicable relationship Relationship rs = relationshipService.find(context, ((RelationshipMetadataValue) rr).getRelationshipId()); - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().equals(dso)) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); From b8f638b3b6eff9c0138dcfe883a9083599a2ffa7 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:44:35 +0200 Subject: [PATCH 250/479] use equals instead of == (cherry picked from commit 80de8f6fb567ceb4497596fa714f2b357f1b8b26) --- .../src/main/java/org/dspace/content/EntityServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java index 2f34129f2e69..6049b7e6aae8 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java @@ -64,7 +64,7 @@ public List getLeftRelations(Context context, Entity entity) { List fullList = entity.getRelationships(); List listToReturn = new LinkedList<>(); for (Relationship relationship : fullList) { - if (relationship.getLeftItem().getID() == entity.getItem().getID()) { + if (relationship.getLeftItem().getID().equals(entity.getItem().getID())) { listToReturn.add(relationship); } } @@ -76,7 +76,7 @@ public List getRightRelations(Context context, Entity entity) { List fullList = entity.getRelationships(); List listToReturn = new LinkedList<>(); for (Relationship relationship : fullList) { - if (relationship.getRightItem().getID() == entity.getItem().getID()) { + if (relationship.getRightItem().getID().equals(entity.getItem().getID())) { listToReturn.add(relationship); } } From dc09d06ed6eedc89249fad5230f5346a830bbe41 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:47:07 +0200 Subject: [PATCH 251/479] use equals instead of == (cherry picked from commit 5e3552ee3885049df34c3fcaf49bfe3028c5dbd0) --- .../java/org/dspace/authorize/ResourcePolicyServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java index 7b93b912378e..86998a2196e7 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java @@ -417,7 +417,7 @@ public boolean isMyResourcePolicy(Context context, EPerson eperson, Integer id) ResourcePolicy resourcePolicy = resourcePolicyDAO.findOneById(context, id); Group group = resourcePolicy.getGroup(); - if (resourcePolicy.getEPerson() != null && resourcePolicy.getEPerson().getID() == eperson.getID()) { + if (resourcePolicy.getEPerson() != null && resourcePolicy.getEPerson().getID().equals(eperson.getID())) { isMy = true; } else if (group != null && groupService.isMember(context, eperson, group)) { isMy = true; From 6a5236f2f93a05999bfa391887ecb22f8bfae257 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:48:28 +0200 Subject: [PATCH 252/479] use equals instead of == (cherry picked from commit d2ef7b01ef1a5d769d764b708be393dbb481fb65) --- .../src/main/java/org/dspace/eperson/GroupServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 730053e42ce2..3fb20e2f1e6f 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -147,7 +147,7 @@ public void addMember(Context context, Group group, EPerson e) { public void addMember(Context context, Group groupParent, Group groupChild) throws SQLException { // don't add if it's already a member // and don't add itself - if (groupParent.contains(groupChild) || groupParent.getID() == groupChild.getID()) { + if (groupParent.contains(groupChild) || groupParent.getID().equals(groupChild.getID())) { return; } @@ -178,7 +178,7 @@ public void removeMember(Context context, Group group, EPerson ePerson) throws S Role role = stepByName.getRole(); for (CollectionRole collectionRole : collectionRoles) { if (StringUtils.equals(collectionRole.getRoleId(), role.getId()) - && claimedTask.getWorkflowItem().getCollection() == collectionRole.getCollection()) { + && claimedTask.getWorkflowItem().getCollection().equals(collectionRole.getCollection())) { // Count number of EPersons who are *direct* members of this group int totalDirectEPersons = ePersonService.countByGroups(context, Set.of(group)); // Count number of Groups which have this groupParent as a direct parent From 36ff5ca6cbc6a8641555f9267e06884f9112badc Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:49:14 +0200 Subject: [PATCH 253/479] use equals instead of == (cherry picked from commit a13cc82d405c5aefe00c7bb86d89c7dc8073a39b) --- .../submit/factory/impl/ItemMetadataValueAddPatchOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java index e749c4e79328..a7cd5bfc1de3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java @@ -213,7 +213,7 @@ private Integer getRelId(String authority) { private void updateRelationshipPlace(Context context, Item dso, int place, Relationship rs) { try { - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().equals(dso)) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); From f1dc25195f025919aa18cc390ad81459c3698169 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 18 Jun 2024 13:52:38 +0200 Subject: [PATCH 254/479] 115693: data-cite xsl targetting dc.identifier.uri fixes doi registration error (cherry picked from commit c5d08f037cb56111b8ef047279f323c06b11a864) --- .../main/java/org/dspace/identifier/DOIIdentifierProvider.java | 1 + dspace/config/crosswalks/DIM2DataCite.xsl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index b70eda960d35..8d71ac92f9d1 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -70,6 +70,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { static final char SLASH = '/'; // Metadata field name elements + // Warning: If this metadata field is changed for whatever reason, DIM2DataCite.xsl's template needs to reflect this // TODO: move these to MetadataSchema or some such? public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index a97d127694ef..d57996e6d8cf 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -333,7 +333,8 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - + + From d240a16b051326b0357cd8476928f899207ddc4c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 27 Jun 2024 10:59:36 +0200 Subject: [PATCH 255/479] 115693: DataCiteConnector fallback for blank metadata doi (cherry picked from commit 021e42434731e505245b3aa149eaf59a5fbccb94) --- .../main/java/org/dspace/identifier/doi/DataCiteConnector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 57136d6143bb..fc4e2652ac2f 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -15,6 +15,7 @@ import java.util.Iterator; import java.util.Map; +import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; @@ -412,7 +413,7 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) } String metadataDOI = extractDOI(root); - if (null == metadataDOI) { + if (StringUtils.isBlank(metadataDOI)) { // The DOI will be saved as metadata of dso after successful // registration. To register a doi it has to be part of the metadata // sent to DataCite. So we add it to the XML we'll send to DataCite From c1be5f8e4ed9577e22ce7b33bca3c171b84083cb Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 19 Jul 2024 13:42:09 +0200 Subject: [PATCH 256/479] 115693: Pass doi metadatafield with xsl parameters (cherry picked from commit 9e11e1f9ae69f19e506618d1d0b1fec0059ba165) --- .../identifier/DOIIdentifierProvider.java | 1 - .../identifier/doi/DataCiteConnector.java | 8 ++++++ dspace/config/crosswalks/DIM2DataCite.xsl | 25 +++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index 8d71ac92f9d1..b70eda960d35 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -70,7 +70,6 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { static final char SLASH = '/'; // Metadata field name elements - // Warning: If this metadata field is changed for whatever reason, DIM2DataCite.xsl's template needs to reflect this // TODO: move these to MetadataSchema or some such? public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index fc4e2652ac2f..a15e3f7fdbfe 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -7,6 +7,10 @@ */ package org.dspace.identifier.doi; +import static org.dspace.identifier.DOIIdentifierProvider.DOI_ELEMENT; +import static org.dspace.identifier.DOIIdentifierProvider.DOI_QUALIFIER; +import static org.dspace.identifier.DOIIdentifierProvider.MD_SCHEMA; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -386,6 +390,10 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) parameters.put("hostinginstitution", configurationService.getProperty(CFG_HOSTINGINSTITUTION)); } + parameters.put("mdSchema", MD_SCHEMA); + parameters.put("mdElement", DOI_ELEMENT); + // Pass an empty string for qualifier if the metadata field doesn't have any + parameters.put("mdQualifier", DOI_QUALIFIER); Element root = null; try { diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index d57996e6d8cf..d4d8cbe6417a 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -36,6 +36,10 @@ + + dc + identifier + uri @@ -333,16 +337,17 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - - - - - - - - - - + + + + + + + + + + + From f61c45bdc6533f7328e84af9c59e908bc1b3e6e2 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 8 Jul 2024 19:26:11 +0200 Subject: [PATCH 257/479] change order of name parts: familyName, givenName (cherry picked from commit 076f1f233ea0eef1a37ed4087f34008d1e92e40a) --- .../external/crossref/CrossRefAuthorMetadataProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java index abf84f52d058..b9b384f8ed77 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java @@ -42,8 +42,8 @@ public Collection processMetadata(String json) { JsonNode author = authors.next(); String givenName = author.at("/given").textValue(); String familyName = author.at("/family").textValue(); - if (StringUtils.isNoneBlank(givenName) && StringUtils.isNoneBlank(familyName)) { - values.add(givenName + " " + familyName); + if (StringUtils.isNotBlank(givenName) && StringUtils.isNotBlank(familyName)) { + values.add(familyName.trim() + ", " + givenName.trim()); } } return values; @@ -64,4 +64,4 @@ public void setPathToArray(String pathToArray) { this.pathToArray = pathToArray; } -} \ No newline at end of file +} From 7e87d57672640e72c432f432fd07ad8a6f6aedc8 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 10:53:23 +0200 Subject: [PATCH 258/479] fix broken unit tests (cherry picked from commit 1712b9f07875c67141b67da96fa93f0deaff4090) --- .../app/rest/CrossRefImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index 31c22692f008..f61a81140ddc 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -162,7 +162,7 @@ private ArrayList getRecords() { MetadatumDTO title = createMetadatumDTO("dc", "title", null, "State of Awareness of Freshers’ Groups Chortkiv State" + " Medical College of Prevention of Iodine Deficiency Diseases"); - MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "L.V. Senyuk"); + MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "Senyuk, L.V."); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "journal-article"); MetadatumDTO date = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof", @@ -191,7 +191,7 @@ private ArrayList getRecords() { List metadatums2 = new ArrayList(); MetadatumDTO title2 = createMetadatumDTO("dc", "title", null, "Ischemic Heart Disease and Role of Nurse of Cardiology Department"); - MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "K. І. Kozak"); + MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Kozak, K. І."); MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "journal-article"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof2 = createMetadatumDTO("dc", "relation", "ispartof", From ffc99d06a1fc301109df664c3b126137410e1e95 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Mon, 12 Aug 2024 15:08:26 -0300 Subject: [PATCH 259/479] Fix index-discovery process when using handle (cherry picked from commit 077aed38dc55172ed70d8bfb9c44fd369d966a6d) --- dspace-api/src/main/java/org/dspace/discovery/IndexClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java index 661c48d91cfc..867359a949c6 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java @@ -21,6 +21,7 @@ import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; +import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.discovery.indexobject.IndexableCollection; import org.dspace.discovery.indexobject.IndexableCommunity; @@ -92,7 +93,7 @@ public void internalRun() throws Exception { .getHandleService().resolveToObject(context, param); if (dso != null) { final IndexFactory indexableObjectService = IndexObjectFactoryFactory.getInstance(). - getIndexFactoryByType(String.valueOf(dso.getType())); + getIndexFactoryByType(Constants.typeText[dso.getType()]); indexableObject = indexableObjectService.findIndexableObject(context, dso.getID().toString()); } } From 0d74bd9f185f2036ca68853576545f09e32d23fb Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 23 Apr 2024 13:33:01 -0400 Subject: [PATCH 260/479] Separate task-list building from execution. The old code would curate the object once for each task, meaning that all but one task would be executed N times up to the length of the list. --- .../curate/XmlWorkflowCuratorServiceImpl.java | 28 +++++++++---------- .../java/org/dspace/curate/package-info.java | 12 ++++++++ 2 files changed, 26 insertions(+), 14 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 27a162d543c2..ec32ff92f9a2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java @@ -140,14 +140,14 @@ public boolean curate(Curator curator, Context c, XmlWorkflowItem wfi) item.setOwningCollection(wfi.getCollection()); for (Task task : step.tasks) { curator.addTask(task.name); + } - // Check whether the task is configured to be queued rather than automatically run - if (StringUtils.isNotEmpty(step.queue)) { - // queue attribute has been set in the FlowStep configuration: add task to configured queue - curator.queue(c, item.getID().toString(), step.queue); - } else { - // Task is configured to be run automatically - curator.curate(c, item); + if (StringUtils.isNotEmpty(step.queue)) { // Step's tasks are to be queued. + curator.queue(c, item.getID().toString(), step.queue); + } else { // Step's tasks are to be run now. + curator.curate(c, item); + + for (Task task : step.tasks) { int status = curator.getStatus(task.name); String result = curator.getResult(task.name); String action = "none"; @@ -184,14 +184,14 @@ public boolean curate(Curator curator, Context c, XmlWorkflowItem wfi) } } curator.clear(); - } - // Record any reporting done by the tasks. - if (reporter.length() > 0) { - LOG.info("Curation tasks over item {} for step {} report:%n{}", - () -> wfi.getItem().getID(), - () -> step.step, - () -> reporter.toString()); + // Record any reporting done by the tasks. + if (reporter.length() > 0) { + LOG.info("Curation tasks over item {} for step {} report:\n{}", + () -> wfi.getItem().getID(), + () -> step.step, + () -> reporter.toString()); + } } } return true; diff --git a/dspace-api/src/main/java/org/dspace/curate/package-info.java b/dspace-api/src/main/java/org/dspace/curate/package-info.java index 492642f60c57..1168bbd283d2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/package-info.java +++ b/dspace-api/src/main/java/org/dspace/curate/package-info.java @@ -20,6 +20,8 @@ * * *

    Curation requests may be run immediately or queued for batch processing. + * See {@link TaskQueue} and its relatives, {@link Curation} and its relatives + * for more on queued curation. * *

    Tasks may also be attached to a workflow step, so that a set of tasks is * applied to each uninstalled Item which passes through that step. See @@ -27,5 +29,15 @@ * *

    A task may return to the Curator a status code, a final status message, * and an optional report character stream. + * + *

    The {@link Reporter} classes absorb strings of text and preserve it in + * various ways. A Reporter is a simple {@link Appendable} and makes no + * assumptions about e.g. whether a string represents a complete line. If you + * want your report formatted, insert appropriate newlines and other whitespace + * as needed. Your tasks can emit marked-up text if you wish, but the stock + * Reporter implementations make no attempt to render it. + * + *

    Tasks may be annotated to inform the Curator of special properties. See + * {@link Distributive}, {@link Mutative}, {@link Suspendable} etc. */ package org.dspace.curate; From 41312eec6e4e7d86574428f03425c54117ea267c Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 26 Aug 2024 15:02:31 -0400 Subject: [PATCH 261/479] Make statistics autocommit much more frequent. (cherry picked from commit 5c9af9764e2485b26398ec06446cb8f0edd9cab2) --- dspace/solr/statistics/conf/solrconfig.xml | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/dspace/solr/statistics/conf/solrconfig.xml b/dspace/solr/statistics/conf/solrconfig.xml index 2b1cff45373d..c3f023ff2eee 100644 --- a/dspace/solr/statistics/conf/solrconfig.xml +++ b/dspace/solr/statistics/conf/solrconfig.xml @@ -32,14 +32,16 @@ - + 32 1000 ${solr.lock.type:native} - + false @@ -48,7 +50,7 @@ 10000 - ${solr.autoCommit.maxTime:900000} + ${solr.autoCommit.maxTime:10000} true @@ -62,14 +64,16 @@ ${solr.max.booleanClauses:1024} + unordered sets of *all* documents that match a + query. Caches results of 'fq' search param. --> - + 1000 - + - + uuid @@ -126,7 +132,8 @@ - + uid From e15f60abd3120b81b4b982cc9cb5344e3e2b2951 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 3 Sep 2024 17:13:01 +0200 Subject: [PATCH 262/479] Translate underscores to dashes in xml:lang attr for DIM2DataCite.xsl Modified the DataCite crosswalk to ensure that the xml:lang attribute translates any underscores in the value of @lang to dashes. This change aligns the attribute formatting with standard language code conventions. (cherry picked from commit a898afd5acbd6dc51a88b837cab2a1c56cc33967) --- dspace/config/crosswalks/DIM2DataCite.xsl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index d4d8cbe6417a..935b3dc4038a 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -25,6 +25,10 @@ to register DOIs anymore. Please follow and reuse the examples included in this file. For more information on the DataCite Schema, see https://schema.datacite.org. --> + 10.5072/dspace- @@ -362,20 +366,20 @@ - + - + AlternativeTitle - - + Subtitle - + TranslatedTitle @@ -394,7 +398,7 @@ --> - + @@ -626,7 +630,7 @@ --> - + Abstract From 404039ade6796f9abfb5dbf420ab83f87057a033 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:25:18 +0200 Subject: [PATCH 263/479] #9806: Add new create methods to group builder Now supports admin groups, default read, workflow role (cherry picked from commit cdb167e55aac9916618dcfee7222105f4456dbd8) --- .../java/org/dspace/builder/GroupBuilder.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java index b3447dd8bd9a..c16fb696b0c3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java @@ -12,6 +12,9 @@ import java.util.UUID; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -51,6 +54,33 @@ public static GroupBuilder createGroup(final Context context) { return builder.create(context); } + public static GroupBuilder createCollectionAdminGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, collection); + } + + public static GroupBuilder createCollectionSubmitterGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createSubmitterGroup(context, collection); + } + + public static GroupBuilder createCollectionDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } + + public static GroupBuilder createCollectionWorkflowRoleGroup(final Context context, Collection collection, + String roleName) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createWorkflowRoleGroup(context, collection, roleName); + } + + public static GroupBuilder createCommunityAdminGroup(final Context context, Community community) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, community); + } + private GroupBuilder create(final Context context) { this.context = context; try { @@ -61,6 +91,54 @@ private GroupBuilder create(final Context context) { return this; } + private GroupBuilder createAdminGroup(final Context context, DSpaceObject container) { + this.context = context; + try { + if (container instanceof Collection) { + group = collectionService.createAdministrators(context, (Collection) container); + } else if (container instanceof Community) { + group = communityService.createAdministrators(context, (Community) container); + } else { + handleException(new IllegalArgumentException("DSpaceObject must be collection or community. " + + "Type: " + container.getType())); + } + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createSubmitterGroup(final Context context, Collection collection) { + this.context = context; + try { + group = collectionService.createSubmitters(context, collection); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + this.context = context; + try { + group = collectionService.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createWorkflowRoleGroup(final Context context, Collection collection, String roleName) { + this.context = context; + try { + group = workflowService.createWorkflowRoleGroup(context, collection, roleName); + } catch (Exception e) { + return handleException(e); + } + return this; + } + @Override protected DSpaceObjectService getService() { return groupService; From 2ff2297b1fdd90b6f7c595c34b1167df3f91c036 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:28:27 +0200 Subject: [PATCH 264/479] #9806: Use builders for coll, comm, group creation in BitstreamRestRepositoryIT (cherry picked from commit b13abac7539944358711a381548fbaedd9dcbd92) --- .../app/rest/BitstreamRestRepositoryIT.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 bd79dc2f2b89..ecfe933fd921 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 @@ -49,6 +49,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; import org.dspace.content.Bitstream; @@ -2767,10 +2768,12 @@ public void deleteBitstreamsInBulk_collectionAdmin() throws Exception { .withEmail("col2admin@test.com") .withPassword(password) .build(); - Group col1_AdminGroup = collectionService.createAdministrators(context, col1); - Group col2_AdminGroup = collectionService.createAdministrators(context, col2); - groupService.addMember(context, col1_AdminGroup, col1Admin); - groupService.addMember(context, col2_AdminGroup, col2Admin); + Group col1_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col1) + .addMember(col1Admin) + .build(); + Group col2_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col2) + .addMember(col2Admin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); @@ -2871,8 +2874,9 @@ public void deleteBitstreamsInBulk_communityAdmin() throws Exception { .withEmail("parentComAdmin@test.com") .withPassword(password) .build(); - Group parentComAdminGroup = communityService.createAdministrators(context, parentCommunity); - groupService.addMember(context, parentComAdminGroup, parentCommunityAdmin); + Group parentComAdminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity) + .addMember(parentCommunityAdmin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); From b9b135163f5f261065aa272b3af41b92ac17cea7 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:29:48 +0200 Subject: [PATCH 265/479] #9806: Use builders for coll, comm, group creation in GroupRestRepositoryIT (cherry picked from commit 9205773802a8d22820b074e18409e245c63f5609) --- .../app/rest/GroupRestRepositoryIT.java | 202 ++++++++++-------- 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 2999b89d934f..92e53d922d25 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -85,9 +85,6 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest { ResourcePolicyService resourcePolicyService; @Autowired private ConfigurationService configurationService; - @Autowired - private CollectionService collectionService; - @Autowired private AuthorizeService authorizeService; @@ -773,12 +770,13 @@ public void addChildGroupCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); - groupService.addMember(context, parentGroup, eperson); groupService.update(context, parentGroup); context.commit(); @@ -810,6 +808,7 @@ public void addChildGroupCommunityAdminTest() throws Exception { ); } finally { + // TODO: Can we remove these lines now that we are creating them with the builder? if (community != null) { CommunityBuilder.deleteCommunity(community.getID()); } @@ -837,9 +836,9 @@ public void addChildGroupForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -882,9 +881,9 @@ public void addChildGroupUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -926,9 +925,9 @@ public void addChildGroupNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -971,18 +970,18 @@ public void addChildGroupUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - groupService.addMember(context, childGroup1, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + childGroup2 = GroupBuilder.createGroup(context).build(); groupService.update(context, childGroup1); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); +// context.commit(); +// +// parentGroup = context.reloadEntity(parentGroup); +// childGroup1 = context.reloadEntity(childGroup1); +// childGroup2 = context.reloadEntity(childGroup2); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); @@ -995,13 +994,15 @@ public void addChildGroupUnprocessableTest() throws Exception { ) ).andExpect(status().isUnprocessableEntity()); + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? getClient(authToken).perform( post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() ) - ).andExpect(status().isUnprocessableEntity()); + ).andExpect(status().isNoContent()); } finally { if (parentGroup != null) { @@ -1093,13 +1094,12 @@ public void addMemberCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1158,9 +1158,9 @@ public void addMemberForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1204,9 +1204,9 @@ public void addMemberUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1249,9 +1249,9 @@ public void addMemberNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1295,9 +1295,9 @@ public void addMemberUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1389,13 +1389,13 @@ public void removeChildGroupCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup = groupService.create(context); - - groupService.addMember(context, parentGroup, childGroup); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1439,8 +1439,8 @@ public void removeChildGroupForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context).build(); context.commit(); @@ -1474,8 +1474,8 @@ public void removeChildGroupUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context).build(); context.commit(); @@ -1508,10 +1508,10 @@ public void removeChildGroupNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1546,10 +1546,10 @@ public void removeChildGroupUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1625,13 +1625,12 @@ public void removeMemberCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member = ePersonService.create(context); - - groupService.addMember(context, parentGroup, member); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(member) + .addMember(eperson) + .build(); assertTrue(groupService.isMember(context, member, parentGroup)); @@ -1678,9 +1677,10 @@ public void removeMemberForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1715,9 +1715,10 @@ public void removeMemberUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1751,9 +1752,10 @@ public void removeMemberNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1789,9 +1791,10 @@ public void removeMemberUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -2586,7 +2589,8 @@ public void commAdminAndColAdminCanManageItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group itemReadGroup = collectionService.createDefaultReadGroup(context, col1, itemGroupString, defaultItemRead); + Group itemReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, + col1, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); @@ -2670,8 +2674,9 @@ public void commAdminAndColAdminCanManageBitstreamReadGroupTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group bitstreamReadGroup = collectionService.createDefaultReadGroup(context, col1, bitstreamGroupString, - defaultBitstreamRead); + Group bitstreamReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, col1, bitstreamGroupString, + defaultBitstreamRead) + .build(); context.restoreAuthSystemState(); @@ -2792,7 +2797,8 @@ public void commAdminAndColAdminCanManageWorkflowGroupsTest() throws Exception { public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2827,7 +2833,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2853,7 +2860,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws E public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2889,7 +2897,8 @@ public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thr public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2923,7 +2932,8 @@ public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2961,7 +2971,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFal public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2990,7 +3001,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse public void collectionAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); From 690dfa0809dc0c1829898c5f2a4d8bdbc2de0095 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:30:52 +0200 Subject: [PATCH 266/479] #9806: Tidy imports for GroupRestRepositoryIT (cherry picked from commit 80328eaca5f2ad36e006d32ea06aa3b89e2ada92) --- .../src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 92e53d922d25..2d191b2fe7c7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -60,7 +60,6 @@ import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; From 67fa262b83cf89d3645959bd466b1efb5d8b3736 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:31:31 +0200 Subject: [PATCH 267/479] #9806: Use builders for group, comm, coll creation in PackagerIT (cherry picked from commit 1f475aa731bbe42ad523f1056e6b56796ea8a2ee) --- .../src/test/java/org/dspace/app/packager/PackagerIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java index 2cddbb511f91..aeda97f818c2 100644 --- a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java @@ -24,6 +24,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; @@ -159,7 +160,7 @@ public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception { performExportScript(article.getHandle(), tempFile); UUID id = article.getID(); itemService.delete(context, article); - WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false, false); + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, col1, id).build(); installItemService.installItem(context, workspaceItem, "123456789/0100"); performImportNoForceScript(tempFile); Iterator items = itemService.findByCollection(context, col1); From 8d576f83002d9a2f7df39717d2af61f744b9d78c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:32:08 +0200 Subject: [PATCH 268/479] #9806: Use builders for group, comm, coll creation in StructBuilderIT (cherry picked from commit 2ef69045d15ae49a4d447227e8080860eecc61cd) --- .../dspace/administer/StructBuilderIT.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java index 63340698ac00..ead338bc8e70 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -23,11 +23,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.AbstractIntegrationTest; +import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; @@ -38,7 +39,6 @@ import org.junit.Test; import org.w3c.dom.Attr; import org.w3c.dom.Node; -import org.xml.sax.SAXException; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.diff.Comparison; import org.xmlunit.diff.ComparisonFormatter; @@ -52,7 +52,7 @@ * @author Mark H. Wood */ public class StructBuilderIT - extends AbstractIntegrationTest { + extends AbstractIntegrationTestWithDatabase { private static final Logger log = LogManager.getLogger(); private static final CommunityService communityService @@ -79,7 +79,8 @@ public static void tearDownClass() { * @throws IOException passed through. */ @Before - public void setUp() throws SQLException, AuthorizeException, IOException { + public void setUp() throws Exception { + super.setUp(); // Clear out all communities and collections. context.turnOffAuthorisationSystem(); for (Community community : communityService.findAllTop(context)) { @@ -285,19 +286,15 @@ public void testImportStructureWithHandles() * @throws org.dspace.authorize.AuthorizeException passed through. */ @Test - public void testExportStructure() - throws ParserConfigurationException, SAXException, IOException, - SQLException, AuthorizeException { + public void testExportStructure() { // Create some structure to test. context.turnOffAuthorisationSystem(); - Community community0 = communityService.create(null, context); - communityService.setMetadataSingleValue(context, community0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Top Community 0"); - Collection collection0_0 = collectionService.create(context, community0); - collectionService.setMetadataSingleValue(context, collection0_0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Collection 0.0"); + // Top level community + Community community0 = CommunityBuilder.createCommunity(context) + .withName("Top Community 0").build(); + // Collection below top level community + Collection collection0_0 = CollectionBuilder.createCollection(context, community0) + .withName("Collection 0.0").build(); // Export the current structure. System.out.println("exportStructure"); From ffc807205f8faecf380c99c7cc43479a5d474803 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:33:30 +0200 Subject: [PATCH 269/479] #9806: Refactor WorkspaceItemBuilder to support specific item uuid (cherry picked from commit b99b1eec29c5a0b6e92998b072282b03cfedc080) --- .../dspace/builder/WorkspaceItemBuilder.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java index 9d786d4761f0..06bdb82bd76e 100644 --- a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.util.UUID; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; @@ -41,14 +42,31 @@ protected WorkspaceItemBuilder(Context context) { public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col) { WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); - return builder.create(context, col); + return builder.create(context, col, null); } - private WorkspaceItemBuilder create(final Context context, final Collection col) { + public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col, UUID uuid) { + WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); + return builder.create(context, col, uuid); + } + + /** + * Create with a specific UUID (e.g. restoring items with Packager import) + * + * @param context DSpace context + * @param col Parent collection + * @param uuid Item UUID + * @return WorkspaceItemBuilder + */ + private WorkspaceItemBuilder create(final Context context, final Collection col, UUID uuid) { this.context = context; try { - workspaceItem = workspaceItemService.create(context, col, false); + if (uuid == null) { + workspaceItem = workspaceItemService.create(context, col, false); + } else { + workspaceItem = workspaceItemService.create(context, col, uuid, false, false); + } item = workspaceItem.getItem(); } catch (Exception e) { return handleException(e); From b9278cbf33bf5fa9efd607bd1976fda33b03ce46 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:34:05 +0200 Subject: [PATCH 270/479] #9806: Use builders for comm, coll, group creation in SupervisionOrderServiceIT (cherry picked from commit 6e9181e3f7cd502b345af7d5879fa25654176c25) --- .../SupervisionOrderServiceIT.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java index 60407823485b..aa4cd8bd4e49 100644 --- a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java @@ -18,6 +18,7 @@ import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; import org.dspace.builder.GroupBuilder; +import org.dspace.builder.SupervisionOrderBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; @@ -85,10 +86,10 @@ public void createSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA).build(); SupervisionOrder supervisionOrderTwo = - supervisionOrderService.create(context, item, groupB); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupB).build(); context.restoreAuthSystemState(); @@ -136,7 +137,8 @@ public void findSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -205,9 +207,12 @@ public void findAllSupervisionOrdersTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -259,9 +264,12 @@ public void findSupervisionOrderByItemTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -310,7 +318,8 @@ public void findSupervisionOrderByItemAndGroupTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA) + .build(); context.restoreAuthSystemState(); @@ -370,7 +379,8 @@ public void isSupervisorTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); From 968559bbafcafc1c6e67f03778e35094df648a21 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:35:14 +0200 Subject: [PATCH 271/479] #9806: Use builders for comm, coll, group creation in CollectionGroupRestControllerIT (cherry picked from commit f4629d8351b70ca945ffeebdf928cebfb868c349) --- .../rest/CollectionGroupRestControllerIT.java | 151 +++++++++--------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java index f6ab10c087ad..8d490109220d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java @@ -28,9 +28,9 @@ import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; -import org.dspace.content.service.CollectionService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; @@ -41,10 +41,6 @@ public class CollectionGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CollectionService collectionService; - @Autowired private GroupService groupService; @@ -68,7 +64,7 @@ public void setup() { @Test public void getCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -81,7 +77,7 @@ public void getCollectionAdminGroupTest() throws Exception { @Test public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -95,7 +91,7 @@ public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { @Test public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -109,7 +105,7 @@ public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -119,7 +115,7 @@ public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -413,7 +409,7 @@ public void postCollectionAdminGroupCreateAdminGroupUnProcessablePermanent() thr @Test public void deleteCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -428,7 +424,7 @@ public void deleteCollectionAdminGroupTest() throws Exception { @Test public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -443,7 +439,7 @@ public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exceptio @Test public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -458,7 +454,7 @@ public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -474,7 +470,7 @@ public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -493,7 +489,7 @@ public void deleteCollectionAdminGroupForbiddenTest() throws Exception { @Test public void deleteCollectionAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -512,7 +508,7 @@ public void deleteCollectionAdminGroupNotFoundTest() throws Exception { @Test public void getCollectionSubmittersGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -525,7 +521,7 @@ public void getCollectionSubmittersGroupTest() throws Exception { @Test public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -539,7 +535,7 @@ public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Except @Test public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -553,7 +549,7 @@ public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -563,7 +559,7 @@ public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -860,7 +856,7 @@ public void postCollectionSubmittersGroupCreateSubmittersGroupUnProcessablePerma @Test public void deleteCollectionSubmitterGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -875,7 +871,7 @@ public void deleteCollectionSubmitterGroupTest() throws Exception { @Test public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -890,7 +886,7 @@ public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exc @Test public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -905,7 +901,7 @@ public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exceptio @Test public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -924,7 +920,7 @@ public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -945,7 +941,7 @@ public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { @Test public void deleteCollectionSubmittersGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -961,7 +957,8 @@ public void getCollectionItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -977,7 +974,8 @@ public void getCollectionDefaultItemReadGroupTestParentCommunityAdmin() throws E String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -995,7 +993,8 @@ public void getCollectionDefaultItemReadGroupTestCollectionAdmin() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1012,7 +1011,8 @@ public void getCollectionDefaultItemReadGroupUnAuthorizedTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1025,7 +1025,8 @@ public void getCollectionDefaultItemReadGroupForbiddenTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1321,7 +1322,7 @@ public void deleteCollectionDefaultItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1345,7 +1346,7 @@ public void deleteCollectionDefaultItemReadGroupTestParentCommunityAdmin() throw String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1367,7 +1368,7 @@ public void deleteCollectionDefaultItemReadGroupTestCollectionAdmin() throws Exc String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1389,7 +1390,8 @@ public void deleteCollectionDefaultItemReadGroupUnAuthorizedTest() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1408,7 +1410,8 @@ public void deleteCollectionDefaultItemReadGroupForbiddenTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1430,7 +1433,7 @@ public void deleteCollectionDefaultItemReadGroupNotFoundTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1445,8 +1448,8 @@ public void getCollectionBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1462,8 +1465,8 @@ public void getCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() thr String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1480,8 +1483,8 @@ public void getCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1498,8 +1501,8 @@ public void getCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1512,8 +1515,8 @@ public void getCollectionDefaultBitstreamReadGroupForbiddenTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1811,8 +1814,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1835,8 +1838,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1859,8 +1862,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throw String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1882,8 +1885,8 @@ public void deleteCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1902,8 +1905,8 @@ public void deleteCollectionDefaultBitstreamReadGroupForbiddenTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1918,8 +1921,8 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1931,7 +1934,7 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep public void getWorkflowGroupForCollectionAndRole() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1944,7 +1947,7 @@ public void getWorkflowGroupForCollectionAndRole() throws Exception { @Test public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1958,7 +1961,7 @@ public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Ex @Test public void getWorkflowGroupForCollectionAndRoleWrongUUIDCollectionNotFound() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1979,7 +1982,7 @@ public void getWorkflowGroupForCollectionAndRoleWrongRoleNotFound() throws Excep public void getWorkflowGroupCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1994,7 +1997,7 @@ public void getWorkflowGroupCommunityAdmin() throws Exception { @Test public void getWorkflowGroupCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2009,7 +2012,7 @@ public void getWorkflowGroupCollectionAdmin() throws Exception { @Test public void getWorkflowGroupUnAuthorized() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2019,7 +2022,7 @@ public void getWorkflowGroupUnAuthorized() throws Exception { @Test public void getWorkflowGroupForbidden() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2324,7 +2327,7 @@ public void postCollectionWorkflowGroupCreateWorkflowGroupUnProcessablePermanent @Test public void deleteCollectionWorkflowGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -2339,7 +2342,7 @@ public void deleteCollectionWorkflowGroupTest() throws Exception { @Test public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2355,7 +2358,7 @@ public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Excep @Test public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2371,7 +2374,7 @@ public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception @Test public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2387,7 +2390,7 @@ public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2406,7 +2409,7 @@ public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { @Test public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2418,7 +2421,7 @@ public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { @Test public void deleteCollectionWorkflowGroupWithPooledTaskTest() throws Exception { context.turnOffAuthorisationSystem(); - Group reviewer = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group reviewer = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); // Submit an Item into the workflow -> moves to the "reviewer" step's pool. // The role must have at least one EPerson, otherwise the WSI gets archived immediately From e6a0bb8943a1022f22e3fe2bc2aa6be51ed95a44 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:35:43 +0200 Subject: [PATCH 272/479] #9806: Builders for comm, coll, group in CommunityAdminGroupRestControllerIT (cherry picked from commit 2d9988f77c52537f46888f27e35dbc0360299835) --- .../CommunityAdminGroupRestControllerIT.java | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java index 37548553b143..074a7e6a3557 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java @@ -34,8 +34,6 @@ import org.dspace.builder.GroupBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -49,19 +47,12 @@ public class CommunityAdminGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CommunityService communityService; - @Autowired private GroupService groupService; @Autowired private AuthorizeService authorizeService; - @Autowired - private CollectionService collectionService; - @Autowired private ConfigurationService configurationService; @@ -78,7 +69,7 @@ public void setup() { @Test public void getCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -91,7 +82,8 @@ public void getCommunityAdminGroupTest() throws Exception { @Test public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); + // TODO: this should actually be "add member", not directly setting a policy, right? authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -106,7 +98,7 @@ public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -116,7 +108,7 @@ public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -379,7 +371,7 @@ public void postCommunityAdminGroupCreateAdminGroupUnProcessablePermanent() thro @Test public void deleteCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -397,7 +389,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) .withName("Sub Community") .build(); - Group adminGroup = communityService.createAdministrators(context, child1); + GroupBuilder.createCommunityAdminGroup(context, child1).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -412,7 +404,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -429,7 +421,7 @@ public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -449,7 +441,7 @@ public void deleteCommunityAdminGroupForbiddenTest() throws Exception { @Test public void deleteCommunityAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -462,7 +454,7 @@ public void deleteCommunityAdminGroupNotFoundTest() throws Exception { public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -489,7 +481,7 @@ public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() th public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -526,7 +518,7 @@ public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -554,7 +546,7 @@ public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -591,7 +583,9 @@ public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFa public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -617,7 +611,9 @@ public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Ex public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -653,7 +649,7 @@ public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thro public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -681,7 +677,7 @@ public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() t public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -719,7 +715,7 @@ public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFals public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -748,7 +744,7 @@ public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse( public void communityAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); From 5fd99d8b14f022c72508b34fe19957d1dd4896a4 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:58:59 +0200 Subject: [PATCH 273/479] #9806: Update object cleanup in GroupRestRepositoryIT (cherry picked from commit 8cfb433c40ad01f348461a2c656665d1891de398) --- .../app/rest/GroupRestRepositoryIT.java | 952 +++++------------- 1 file changed, 239 insertions(+), 713 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 2d191b2fe7c7..a711c6c8be16 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -59,14 +59,11 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.GroupService; import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; @@ -757,263 +754,136 @@ public void addChildGroupTest() throws Exception { @Test public void addChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) - .addMember(eperson) - .build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - groupService.update(context, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(parentGroup, childGroup1) - ); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - assertTrue( - groupService.isMember(parentGroup, childGroup2) - ); + assertTrue( + groupService.isMember(parentGroup, childGroup1) + ); - } finally { - // TODO: Can we remove these lines now that we are creating them with the builder? - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + assertTrue( + groupService.isMember(parentGroup, childGroup2) + ); } @Test public void addChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); + context.turnOffAuthorisationSystem(); - context.commit(); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.commit(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnauthorized()); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - groupService.update(context, childGroup1); - -// context.commit(); -// -// parentGroup = context.reloadEntity(parentGroup); -// childGroup1 = context.reloadEntity(childGroup1); -// childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - // TODO - confirm with reviewers that this is a mistake - it actually should be No Content - // (see AddMember test) but was incorrectly expecting 422? - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); } @Test @@ -1081,251 +951,118 @@ public void addMemberTest() throws Exception { @Test public void addMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) - .addMember(eperson) - .build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(context, member1, parentGroup) - ); + parentGroup = context.reloadEntity(parentGroup); + member1 = context.reloadEntity(member1); + member2 = context.reloadEntity(member2); - assertTrue( - groupService.isMember(context, member2, parentGroup) - ); + assertTrue( + groupService.isMember(context, member1, parentGroup) + ); - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + assertTrue( + groupService.isMember(context, member2, parentGroup) + ); } @Test public void addMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1378,29 +1115,18 @@ public void removeChildGroupTest() throws Exception { @Test public void removeChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - Group parentGroup = null; - Group childGroup = null; - - try { context.turnOffAuthorisationSystem(); - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) .addMember(eperson) .build(); - childGroup = GroupBuilder.createGroup(context) + Group childGroup = GroupBuilder.createGroup(context) .withParent(parentGroup) .build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1413,163 +1139,71 @@ public void removeChildGroupCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(parentGroup, childGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } } @Test public void removeChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1613,31 +1247,19 @@ public void removeMemberTest() throws Exception { @Test public void removeMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member = null; - try { context.turnOffAuthorisationSystem(); - community = CommunityBuilder.createCommunity(context).build(); - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + Community community = CommunityBuilder.createCommunity(context).build(); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) .addMember(member) .addMember(eperson) .build(); assertTrue(groupService.isMember(context, member, parentGroup)); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1650,171 +1272,75 @@ public void removeMemberCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(context, member, parentGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test public void removeMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isForbidden()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isUnauthorized()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { context.turnOffAuthorisationSystem(); - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) .addMember(member) .build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform( delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + UUID.randomUUID()) ).andExpect(status().isUnprocessableEntity()); - - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test From 282d4db36bebb3e159a09f222e2aa13160a99468 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 16:43:36 +0200 Subject: [PATCH 274/479] #9806: Align provider reg in CreateMissingIdentifiersIT with other tests VersionedHandlerIdentifierProviderIT uses this registerProvider method which looks more reliable and doesn't do a refresh/reload of applicationContext after (which I suspected might have an odd interaction with VersioningWithRelationshipsIT and its createBean() calls?) (cherry picked from commit 90536e443b7fedef0481a34326cafc58fb334396) --- .../general/CreateMissingIdentifiersIT.java | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 2a07799deee5..8038a7153325 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,6 +10,8 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; @@ -19,7 +21,10 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; +import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.After; @@ -32,10 +37,23 @@ */ public class CreateMissingIdentifiersIT extends AbstractIntegrationTestWithDatabase { + private ServiceManager serviceManager; + private IdentifierServiceImpl identifierService; private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + + serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + // Clean out providers to avoid any being used for creation of community and collection + identifierService.setProviders(new ArrayList<>()); + } + @Test public void testPerform() throws IOException { @@ -67,11 +85,7 @@ public void testPerform() /* * Now install an incompatible provider to make the task fail. */ - DSpaceServicesFactory.getInstance() - .getServiceManager() - .registerServiceClass( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - VersionedHandleIdentifierProviderWithCanonicalHandles.class); + registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); curator.curate(context, item); System.out.format("With incompatible provider, result is '%s'.\n", @@ -86,4 +100,14 @@ public void destroy() throws Exception { super.destroy(); DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); } + + private void registerProvider(Class type) { + // Register our new provider + serviceManager.registerServiceClass(type.getName(), type); + IdentifierProvider identifierProvider = + (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService.setProviders(List.of(identifierProvider)); + } } From 48c8848fc21ed739c99045b5e3e2b690297ba3da Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 16:45:05 +0200 Subject: [PATCH 275/479] #9806: Use builders for creation in VersioningWithRelationshipsIT I am a bit uncertain about the createBean() calls here, why do we not simply *get* the configured beans using the service manager instead, but will look at that in a separate change (cherry picked from commit 3521ab6d3598e716cc9fe8e938e883b302006580) --- .../VersioningWithRelationshipsIT.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 44653300e0de..accc52d0d830 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -49,6 +49,7 @@ import org.dspace.builder.ItemBuilder; import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipTypeBuilder; +import org.dspace.builder.VersionBuilder; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; @@ -62,8 +63,6 @@ import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; -import org.dspace.versioning.factory.VersionServiceFactory; -import org.dspace.versioning.service.VersioningService; import org.hamcrest.Matcher; import org.junit.Assert; import org.junit.Before; @@ -74,8 +73,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa private final RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); - private final VersioningService versioningService = - VersionServiceFactory.getInstance().getVersionService(); private final WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); private final InstallItemService installItemService = @@ -291,7 +288,7 @@ public void test_createNewVersionOfItemOnLeftSideOfRelationships() throws Except // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -567,7 +564,7 @@ public void test_createNewVersionOfItemAndModifyRelationships() throws Exception // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -927,7 +924,7 @@ public void test_createNewVersionOfItemOnRightSideOfRelationships() throws Excep // create a new version of the person // //////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPerson); + Version newVersion = VersionBuilder.createVersion(context, originalPerson, "test").build(); Item newPerson = newVersion.getItem(); assertNotSame(originalPerson, newPerson); @@ -1300,7 +1297,7 @@ public void test_createNewVersionOfItemAndVerifyMetadataOrder() throws Exception // create new version of publication // /////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1463,7 +1460,7 @@ public void test_createNewVersionOfItemWithAddRemoveMove() throws Exception { // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1782,7 +1779,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -1790,7 +1787,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -2316,7 +2313,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - person 3.2 // ///////////////////////////////////// - Item pe3_2 = versioningService.createNewVersion(context, pe3_1).getItem(); + Item pe3_2 = VersionBuilder.createVersion(context, pe3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pe3_2)); context.commit(); @@ -2324,7 +2321,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - project 3.2 // ////////////////////////////////////// - Item pr3_2 = versioningService.createNewVersion(context, pr3_1).getItem(); + Item pr3_2 = VersionBuilder.createVersion(context, pr3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pr3_2)); context.commit(); @@ -3056,7 +3053,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -3064,7 +3061,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -3509,7 +3506,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create a new version of publication 1 and archive // /////////////////////////////////////////////////////// - Item publication1V2 = versioningService.createNewVersion(context, publication1V1).getItem(); + Item publication1V2 = VersionBuilder.createVersion(context, publication1V1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, publication1V2)); context.dispatchEvents(); @@ -3517,7 +3514,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 1 // //////////////////////////////////// - Item person1V2 = versioningService.createNewVersion(context, person1V1).getItem(); + Item person1V2 = VersionBuilder.createVersion(context, person1V1, "test").build().getItem(); // update "Smith, Donald" to "Smith, D." itemService.replaceMetadata( context, person1V2, "person", "givenName", null, null, "D.", @@ -3853,7 +3850,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 2 // //////////////////////////////////// - Item person2V2 = versioningService.createNewVersion(context, person2V1).getItem(); + Item person2V2 = VersionBuilder.createVersion(context, person2V1, "test").build().getItem(); Relationship rel1 = getRelationship(publication1V2, isAuthorOfPublication, person2V2); assertNotNull(rel1); rel1.setRightwardValue("Doe, Jane Jr"); From 89796ece6b4e7ad3401c02ecab54e1f33926263c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 17:15:15 +0200 Subject: [PATCH 276/479] #9806: Set explicit id provider before VersioningWithRelationshipsIT (cherry picked from commit 4af690065038d9cb401d0fa1fc5fbfe0fa1fe2c7) --- .../VersioningWithRelationshipsIT.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index accc52d0d830..10cb30cd52b9 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -60,6 +60,9 @@ import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; import org.dspace.discovery.SolrSearchCore; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; +import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -81,7 +84,7 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - + private IdentifierServiceImpl identifierService; protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -98,6 +101,22 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa protected RelationshipType isIssueOfJournalVolume; protected RelationshipType isProjectOfPerson; + private void registerProvider(Class type) { + // Register our new provider + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); + } + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); + identifierService.setProviders(List.of(identifierProvider)); + } + @Override @Before public void setUp() throws Exception { @@ -105,6 +124,9 @@ public void setUp() throws Exception { context.turnOffAuthorisationSystem(); + + registerProvider(VersionedHandleIdentifierProvider.class); + community = CommunityBuilder.createCommunity(context) .withName("community") .build(); From 123aedde04b0e05419d18767a59f6008126589a9 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 17:58:23 +0200 Subject: [PATCH 277/479] #9806: Move cleanup of handle provider to destroy in VersionedHandleIdentifierProviderIT (cherry picked from commit f6cabe648dfcda786afdf7d5411dc3d7ad85c405) --- .../VersioningWithRelationshipsIT.java | 23 ------------------- .../VersionedHandleIdentifierProviderIT.java | 22 ++++++++++++++++-- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 10cb30cd52b9..3acc4ca146ee 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -60,9 +60,6 @@ import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; import org.dspace.discovery.SolrSearchCore; -import org.dspace.identifier.IdentifierProvider; -import org.dspace.identifier.IdentifierServiceImpl; -import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -84,7 +81,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - private IdentifierServiceImpl identifierService; protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -101,22 +97,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa protected RelationshipType isIssueOfJournalVolume; protected RelationshipType isProjectOfPerson; - private void registerProvider(Class type) { - // Register our new provider - IdentifierProvider identifierProvider = - (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); - if (identifierProvider == null) { - DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); - identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); - } - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService = DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(IdentifierServiceImpl.class).get(0); - identifierService.setProviders(new ArrayList<>()); - identifierService.setProviders(List.of(identifierProvider)); - } - @Override @Before public void setUp() throws Exception { @@ -124,9 +104,6 @@ public void setUp() throws Exception { context.turnOffAuthorisationSystem(); - - registerProvider(VersionedHandleIdentifierProvider.class); - community = CommunityBuilder.createCommunity(context) .withName("community") .build(); diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 7e549f6cae33..57acf1f1c453 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -24,6 +24,7 @@ import org.dspace.content.Item; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -57,13 +58,30 @@ public void setUp() throws Exception { .build(); } + @After + @Override + public void destroy() throws Exception { + super.destroy(); + // After this test has finished running, refresh application context and + // set the expected 'default' versioned handle provider back to ensure other tests don't fail + DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); + } + private void registerProvider(Class type) { // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); identifierService.setProviders(List.of(identifierProvider)); } From 724f821bee86479663671f0eda94341c0e34ec85 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 25 Jun 2024 13:45:31 +0200 Subject: [PATCH 278/479] Fix request a copy link token generation Ensure DSpace URLs with extra segments are included fully in the generated link --- .../rest/repository/RequestItemRepository.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 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 5945d516600a..a666b673530f 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,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.validator.routines.EmailValidator; import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; @@ -287,19 +288,17 @@ public Class getDomainClass() { * Generate a link back to DSpace, to act on a request. * * @param token identifies the request. - * @return URL to the item request API, with the token as request parameter - * "token". + * @return URL to the item request API, with /request-a-copy/{token} as the last URL segments * @throws URISyntaxException passed through. * @throws MalformedURLException passed through. */ private String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); - - URI link = new URIBuilder(base) - .setPathSegments("request-a-copy", token) - .build(); - - return link.toURL().toExternalForm(); + URIBuilder uriBuilder = new URIBuilder(base); + // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) + URI uri = uriBuilder.setPath(StringUtils.stripEnd(uriBuilder.getPath(), "") + + "/request-a-copy/" + token).build(); + return uri.toURL().toExternalForm(); } } From cdb255fecaf2fb06944137d359cc188a36e5ee66 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 25 Jun 2024 14:36:17 +0200 Subject: [PATCH 279/479] Make RequestItemRepository#getLinkTokenEmail public, write test --- .../repository/RequestItemRepository.java | 2 +- .../app/rest/RequestItemRepositoryIT.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 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 a666b673530f..3a3444615344 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 @@ -292,7 +292,7 @@ public Class getDomainClass() { * @throws URISyntaxException passed through. * @throws MalformedURLException passed through. */ - private String getLinkTokenEmail(String token) + public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); URIBuilder uriBuilder = new URIBuilder(base); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index 2fb7dbbc969d..065660f2c879 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -29,6 +29,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.sql.SQLException; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -56,10 +58,12 @@ import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; /** * @@ -82,6 +86,12 @@ public class RequestItemRepositoryIT @Autowired(required = true) RequestItemService requestItemService; + @Autowired + ApplicationContext applicationContext; + + @Autowired + private ConfigurationService configurationService; + private Collection collection; private Item item; @@ -610,4 +620,23 @@ public void testGetDomainClass() { Class instanceClass = instance.getDomainClass(); assertEquals("Wrong domain class", RequestItemRest.class, instanceClass); } + + /** + * Test that generated links include the correct base URL, even if it has extra URL segments + */ + @Test + public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxException { + RequestItemRepository instance = applicationContext.getBean( + RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRepository.class); + String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); + String newDspaceUrl = currentDspaceUrl + "/subdir"; + // Add a /subdir to the url for this test + configurationService.setProperty("dspace.ui.url", newDspaceUrl); + String expectedUrl = newDspaceUrl + "/request-a-copy/token"; + String generatedLink = instance.getLinkTokenEmail("token"); + // The URLs should match + assertEquals(expectedUrl, generatedLink); + configurationService.reloadConfig(); + } } From 64255ffb05788898ee37baef7d42e1e89f98764a Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 10 Jul 2024 16:04:34 +0200 Subject: [PATCH 280/479] #9668: Ensure proper handling of non-subpath URLs in link tokens --- .../repository/RequestItemRepository.java | 5 +++-- .../app/rest/RequestItemRepositoryIT.java | 20 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 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 3a3444615344..71c46b4dc843 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 @@ -297,8 +297,9 @@ public String getLinkTokenEmail(String token) final String base = configurationService.getProperty("dspace.ui.url"); URIBuilder uriBuilder = new URIBuilder(base); // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) - URI uri = uriBuilder.setPath(StringUtils.stripEnd(uriBuilder.getPath(), "") - + "/request-a-copy/" + token).build(); + URI uri = uriBuilder.setPath( + (uriBuilder.getPath() == null ? "" : StringUtils.stripEnd(uriBuilder.getPath(), "")) + + "/request-a-copy/" + token).build(); return uri.toURL().toExternalForm(); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index 065660f2c879..f8732d0826e3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -622,10 +622,10 @@ public void testGetDomainClass() { } /** - * Test that generated links include the correct base URL, even if it has extra URL segments + * Test that generated links include the correct base URL, where the UI URL has a subpath like /subdir */ @Test - public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxException { + public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, RequestItemRepository.class); @@ -639,4 +639,20 @@ public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxExcep assertEquals(expectedUrl, generatedLink); configurationService.reloadConfig(); } + + /** + * Test that generated links include the correct base URL, with NO subpath elements + */ + @Test + public void testGetLinkTokenEmailWithoutSubPath() throws MalformedURLException, URISyntaxException { + RequestItemRepository instance = applicationContext.getBean( + RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRepository.class); + String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); + String expectedUrl = currentDspaceUrl + "/request-a-copy/token"; + String generatedLink = instance.getLinkTokenEmail("token"); + // The URLs should match + assertEquals(expectedUrl, generatedLink); + configurationService.reloadConfig(); + } } From 1914aac18b244c3b4053a56905c78d359b5e1910 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 1 Sep 2024 12:53:04 +0200 Subject: [PATCH 281/479] Improved URI build method as per review --- .../rest/repository/RequestItemRepository.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 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 71c46b4dc843..8be7971c84dd 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 @@ -15,7 +15,11 @@ import java.net.URI; import java.net.URISyntaxException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; @@ -295,11 +299,16 @@ public Class getDomainClass() { public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); + URIBuilder uriBuilder = new URIBuilder(base); - // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) - URI uri = uriBuilder.setPath( - (uriBuilder.getPath() == null ? "" : StringUtils.stripEnd(uriBuilder.getPath(), "")) - + "/request-a-copy/" + token).build(); + List segments = new LinkedList<>(); + if (StringUtils.isNotBlank(uriBuilder.getPath())) { + segments.add(StringUtils.strip(uriBuilder.getPath(), "/")); + } + segments.add("request-a-copy"); + segments.add(token); + URI uri = uriBuilder.setPathSegments(segments).build(); + return uri.toURL().toExternalForm(); } } From ab43b8606615ce6a77cb88333469611dc8411fb6 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 4 Sep 2024 14:04:20 +0200 Subject: [PATCH 282/479] Tidy implementation of link token generation --- .../dspace/app/rest/repository/RequestItemRepository.java | 5 +++-- 1 file changed, 3 insertions(+), 2 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 8be7971c84dd..f7c986ff9a02 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 @@ -300,6 +300,7 @@ public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); + // Construct the link, making sure to support sub-paths URIBuilder uriBuilder = new URIBuilder(base); List segments = new LinkedList<>(); if (StringUtils.isNotBlank(uriBuilder.getPath())) { @@ -307,8 +308,8 @@ public String getLinkTokenEmail(String token) } segments.add("request-a-copy"); segments.add(token); - URI uri = uriBuilder.setPathSegments(segments).build(); - return uri.toURL().toExternalForm(); + // Build and return the URL from segments (or throw exception) + return uriBuilder.setPathSegments(segments).build().toURL().toExternalForm(); } } From 7ec746d0052a01f5c5ed9f0351c988fe13f6aafa Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 4 Sep 2024 15:19:34 +0200 Subject: [PATCH 283/479] lint fixes (RequestItemRepository) --- .../org/dspace/app/rest/repository/RequestItemRepository.java | 3 --- 1 file changed, 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 f7c986ff9a02..10208b27fe7a 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 @@ -12,11 +12,8 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; From 4ac4c911cf47ecf3540e05a9067550bba3faab25 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 6 Sep 2024 14:38:15 -0500 Subject: [PATCH 284/479] Singular name needed in 7.x --- .../java/org/dspace/app/rest/RequestItemRepositoryIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index f8732d0826e3..fbbd179fd287 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -627,7 +627,7 @@ public void testGetDomainClass() { @Test public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( - RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRest.CATEGORY + '.' + RequestItemRest.NAME, RequestItemRepository.class); String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); String newDspaceUrl = currentDspaceUrl + "/subdir"; @@ -646,7 +646,7 @@ public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URI @Test public void testGetLinkTokenEmailWithoutSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( - RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRest.CATEGORY + '.' + RequestItemRest.NAME, RequestItemRepository.class); String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); String expectedUrl = currentDspaceUrl + "/request-a-copy/token"; From 9031322e7d4eccc73f057a5b3cf11c3c8cfc5361 Mon Sep 17 00:00:00 2001 From: Mikhail Schastlivtsev Date: Wed, 15 May 2024 10:08:46 +0300 Subject: [PATCH 285/479] add missing wosPublisherContrib key-ref in wos-integration.xml (#9579) (cherry picked from commit 6a8c76bbe1e7149785c17c7a7249e319199c3e0a) --- dspace/config/spring/api/wos-integration.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace/config/spring/api/wos-integration.xml b/dspace/config/spring/api/wos-integration.xml index 17bd53c04a4d..ba90363a2e60 100644 --- a/dspace/config/spring/api/wos-integration.xml +++ b/dspace/config/spring/api/wos-integration.xml @@ -35,6 +35,7 @@ + From a49ee624508f71c3e928d032b8448d1ec1316d6d Mon Sep 17 00:00:00 2001 From: Mikhail Schastlivtsev Date: Mon, 17 Jun 2024 14:06:53 +0300 Subject: [PATCH 286/479] add missing publisher metadatum in test (#9579) (cherry picked from commit cde892c8c7e1df8fff70a4cbfc693f378c2065d2) --- .../org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java index f1d3f5303ec0..9f68d79c2036 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java @@ -136,6 +136,7 @@ private ArrayList getRecords() { MetadatumDTO subject15 = createMetadatumDTO("dc", "subject", null, "Coding concepts"); MetadatumDTO subject16 = createMetadatumDTO("dc", "subject", null, "Lesson design"); MetadatumDTO subject17 = createMetadatumDTO("dc", "subject", null, "Social Sciences"); + MetadatumDTO publisher = createMetadatumDTO("dc", "publisher", null, "SPRINGER"); MetadatumDTO other = createMetadatumDTO("dc", "identifier", "other", "WOS:000805105200003"); metadatums.add(edition); metadatums.add(date); @@ -166,6 +167,7 @@ private ArrayList getRecords() { metadatums.add(subject15); metadatums.add(subject16); metadatums.add(subject17); + metadatums.add(publisher); metadatums.add(other); ImportRecord firstrRecord = new ImportRecord(metadatums); @@ -205,6 +207,7 @@ private ArrayList getRecords() { MetadatumDTO subject26 = createMetadatumDTO("dc", "subject", null, "Social Sciences"); MetadatumDTO subject27 = createMetadatumDTO("dc", "subject", null, "Science & Technology"); MetadatumDTO subject28 = createMetadatumDTO("dc", "subject", null, "Life Sciences & Biomedicine"); + MetadatumDTO publisher2 = createMetadatumDTO("dc", "publisher", null, "NATURE PORTFOLIO"); MetadatumDTO other2 = createMetadatumDTO("dc", "identifier", "other", "WOS:000805100600001"); MetadatumDTO rid = createMetadatumDTO("person", "identifier", "rid", "C-6334-2011"); MetadatumDTO rid2 = createMetadatumDTO("person", "identifier", "rid", "B-1251-2008"); @@ -236,6 +239,7 @@ private ArrayList getRecords() { metadatums2.add(subject26); metadatums2.add(subject27); metadatums2.add(subject28); + metadatums2.add(publisher2); metadatums2.add(other2); metadatums2.add(rid); metadatums2.add(rid2); From 405397b8b061205253768db73924aeffa77c846d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:23:32 +0200 Subject: [PATCH 287/479] update eperson's attributes right after successful login (cherry picked from commit 428489ca5258ea7ac6942a722621e22c3371bfcf) --- .../authenticate/LDAPAuthentication.java | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 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 585eaf9cd8b1..2203937e75d6 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -68,12 +68,8 @@ * @author Ivan Masár * @author Michael Plate */ -public class LDAPAuthentication - implements AuthenticationMethod { +public class LDAPAuthentication implements AuthenticationMethod { - /** - * log4j category - */ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDAPAuthentication.class); @@ -130,7 +126,7 @@ public boolean allowSetPassword(Context context, return false; } - /* + /** * This is an explicit method. */ @Override @@ -138,7 +134,7 @@ public boolean isImplicit() { return false; } - /* + /** * Add authenticated users to the group defined in dspace.cfg by * the login.specialgroup key. */ @@ -177,7 +173,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request) return Collections.EMPTY_LIST; } - /* + /** * Authenticate the given credentials. * This is the heart of the authentication method: test the * credentials for authenticity, and if accepted, attempt to match @@ -250,7 +246,7 @@ public int authenticate(Context context, } // Check a DN was found - if ((dn == null) || (dn.trim().equals(""))) { + if (StringUtils.isBlank(dn)) { log.info(LogHelper .getHeader(context, "failed_login", "no DN found for user " + netid)); return BAD_CREDENTIALS; @@ -269,6 +265,18 @@ public int authenticate(Context context, context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); + // update eperson's attributes + context.turnOffAuthorisationSystem(); + setEpersonAttributes(context, eperson, ldap, Optional.empty()); + try { + ePersonService.update(context, eperson); + context.dispatchEvents(); + } catch (AuthorizeException e) { + log.warn("update of eperson " + eperson.getID() + " failed", e); + } finally { + context.restoreAuthSystemState(); + } + // assign user to groups based on ldap dn assignGroups(dn, ldap.ldapGroup, context); @@ -313,14 +321,13 @@ public int authenticate(Context context, log.info(LogHelper.getHeader(context, "type=ldap-login", "type=ldap_but_already_email")); context.turnOffAuthorisationSystem(); - eperson.setNetid(netid.toLowerCase()); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); ePersonService.update(context, eperson); context.dispatchEvents(); context.restoreAuthSystemState(); context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); - // assign user to groups based on ldap dn assignGroups(dn, ldap.ldapGroup, context); @@ -331,20 +338,7 @@ public int authenticate(Context context, try { context.turnOffAuthorisationSystem(); eperson = ePersonService.create(context); - if (StringUtils.isNotEmpty(email)) { - eperson.setEmail(email); - } - if (StringUtils.isNotEmpty(ldap.ldapGivenName)) { - eperson.setFirstName(context, ldap.ldapGivenName); - } - if (StringUtils.isNotEmpty(ldap.ldapSurname)) { - eperson.setLastName(context, ldap.ldapSurname); - } - if (StringUtils.isNotEmpty(ldap.ldapPhone)) { - ePersonService.setMetadataSingleValue(context, eperson, - MD_PHONE, ldap.ldapPhone, null); - } - eperson.setNetid(netid.toLowerCase()); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); eperson.setCanLogIn(true); authenticationService.initEPerson(context, request, eperson); ePersonService.update(context, eperson); @@ -382,6 +376,27 @@ public int authenticate(Context context, return BAD_ARGS; } + /** + * Update eperson's attributes + */ + private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) throws SQLException { + if (StringUtils.isNotEmpty(ldap.ldapEmail)) { + eperson.setEmail(ldap.ldapEmail); + } + if (StringUtils.isNotEmpty(ldap.ldapGivenName)) { + eperson.setFirstName(context, ldap.ldapGivenName); + } + if (StringUtils.isNotEmpty(ldap.ldapSurname)) { + eperson.setLastName(context, ldap.ldapSurname); + } + if (StringUtils.isNotEmpty(ldap.ldapPhone)) { + ePersonService.setMetadataSingleValue(context, eperson, MD_PHONE, ldap.ldapPhone, null); + } + if (netid.isPresent()) { + eperson.setNetid(netid.get().toLowerCase()); + } + } + /** * Internal class to manage LDAP query and results, mainly * because there are multiple values to return. @@ -671,7 +686,7 @@ protected boolean ldapAuthenticate(String netid, String password, } } - /* + /** * Returns the URL of an external login page which is not applicable for this authn method. * * Note: Prior to DSpace 7, this method return the page of login servlet. @@ -699,7 +714,7 @@ public String getName() { return "ldap"; } - /* + /** * Add authenticated users to the group defined in dspace.cfg by * the authentication-ldap.login.groupmap.* key. * From b0370a064b843db6d77efb58c5d54788e3872253 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:36:45 +0200 Subject: [PATCH 288/479] add missing import (cherry picked from commit c5ad32a9b3ece7e64043f9f39d22b594e913737f) --- .../main/java/org/dspace/authenticate/LDAPAuthentication.java | 1 + 1 file changed, 1 insertion(+) 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 2203937e75d6..06bd6ae603d9 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -17,6 +17,7 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Optional; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; From 31af49ea725b5658f408e519cc577805b8f8c72d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:54:53 +0200 Subject: [PATCH 289/479] fix Checkstyle violations (cherry picked from commit aaa74b88c99af8ece67d43fa412a39a7406fe10c) --- .../org/dspace/authenticate/LDAPAuthentication.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 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 06bd6ae603d9..93f0feb384c0 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -184,7 +184,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request) * @param context * DSpace context, will be modified (ePerson set) upon success. * - * @param username + * @param netid * Username (or email address) when method is explicit. Use null for * implicit method. * @@ -322,7 +322,7 @@ public int authenticate(Context context, log.info(LogHelper.getHeader(context, "type=ldap-login", "type=ldap_but_already_email")); context.turnOffAuthorisationSystem(); - setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); ePersonService.update(context, eperson); context.dispatchEvents(); context.restoreAuthSystemState(); @@ -380,7 +380,9 @@ public int authenticate(Context context, /** * Update eperson's attributes */ - private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) throws SQLException { + private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) + throws SQLException { + if (StringUtils.isNotEmpty(ldap.ldapEmail)) { eperson.setEmail(ldap.ldapEmail); } @@ -389,7 +391,7 @@ private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDA } if (StringUtils.isNotEmpty(ldap.ldapSurname)) { eperson.setLastName(context, ldap.ldapSurname); - } + } if (StringUtils.isNotEmpty(ldap.ldapPhone)) { ePersonService.setMetadataSingleValue(context, eperson, MD_PHONE, ldap.ldapPhone, null); } From 702ff9cf4c0e9e2afeb1bab820087e6fb7584154 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 27 Sep 2024 12:07:52 +0200 Subject: [PATCH 290/479] minor fix in parameter description (cherry picked from commit 5758d9e90302d4da33ec869b6ec656e5c25b865a) --- dspace/config/emails/subscriptions_content | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/emails/subscriptions_content b/dspace/config/emails/subscriptions_content index 9b8c91e559df..a1b107e8fc50 100644 --- a/dspace/config/emails/subscriptions_content +++ b/dspace/config/emails/subscriptions_content @@ -1,7 +1,7 @@ ## E-mail sent to designated address about updates on subscribed items ## -## Parameters: {0} Collections updates -## {1} Communities updates +## Parameters: {0} Communities updates +## {1} Collections updates #set($subject = "${config.get('dspace.name')} Subscriptions") This email is sent from ${config.get('dspace.name')} based on the chosen subscription preferences. From a721f8c41e92762d6d1af831b337b111a5ea9991 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 27 Aug 2024 10:33:01 +0200 Subject: [PATCH 291/479] fix failed first login attempt in HAL browser (cherry picked from commit 002e637d4fc69b31c2a2321230d96534e49ee33e) --- .../src/main/webapp/login.html | 128 ++++++++++-------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/dspace-server-webapp/src/main/webapp/login.html b/dspace-server-webapp/src/main/webapp/login.html index 50d95b80472f..0597a66bfe96 100644 --- a/dspace-server-webapp/src/main/webapp/login.html +++ b/dspace-server-webapp/src/main/webapp/login.html @@ -32,7 +32,7 @@ border-radius: 5px; box-shadow: 0 1px 2px rgba(0, 0, 0, .05); } - .form-signin .form-signin-heading, .form-signin .checkbox { + .form-signin .form-signin-heading, .form-signin { margin-bottom: 10px; } .form-signin input[type="text"], .form-signin input[type="password"] { @@ -94,63 +94,71 @@

    Other login methods:

    "onclick" : function() { toastr.remove(); } } + // retrieves a valid CSRF token (please note that this method works both in DS 7 and DS 8) + // HTTP response code 403 is expected at this point (the response contains the DSPACE-XSRF-TOKEN header) + $.ajax({ + url : window.location.href.replace("login.html", "") + 'api/authn/login', + type : 'POST', + error : function(xhr) { + // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) + checkForUpdatedCSRFTokenInResponse(xhr); + } + }); + // When the login page loads, we do *two* AJAX requests. - // (1) Call GET /api/authn/status. This call has two purposes. First, it checks to see if you are logged in, - // (if not, WWW-Authenticate will return login options). Second, it retrieves the CSRF token, if a - // new one has been assigned (as a valid CSRF token is required for the POST call). + // (1) Call GET /api/authn/status. This call checks to see if you are logged in + // (if not, WWW-Authenticate will return login options). // (2) If that /api/authn/status call finds authentication data, call POST /api/authn/login. - // This scenario occurs when you login via an external authentication system (e.g. Shibboleth)... + // This scenario occurs when you log in via an external authentication system (e.g. Shibboleth) // in which case the main role of /api/authn/login is to simply ensure the "Authorization" header // is sent back to the client (based on your authentication data). $.ajax({ - url : window.location.href.replace("login.html", "") + 'api/authn/status', - type : 'GET', - success : function(result, status, xhr) { - // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) - checkForUpdatedCSRFTokenInResponse(xhr); + url : window.location.href.replace("login.html", "") + 'api/authn/status', + type : 'GET', + success : function(result, status, xhr) { - // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and - // therefore we need to display available authentication options. - var authenticate = xhr.getResponseHeader("WWW-Authenticate"); - if (authenticate !== null) { - var element = $('div.other-login-methods'); - var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); - if (realms.length == 1){ - var loc = /location="([^,]*)"/.exec(authenticate); - if (loc !== null && loc.length === 2) { - document.location = loc[1]; - } - } else if (realms.length > 1){ - for (var i = 0; i < realms.length; i++){ - addLocationButton(realms[i], element); + // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and + // therefore we need to display available authentication options. + var authenticate = xhr.getResponseHeader("WWW-Authenticate"); + if (authenticate !== null && authenticate.contains('location=')) { + var element = $('div.other-login-methods'); + var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); + if (realms.length === 1){ + var loc = /location="([^,]*)"/.exec(authenticate); + if (loc !== null && loc.length === 2) { + document.location = loc[1]; + } + } else if (realms.length > 1){ + for (var i = 0; i < realms.length; i++){ + addLocationButton(realms[i], element); + } } + } else { + // If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT + // is sent back in the "Authorization" header. This simply completes an external authentication + // process (e.g. Shibboleth) + $.ajax({ + url : window.location.href.replace("login.html", "") + 'api/authn/login', + type : 'POST', + beforeSend: function (xhr) { + // If CSRF token found in cookie, send it back as X-XSRF-Token header + var csrfToken = getCSRFToken(); + if (csrfToken != null) { + xhr.setRequestHeader('X-XSRF-Token', csrfToken); + } + }, + success : successHandler, + error : function(xhr) { + // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) + checkForUpdatedCSRFTokenInResponse(xhr); + toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed'); + } + }); } - } else { - // If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT - // is sent back in the "Authorization" header. This simply completes an external authentication - // process (e.g. Shibboleth) - $.ajax({ - url : window.location.href.replace("login.html", "") + 'api/authn/login', - type : 'POST', - beforeSend: function (xhr, settings) { - // If CSRF token found in cookie, send it back as X-XSRF-Token header - var csrfToken = getCSRFToken(); - if (csrfToken != null) { - xhr.setRequestHeader('X-XSRF-Token', csrfToken); - } - }, - success : successHandler, - error : function(xhr, textStatus, errorThrown) { - // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) - checkForUpdatedCSRFTokenInResponse(xhr); - toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed'); - } - }); + }, + error : function() { + toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load'); } - }, - error : function(xhr, textStatus, errorThrown) { - toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load'); - } }); function addLocationButton(realm, element){ @@ -166,22 +174,22 @@

    Other login methods:

    return string.charAt(0).toUpperCase() + string.slice(1); } - /** - * Check current response headers to see if the CSRF Token has changed. If a new value is found in headers, - * save the new value into our "MyHalBrowserCsrfToken" cookie. - **/ + /** + * Check current response headers to see if the CSRF Token has changed. If a new value is found in headers, + * save the new value into our "MyHalBrowserCsrfToken" cookie. + **/ function checkForUpdatedCSRFTokenInResponse(jqxhr) { // look for DSpace-XSRF-TOKEN header & save to our MyHalBrowserCsrfToken cookie (if found) var updatedCsrfToken = jqxhr.getResponseHeader('DSPACE-XSRF-TOKEN'); if (updatedCsrfToken != null) { - document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken; + document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken; } } - /** - * Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie. - * This cookie is set in login.html after a successful login occurs. - **/ + /** + * Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie. + * This cookie is set in login.html after a successful login occurs. + **/ function getCSRFToken() { var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserCsrfToken' + '\\s*=\\s*([^;]+)'); if (cookie != null) { @@ -204,11 +212,11 @@

    Other login methods:

    user: $("#username").val(), password: $("#password").val() }, - beforeSend: function (xhr, settings) { + beforeSend: function (xhr) { // If CSRF token found in cookie, send it back as X-XSRF-Token header var csrfToken = getCSRFToken(); if (csrfToken != null) { - xhr.setRequestHeader('X-XSRF-Token', csrfToken); + xhr.setRequestHeader('X-XSRF-Token', csrfToken); } }, success : successHandler, From 4669d8f7605a980a3f038c6f10835f89b7d81bee Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Aug 2024 16:24:38 +0200 Subject: [PATCH 292/479] applied change suggested by reviewer: use String.prototype.includes (cherry picked from commit 546afb189e1df8bdfbc9948e790004baa81ec894) --- dspace-server-webapp/src/main/webapp/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/webapp/login.html b/dspace-server-webapp/src/main/webapp/login.html index 0597a66bfe96..959190020a13 100644 --- a/dspace-server-webapp/src/main/webapp/login.html +++ b/dspace-server-webapp/src/main/webapp/login.html @@ -120,7 +120,7 @@

    Other login methods:

    // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and // therefore we need to display available authentication options. var authenticate = xhr.getResponseHeader("WWW-Authenticate"); - if (authenticate !== null && authenticate.contains('location=')) { + if (authenticate !== null && authenticate.includes('location=')) { var element = $('div.other-login-methods'); var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); if (realms.length === 1){ From 1e47fa61d92c9c87aeabbb4d1c074cc360c721af Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 11 Oct 2024 14:38:47 -0500 Subject: [PATCH 293/479] Bump to Spring 5.3.39 and Spring Security 5.8.14 --- dspace-server-webapp/pom.xml | 21 +++++++++++++++++++++ pom.xml | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index e0ec7ef5ed76..97bdaa241a4f 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -362,6 +362,27 @@ org.springframework.boot spring-boot-starter-security ${spring-boot.version} + + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-web + + +
    + + org.springframework.security + spring-security-core + ${spring-security.version} + + + org.springframework.security + spring-security-web + ${spring-security.version} diff --git a/pom.xml b/pom.xml index b662e333225d..6433b6f227de 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 11 - 5.3.34 + 5.3.39 2.7.18 - 5.7.11 + 5.8.14 5.6.15.Final 6.2.5.Final 42.7.3 From 70dd8477599abf1e0b4c8933b45c80ddd49e7561 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 11 Oct 2024 16:41:40 -0500 Subject: [PATCH 294/479] Revert to Spring Security 5.7.12. Spring Security 5.8.x changes behavior of CSRF tokens --- dspace-server-webapp/pom.xml | 9 +++++++++ pom.xml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 97bdaa241a4f..5a4e1b32123d 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -364,6 +364,10 @@ ${spring-boot.version} + + org.springframework.security + spring-security-config + org.springframework.security spring-security-core @@ -374,6 +378,11 @@
    + + org.springframework.security + spring-security-config + ${spring-security.version} + org.springframework.security spring-security-core diff --git a/pom.xml b/pom.xml index 6433b6f227de..02579c03294a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 11 5.3.39 2.7.18 - 5.8.14 + 5.7.12 5.6.15.Final 6.2.5.Final 42.7.3 From 4a4a8bcb22796e5b22c9bbb1796e3458b48f1c07 Mon Sep 17 00:00:00 2001 From: Brian Keese Date: Tue, 15 Oct 2024 11:38:54 -0500 Subject: [PATCH 295/479] Fix full-text indexing for files over the character limit The error handler for files over the limit logged the correct message, but never actually added the full text to the index doc. --- .../indexobject/IndexFactoryImpl.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index f1ae137b9163..c9a865ec85b2 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -118,20 +118,10 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea ParseContext tikaContext = new ParseContext(); // Use Apache Tika to parse the full text stream(s) + boolean extractionSucceeded = false; try (InputStream fullTextStreams = streams.getStream()) { tikaParser.parse(fullTextStreams, tikaHandler, tikaMetadata, tikaContext); - - // Write Tika metadata to "tika_meta_*" fields. - // This metadata is not very useful right now, - // but we'll keep it just in case it becomes more useful. - for (String name : tikaMetadata.names()) { - for (String value : tikaMetadata.getValues(name)) { - doc.addField("tika_meta_" + name, value); - } - } - - // Save (parsed) full text to "fulltext" field - doc.addField("fulltext", tikaHandler.toString()); + extractionSucceeded = true; } catch (SAXException saxe) { // Check if this SAXException is just a notice that this file was longer than the character limit. // Unfortunately there is not a unique, public exception type to catch here. This error is thrown @@ -141,6 +131,7 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea // log that we only indexed up to that configured limit log.info("Full text is larger than the configured limit (discovery.solr.fulltext.charLimit)." + " Only the first {} characters were indexed.", charLimit); + extractionSucceeded = true; } else { log.error("Tika parsing error. Could not index full text.", saxe); throw new IOException("Tika parsing error. Could not index full text.", saxe); @@ -148,11 +139,19 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea } catch (TikaException | IOException ex) { log.error("Tika parsing error. Could not index full text.", ex); throw new IOException("Tika parsing error. Could not index full text.", ex); - } finally { - // Add document to index - solr.add(doc); } - return; + if (extractionSucceeded) { + // Write Tika metadata to "tika_meta_*" fields. + // This metadata is not very useful right now, + // but we'll keep it just in case it becomes more useful. + for (String name : tikaMetadata.names()) { + for (String value : tikaMetadata.getValues(name)) { + doc.addField("tika_meta_" + name, value); + } + } + // Save (parsed) full text to "fulltext" field + doc.addField("fulltext", tikaHandler.toString()); + } } // Add document to index solr.add(doc); From 31d36e7abf89f188fffba004520e4cbe2c4890a0 Mon Sep 17 00:00:00 2001 From: Miika Nurminen Date: Tue, 27 Aug 2024 08:46:15 +0300 Subject: [PATCH 296/479] Bugfix: BitstreamRestController etag/content-length calculation does not consider cover page Ported Alphonse Bendt's PR #9666 to DSpace 7 branch (squashed 5 commits). This PR fixes a bug where the etag/content-length calculation did not respect the potential existence of a coverpage. The controller now will use the post processed pdf if coverpages are enabled. --- .../app/rest/BitstreamRestController.java | 14 +- .../app/rest/utils/BitstreamResource.java | 119 ++++++++++------ .../app/rest/BitstreamRestControllerIT.java | 129 +++++++++++++++++- 3 files changed, 209 insertions(+), 53 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index 22b18724b90b..242b231654c6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -135,11 +135,16 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp long filesize = bit.getSizeBytes(); Boolean citationEnabledForBitstream = citationDocumentService.isCitationEnabledForBitstream(bit, context); + var bitstreamResource = + new org.dspace.app.rest.utils.BitstreamResource(name, uuid, + currentUser != null ? currentUser.getID() : null, + context.getSpecialGroupUuids(), citationEnabledForBitstream); + HttpHeadersInitializer httpHeadersInitializer = new HttpHeadersInitializer() .withBufferSize(BUFFER_SIZE) .withFileName(name) - .withChecksum(bit.getChecksum()) - .withLength(bit.getSizeBytes()) + .withChecksum(bitstreamResource.getChecksum()) + .withLength(bitstreamResource.contentLength()) .withMimetype(mimetype) .with(request) .with(response); @@ -157,11 +162,6 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp httpHeadersInitializer.withDisposition(HttpHeadersInitializer.CONTENT_DISPOSITION_ATTACHMENT); } - org.dspace.app.rest.utils.BitstreamResource bitstreamResource = - new org.dspace.app.rest.utils.BitstreamResource(name, uuid, - currentUser != null ? currentUser.getID() : null, - context.getSpecialGroupUuids(), citationEnabledForBitstream); - //We have all the data we need, close the connection to the database so that it doesn't stay open during //download/streaming context.complete(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java index 4e5545fabc7f..2f47dacc7ccc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java @@ -15,7 +15,8 @@ import java.util.UUID; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.factory.ContentServiceFactory; @@ -27,6 +28,7 @@ import org.dspace.eperson.service.EPersonService; import org.dspace.utils.DSpace; import org.springframework.core.io.AbstractResource; +import org.springframework.util.DigestUtils; /** * This class acts as a {@link AbstractResource} used by Spring's framework to send the data in a proper and @@ -36,18 +38,23 @@ */ public class BitstreamResource extends AbstractResource { - private String name; - private UUID uuid; - private UUID currentUserUUID; - private boolean shouldGenerateCoverPage; - private byte[] file; - private Set currentSpecialGroups; + private static final Logger LOG = LogManager.getLogger(BitstreamResource.class); - private BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - private CitationDocumentService citationDocumentService = + private final String name; + private final UUID uuid; + private final UUID currentUserUUID; + private final boolean shouldGenerateCoverPage; + private final Set currentSpecialGroups; + + private final BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + private final EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + private final CitationDocumentService citationDocumentService = new DSpace().getServiceManager() - .getServicesByType(CitationDocumentService.class).get(0); + .getServicesByType(CitationDocumentService.class).get(0); + + private String documentEtag; + private long documentLength; + private InputStream documentInputStream = null; public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, Set currentSpecialGroups, boolean shouldGenerateCoverPage) { @@ -68,16 +75,14 @@ public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, Set */ private byte[] getCoverpageByteArray(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException { - if (file == null) { - try { - Pair citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); - this.file = citedDocument.getLeft(); - } catch (Exception e) { - // Return the original bitstream without the cover page - this.file = IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); - } + try { + var citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); + return citedDocument.getLeft(); + } catch (Exception e) { + LOG.warn("Could not generate cover page. Will fallback to original document", e); + // Return the original bitstream without the cover page + return IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); } - return file; } @Override @@ -87,22 +92,9 @@ public String getDescription() { @Override public InputStream getInputStream() throws IOException { - try (Context context = initializeContext()) { - - Bitstream bitstream = bitstreamService.find(context, uuid); - InputStream out; - - if (shouldGenerateCoverPage) { - out = new ByteArrayInputStream(getCoverpageByteArray(context, bitstream)); - } else { - out = bitstreamService.retrieve(context, bitstream); - } + fetchDocument(); - this.file = null; - return out; - } catch (SQLException | AuthorizeException e) { - throw new IOException(e); - } + return this.documentInputStream; } @Override @@ -111,17 +103,63 @@ public String getFilename() { } @Override - public long contentLength() throws IOException { + public long contentLength() { + fetchDocument(); + + return this.documentLength; + } + + public String getChecksum() { + fetchDocument(); + + return this.documentEtag; + } + + private void fetchDocument() { + if (this.documentInputStream != null) { + return; + } + try (Context context = initializeContext()) { Bitstream bitstream = bitstreamService.find(context, uuid); if (shouldGenerateCoverPage) { - return getCoverpageByteArray(context, bitstream).length; + var coverPage = getCoverpageByteArray(context, bitstream); + + this.documentEtag = etag(bitstream); + this.documentLength = coverPage.length; + this.documentInputStream = new ByteArrayInputStream(coverPage); + } else { - return bitstream.getSizeBytes(); + + this.documentEtag = bitstream.getChecksum(); + this.documentLength = bitstream.getSizeBytes(); + this.documentInputStream = bitstreamService.retrieve(context, bitstream); + } - } catch (SQLException | AuthorizeException e) { - throw new IOException(e); + } catch (SQLException | AuthorizeException | IOException e) { + throw new RuntimeException(e); } + + LOG.debug("fetched document {} {} {}", shouldGenerateCoverPage, this.documentEtag, this.documentLength); + } + + private String etag(Bitstream bitstream) { + + /* Ideally we would calculate the md5 checksum based on the document with coverpage. + However it looks like the coverpage generation is not stable (e.g. if invoked twice it will return + different results). This means we cannot use it for etag calculation/comparison! + + Instead we will create the MD5 based off the original checksum plus fixed prefix. This ensures + that checksums will differ when coverpage is on/off. + However the checksum will _not_ change if the coverpage content changes. + */ + + var content = "coverpage:" + bitstream.getChecksum(); + + StringBuilder builder = new StringBuilder(37); + DigestUtils.appendMd5DigestAsHex(content.getBytes(), builder); + + return builder.toString(); } private Context initializeContext() throws SQLException { @@ -131,4 +169,5 @@ private Context initializeContext() throws SQLException { currentSpecialGroups.forEach(context::setSpecialGroup); return context; } + } 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 855fedbaa2ab..45f8565fbaf3 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 @@ -23,6 +23,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -49,8 +50,10 @@ import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; +import java.nio.file.Files; import java.util.UUID; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; @@ -90,6 +93,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; /** * Integration test to test the /api/core/bitstreams/[id]/* endpoints @@ -948,12 +952,11 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { //** THEN ** .andExpect(status().isOk()) - //The Content Length must match the full length + // exact content-length and etag values are verified in s separate test .andExpect(header().string("Content-Length", not(nullValue()))) + .andExpect(header().string("ETag", not(nullValue()))) //The server should indicate we support Range requests .andExpect(header().string("Accept-Ranges", "bytes")) - //The ETag has to be based on the checksum - .andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\"")) //We expect the content type to match the bitstream mime type .andExpect(content().contentType("application/pdf;charset=UTF-8")) //THe bytes of the content must match the original content @@ -962,20 +965,22 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { // The citation cover page contains the item title. // We will now verify that the pdf text contains this title. String pdfText = extractPDFText(content); - System.out.println(pdfText); assertTrue(StringUtils.contains(pdfText,"Public item citation cover page test 1")); // The dspace-api/src/test/data/dspaceFolder/assetstore/ConstitutionofIreland.pdf file contains 64 pages, // manually counted + 1 citation cover page assertEquals(65,getNumberOfPdfPages(content)); + var etagHeader = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("ETag"); + //A If-None-Match HEAD request on the ETag must tell is the bitstream is not modified getClient().perform(head("/api/core/bitstreams/" + bitstream.getID() + "/content") - .header("If-None-Match", bitstream.getChecksum())) + .header("If-None-Match", etagHeader)) .andExpect(status().isNotModified()); //The download and head request should also be logged as a statistics record - checkNumberOfStatsRecords(bitstream, 2); + checkNumberOfStatsRecords(bitstream, 3); } private String extractPDFText(byte[] content) throws IOException { @@ -1383,4 +1388,116 @@ private void verifyBitstreamDownload(Bitstream file, String contentType, boolean header.contains("attachment")); } } + + @Test + public void contentLengthAndEtagUsesOriginalBitstream() throws Exception { + givenPdf(false, originalPdf -> { + var originalMd5 = md5Checksum(originalPdf); + long originalLength = Files.size(originalPdf.toPath()); + + assertThat(originalLength, greaterThan(0L)); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(header().longValue("Content-Length", originalLength)) + .andExpect(header().string("ETag", "\"" + originalMd5 + "\"")); + }); + } + + private static String md5Checksum(File file) throws IOException { + final String md5; + try (InputStream is = new FileInputStream(file)) { + md5 = DigestUtils.md5Hex(is); + } + return md5; + } + + @Test + public void withCoverPageContentLengthAndEtagChanges() throws Exception { + givenPdf(true, originalPdf -> { + var originalMd5 = md5Checksum(originalPdf); + long originalLength = Files.size(originalPdf.toPath()); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(header().string("Content-Length", not(Long.toString(originalLength)))) + .andExpect(header().string("ETag", not("\"" + originalMd5 + "\""))); + }); + } + + @Test + public void etagAndContentLengthIsStable() { + givenPdf(false, ignored -> { + var etag1 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + var etag2 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + assertThat(etag1, equalTo(etag2)); + }); + } + + @Test + public void withCoverPageEtagAndContentLengthIsStable() { + givenPdf(true, ignored -> { + var etag1 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + var etag2 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + assertThat(etag1, equalTo(etag2)); + }); + } + + @FunctionalInterface + interface ThrowingConsumer { + void accept(T t) throws Exception; + } + + private void givenPdf(boolean coverPageEnabled, ThrowingConsumer block) { + configurationService.setProperty("citation-page.enable_globally", coverPageEnabled); + + try { + citationDocumentService.afterPropertiesSet(); + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community and one collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = + CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build(); + + //2. A public item with a bitstream + File originalPdf = new File(testProps.getProperty("test.bitstream")); + + try (InputStream is = new FileInputStream(originalPdf)) { + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item citation cover page test 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .build(); + + bitstream = BitstreamBuilder + .createBitstream(context, publicItem1, is) + .withName("Test bitstream") + .withDescription("This is a bitstream to test the citation cover page.") + .withMimeType("application/pdf") + .build(); + } + context.restoreAuthSystemState(); + + block.accept(originalPdf); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } From 6018f926d7a599b081664bf862531ededb645839 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 15 Oct 2024 23:23:24 +0200 Subject: [PATCH 297/479] Add Eclipse JDT .factorypath to .gitignore (cherry picked from commit 9ce645e08b50cd752e48a640e340b55466f019be) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2fcb46b9932c..529351edc5c2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ tags .project .classpath .checkstyle +.factorypath ## Ignore project files created by IntelliJ IDEA *.iml From 68266cd3c19d27dbed4f181bc12017d46fc6f64c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Nov 2024 13:36:17 -0600 Subject: [PATCH 298/479] Move logging of test methods to Abstract*Test classes in dspace-api. That way they work for **both** dspace-server-webapp and dspace-api tests. (cherry picked from commit bd20c9262b1992ff15033701fa01738329864b1b) --- .../dspace/AbstractDSpaceIntegrationTest.java | 24 ++++++++++ .../java/org/dspace/AbstractDSpaceTest.java | 23 ++++++++++ .../AbstractControllerIntegrationTest.java | 4 -- .../AbstractWebClientIntegrationTest.java | 4 -- .../test/LoggingTestExecutionListener.java | 44 ------------------- 5 files changed, 47 insertions(+), 52 deletions(-) delete mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java index 5a5ce8bf6d4c..791fdbc66abc 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java @@ -21,8 +21,12 @@ import org.dspace.discovery.SearchUtils; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TestName; /** * Abstract Test class copied from DSpace API @@ -46,6 +50,12 @@ public class AbstractDSpaceIntegrationTest { */ protected static DSpaceKernelImpl kernelImpl; + /** + * Obtain the TestName from JUnit, so that we can print it out in the test logs (see below) + */ + @Rule + public TestName testName = new TestName(); + /** * Default constructor */ @@ -90,6 +100,20 @@ public static void initTestEnvironment() { } } + @Before + public void printTestMethodBefore() { + // Log the test method being executed. Put lines around it to make it stand out. + log.info("---"); + log.info("Starting execution of test method: {}()", testName.getMethodName()); + log.info("---"); + } + + @After + public void printTestMethodAfter() { + // Log the test method just completed. + log.info("Finished execution of test method: {}()", testName.getMethodName()); + } + /** * This method will be run after all tests finish as per @AfterClass. It * will clean resources initialized by the @BeforeClass methods. diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java index 36477556d3de..136af83f076f 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java @@ -18,9 +18,13 @@ import org.apache.logging.log4j.Logger; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; +import org.junit.Rule; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -62,6 +66,12 @@ protected AbstractDSpaceTest() { } */ protected static DSpaceKernelImpl kernelImpl; + /** + * Obtain the TestName from JUnit, so that we can print it out in the test logs (see below) + */ + @Rule + public TestName testName = new TestName(); + /** * This method will be run before the first test as per @BeforeClass. It will * initialize shared resources required for all tests of this class. @@ -94,6 +104,19 @@ public static void initKernel() { } } + @Before + public void printTestMethodBefore() { + // Log the test method being executed. Put lines around it to make it stand out. + log.info("---"); + log.info("Starting execution of test method: {}()", testName.getMethodName()); + log.info("---"); + } + + @After + public void printTestMethodAfter() { + // Log the test method just completed. + log.info("Finished execution of test method: {}()", testName.getMethodName()); + } /** * This method will be run after all tests finish as per @AfterClass. It diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java index 00339ba2e482..28c5560edf31 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java @@ -39,7 +39,6 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -75,9 +74,6 @@ @WebAppConfiguration // Load our src/test/resources/application-test.properties to override some settings in default application.properties @TestPropertySource(locations = "classpath:application-test.properties") -// Enable our custom Logging listener to log when each test method starts/stops -@TestExecutionListeners(listeners = {LoggingTestExecutionListener.class}, - mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class AbstractControllerIntegrationTest extends AbstractIntegrationTestWithDatabase { protected static final String AUTHORIZATION_HEADER = "Authorization"; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index 75b0143e3e65..df6afcb16e32 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -22,7 +22,6 @@ import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -52,9 +51,6 @@ @ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class }) // Load our src/test/resources/application-test.properties to override some settings in default application.properties @TestPropertySource(locations = "classpath:application-test.properties") -// Enable our custom Logging listener to log when each test method starts/stops -@TestExecutionListeners(listeners = {LoggingTestExecutionListener.class}, - mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWithDatabase { // (Random) port chosen for test web server @LocalServerPort diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java deleted file mode 100644 index 2a04bab2041c..000000000000 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.dspace.app.rest.test; - -import org.apache.logging.log4j.Logger; -import org.springframework.test.context.TestContext; -import org.springframework.test.context.support.AbstractTestExecutionListener; - -/** - * Custom DSpace TestExecutionListener which logs messages whenever a specific Test Case (i.e. test method) has - * started or ended execution. This makes Test environment logs easier to read/understand as you know which method has - * caused errors, etc. - */ -public class LoggingTestExecutionListener extends AbstractTestExecutionListener { - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LoggingTestExecutionListener.class); - - /** - * Before each test method is run - * @param testContext - */ - @Override - public void beforeTestMethod(TestContext testContext) { - // Log the test method being executed. Put lines around it to make it stand out. - log.info("---"); - log.info("Starting execution of test method: {}()", testContext.getTestMethod().getName()); - log.info("---"); - } - - /** - * After each test method is run - * @param testContext - */ - @Override - public void afterTestMethod(TestContext testContext) { - // Log the test method just completed. - log.info("Finished execution of test method: {}()", testContext.getTestMethod().getName()); - } -} From 831db33018cdcfa132dc0b1da0f427aca0d03585 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 14 Oct 2024 17:03:15 +0200 Subject: [PATCH 299/479] 118774: status of doi should be set to TO_BE_DELETED when related item is removed permanently (cherry picked from commit 352f4c21523237cefa5bf67c99f7d7b26a51d72b) --- .../src/main/java/org/dspace/content/ItemServiceImpl.java | 2 ++ 1 file changed, 2 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 b2cc3e939d87..ad4156328aeb 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -66,6 +66,7 @@ import org.dspace.harvest.HarvestedItem; import org.dspace.harvest.service.HarvestedItemService; import org.dspace.identifier.DOI; +import org.dspace.identifier.DOIIdentifierProvider; import org.dspace.identifier.IdentifierException; import org.dspace.identifier.service.DOIService; import org.dspace.identifier.service.IdentifierService; @@ -848,6 +849,7 @@ protected void rawDelete(Context context, Item item) throws AuthorizeException, DOI doi = doiService.findDOIByDSpaceObject(context, item); if (doi != null) { doi.setDSpaceObject(null); + doi.setStatus(DOIIdentifierProvider.TO_BE_DELETED); } // remove version attached to the item From fde825265db6b44ca0bd852ec6df56ca3f3eacfe Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 27 Sep 2024 12:52:03 +0200 Subject: [PATCH 300/479] fix: performance of claiming workflow task (cherry picked from commit 27dd5a2ec58970256964f15f0879834c436aa542) --- .../storedcomponents/PoolTaskServiceImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java index fb673725e181..d3c8f6334d8f 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; @@ -100,12 +101,17 @@ public PoolTask findByWorkflowIdAndEPerson(Context context, XmlWorkflowItem work //If the user does not have a claimedtask yet, see whether one of the groups of the user has pooltasks //for this workflow item Set groups = groupService.allMemberGroupsSet(context, ePerson); - for (Group group : groups) { - poolTask = poolTaskDAO.findByWorkflowItemAndGroup(context, group, workflowItem); - if (poolTask != null) { - return poolTask; - } + List generalTasks = poolTaskDAO.findByWorkflowItem(context, workflowItem); + Optional firstClaimedTask = groups.stream() + .flatMap(group -> generalTasks.stream() + .filter(f -> f.getGroup().getID().equals(group.getID())) + .findFirst() + .stream()) + .findFirst(); + + if (firstClaimedTask.isPresent()) { + return firstClaimedTask.get(); } } } From c66dde2aa85540dfbfa36f18f581599a6457889e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 10:26:29 -0600 Subject: [PATCH 301/479] Add a job to test Docker deployment with the built images --- .github/workflows/docker.yml | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a9ff8760e763..12d49689b319 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -147,4 +147,45 @@ jobs: tags_flavor: suffix=-loadsql secrets: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} \ No newline at end of file + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + ######################################################################## + # Test Deployment via Docker to ensure images are working properly + ######################################################################## + docker-deploy: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + runs-on: ubuntu-latest + # Must run after all major images are built + needs: [dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + steps: + - name: Checkout codebase + uses: actions/checkout@v4 + # Start backend using our compose script in the codebase. + - name: Start backend in Docker + env: + # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 + dspace__P__server__P__url: http://127.0.0.1:8080/server + run: | + docker compose -f docker-compose.yml up -d + sleep 10 + docker container ls + # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml + - name: Load test data into Backend + run: | + docker compose -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en + docker compose -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli + # Verify backend started successfully. + # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) + # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) + - name: Verify backend is responding properly + run: | + result=$(wget -O- -q http://127.0.0.1:8080/server/api) + echo "$result" + echo "$result" | grep -oE "\"DSpace Started with Docker Compose\"," + result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) + echo "$result" + echo "$result" | grep -oE "\"Dog in Yard\"," + - name: Shutdown Docker containers + run: | + docker compose -f docker-compose.yml down From 3ce85a2df3c548bf7842227cf86d8aa8bd2191b0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 14:10:44 -0600 Subject: [PATCH 302/479] Add a check that the Handle Server can be started & works properly --- .github/workflows/docker.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 12d49689b319..660c9eb817a6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -186,6 +186,25 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) echo "$result" echo "$result" | grep -oE "\"Dog in Yard\"," + # Verify Handle Server can be stared and is working properly + # 1. First generate the "[dspace]/handle-server" folder with the sitebndl.zip + # 2. Start the Handle Server (and wait 20 seconds to let it start up) + # 3. Verify logs do NOT include "Exception" in the text (as that means an error occurred) + # 4. Check that Handle Proxy HTML page is responding on default port (8000) + - name: Verify Handle Server is working properly + run: | + docker exec -i dspace /dspace/bin/make-handle-config + docker exec -i dspace /dspace/bin/start-handle-server + sleep 20 + echo "Checking for errors in handle-server.log..." + result=$(docker exec -i dspace cat /dspace/log/handle-server.log) + echo "$result" + echo "$result" | grep -vqz "Exception" + echo "Checking to see if Handle Proxy webpage is available..." + result=$(wget -O- -q http://127.0.0.1:8000/) + echo "$result" + echo "$result" | grep -oE "Handle Proxy" + # Shutdown our containers - name: Shutdown Docker containers run: | docker compose -f docker-compose.yml down From c1f168245b9756b6109bf6b53bc87317a66aa55c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 12 Nov 2024 13:50:57 -0600 Subject: [PATCH 303/479] Ensure Docker images built from PRs are stored as artifacts. This allows us to use those new images when testing deployment (in docker-deploy) --- .github/workflows/docker.yml | 34 +++++++++--- .github/workflows/reusable-docker-build.yml | 58 +++++++++++++++++++-- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 660c9eb817a6..eed8de5a8722 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -157,27 +157,47 @@ jobs: if: github.repository == 'dspace/dspace' runs-on: ubuntu-latest # Must run after all major images are built - needs: [dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + needs: [dspace, dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] steps: + # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase uses: actions/checkout@v4 - # Start backend using our compose script in the codebase. + # For PRs, download Docker image artifacts (built by reusable-docker-build.yml for all PRs) + - name: Download Docker image artifacts (for PRs) + if: github.event_name == 'pull_request' + uses: actions/download-artifact@v4 + with: + # Download all Docker images (TAR files) into the /tmp/docker directory + pattern: docker-image-* + path: /tmp/docker + merge-multiple: true + # For PRs, load each of the images into Docker by calling "docker image load" for each. + # This ensures we are using the images built from this PR & not the prior versions on DockerHub + - name: Load all downloaded Docker images (for PRs) + if: github.event_name == 'pull_request' + run: | + find /tmp/docker -type f -name "*.tar" -exec docker image load --input "{}" \; + docker image ls -a + # Start backend using our compose script in the codebase. - name: Start backend in Docker env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server + # Force using "pr-testing" version of Docker images. The "pr-testing" tag is a temporary tag that we assign to + # all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: pr-testing run: | docker compose -f docker-compose.yml up -d sleep 10 docker container ls - # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml + # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml - name: Load test data into Backend run: | docker compose -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en docker compose -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli - # Verify backend started successfully. - # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) - # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) + # Verify backend started successfully. + # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) + # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) - name: Verify backend is responding properly run: | result=$(wget -O- -q http://127.0.0.1:8080/server/api) @@ -204,7 +224,7 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8000/) echo "$result" echo "$result" | grep -oE "Handle Proxy" - # Shutdown our containers + # Shutdown our containers - name: Shutdown Docker containers run: | docker compose -f docker-compose.yml down diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 12aa0cfe2864..83dfd74b9a1a 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -113,6 +113,12 @@ jobs: - name: Set up QEMU emulation to build for multiple architectures uses: docker/setup-qemu-action@v3 + #------------------------------------------------------------ + # Build & deploy steps for new commits to a branch (non-PRs) + # + # These steps build the images, push to DockerHub, and + # (if necessary) redeploy demo/sandbox sites. + #------------------------------------------------------------ # 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 @@ -125,6 +131,7 @@ jobs: # https://github.com/docker/metadata-action # Get Metadata for docker_build_deps step below - name: Sync metadata (tags, labels) from GitHub to Docker for image + if: ${{ ! matrix.isPr }} id: meta_build uses: docker/metadata-action@v5 with: @@ -133,7 +140,9 @@ jobs: flavor: ${{ env.TAGS_FLAVOR }} # https://github.com/docker/build-push-action - - name: Build and push image + - name: Build and push image to DockerHub + # Only build & push if not a PR + if: ${{ ! matrix.isPr }} id: docker_build uses: docker/build-push-action@v5 with: @@ -142,9 +151,7 @@ jobs: context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ ! matrix.isPr }} + push: true # Use tags / labels provided by 'docker/metadata-action' above tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} @@ -189,11 +196,54 @@ jobs: run: | curl -X POST $REDEPLOY_DEMO_URL + #------------------------------------------------------------- + # Build steps for PRs only + # + # These steps build the images and store as a build artifact. + # These artifacts can then be used by later jobs to run the + # brand-new images for automated testing. + #-------------------------------------------------------------- + # Get Metadata for docker_build_deps step below + - name: Create metadata (tags, labels) for local Docker image + if: matrix.isPr + id: meta_build_pr + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + # Hardcode to use custom "pr-testing" tag because that will allow us to spin up this PR + # for testing in docker.yml + tags: pr-testing + flavor: ${{ env.TAGS_FLAVOR }} + # Build local image and stores in a TAR file in /tmp directory + - name: Build and push image to local image + if: matrix.isPr + uses: docker/build-push-action@v5 + with: + build-contexts: | + ${{ inputs.dockerfile_additional_contexts }} + context: ${{ inputs.dockerfile_context }} + file: ${{ inputs.dockerfile_path }} + platforms: ${{ matrix.arch }} + tags: ${{ steps.meta_build_pr.outputs.tags }} + labels: ${{ steps.meta_build_pr.outputs.labels }} + # Export image to a local TAR file + outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar + # Upload the local docker image (in TAR file) to a build Artifact + - name: Upload local image to artifact + if: matrix.isPr + uses: actions/upload-artifact@v4 + with: + name: docker-image-${{ inputs.build_id }} + path: /tmp/${{ inputs.build_id }}.tar + if-no-files-found: error + retention-days: 1 + # Merge Docker digests (from various architectures) into a manifest. # This runs after all Docker builds complete above, and it tells hub.docker.com # that these builds should be all included in the manifest for this tag. # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) docker-build_manifest: + # Only run if this is NOT a PR if: ${{ github.event_name != 'pull_request' }} runs-on: ubuntu-latest needs: From 9853aa5bb45ceef6ba6e01a08515f0cdea68f92c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 16:45:59 -0600 Subject: [PATCH 304/479] Fix error in running Handle Server in GitHub Actions. Must exclude "spring-jcl" from dependencies as it conflicts with "commons-logging" (used by more of our dependencies) --- dspace-api/pom.xml | 7 +++++++ dspace-iiif/pom.xml | 5 +++++ dspace-oai/pom.xml | 5 +++++ dspace-rdf/pom.xml | 5 +++++ dspace-server-webapp/pom.xml | 5 +++++ dspace-services/pom.xml | 7 +++++++ dspace-sword/pom.xml | 5 +++++ dspace-swordv2/pom.xml | 5 +++++ pom.xml | 7 +++++++ 9 files changed, 51 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index bdf035179bee..c3ef2c86044d 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -396,6 +396,13 @@ org.springframework spring-orm + + + + org.springframework + spring-jcl + + diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 64dd5106f0af..49ee02b56500 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -38,6 +38,11 @@ org.springframework.boot spring-boot-starter-logging
    + + + org.springframework + spring-jcl +
    diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 28b610e996c1..bfa2c8a27381 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -80,6 +80,11 @@ org.springframework.boot spring-boot-starter-logging
    + + + org.springframework + spring-jcl +
    diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index e656a920a1e6..075f2d7038e8 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -67,6 +67,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 5a4e1b32123d..35fa473fc170 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -405,6 +405,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 51c6dea62413..7c85ff7f0f0f 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -85,6 +85,13 @@ spring-context-support ${spring.version} compile + + + + org.springframework + spring-jcl + + org.apache.commons diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 1403c779e720..01aa68dfbfe6 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -60,6 +60,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 470388883126..f575898c122a 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -80,6 +80,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/pom.xml b/pom.xml index 02579c03294a..b0d9d1819d3f 100644 --- a/pom.xml +++ b/pom.xml @@ -1171,6 +1171,13 @@ org.springframework spring-orm ${spring.version} + + + + org.springframework + spring-jcl + + org.swordapp From faca14ad4059de0cb66c6d6ca0baf4161ac3aadd Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 09:17:30 -0600 Subject: [PATCH 305/479] Ensure "host" command is installed in images, so "bin/make-handle-config" will work. --- Dockerfile | 5 +++++ Dockerfile.test | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Dockerfile b/Dockerfile index 9d89710fe11c..ffce09239d1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,6 +55,11 @@ FROM tomcat:9-jdk${JDK_VERSION} ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL +# Need host command for "[dspace]/bin/make-handle-config" +RUN apt-get update \ + && apt-get install -y --no-install-recommends host \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* # Expose Tomcat port and AJP port EXPOSE 8080 8009 # Give java extra memory (2GB) diff --git a/Dockerfile.test b/Dockerfile.test index 031394ad256c..d88699ca52ab 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -54,6 +54,11 @@ ENV DSPACE_INSTALL=/dspace ENV TOMCAT_INSTALL=/usr/local/tomcat # Copy the /dspace directory from 'ant_build' containger to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL +# Need host command for "[dspace]/bin/make-handle-config" +RUN apt-get update \ + && apt-get install -y --no-install-recommends host \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* # Enable the AJP connector in Tomcat's server.xml # NOTE: secretRequired="false" should only be used when AJP is NOT accessible from an external network. But, secretRequired="true" isn't supported by mod_proxy_ajp until Apache 2.5 RUN sed -i '/Service name="Catalina".*/a \\n ' $TOMCAT_INSTALL/conf/server.xml From a0ed4a40eabab6296a577b3d7c8aa5a5fe024888 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 10:37:53 -0600 Subject: [PATCH 306/479] Bug fixes. Ensure all steps of docker-deploy use the same environment variables. Ensure Handle Server HTTP port is open --- .github/workflows/docker.yml | 13 +++++++------ Dockerfile | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index eed8de5a8722..5197ed3bfe39 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -158,6 +158,12 @@ jobs: runs-on: ubuntu-latest # Must run after all major images are built needs: [dspace, dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + env: + # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 + dspace__P__server__P__url: http://127.0.0.1:8080/server + # Force using "pr-testing" version of all Docker images. The "pr-testing" tag is a temporary tag that we + # assign to all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: pr-testing steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase @@ -180,12 +186,6 @@ jobs: docker image ls -a # Start backend using our compose script in the codebase. - name: Start backend in Docker - env: - # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 - dspace__P__server__P__url: http://127.0.0.1:8080/server - # Force using "pr-testing" version of Docker images. The "pr-testing" tag is a temporary tag that we assign to - # all PR-built docker images in reusabe-docker-build.yml - DSPACE_VER: pr-testing run: | docker compose -f docker-compose.yml up -d sleep 10 @@ -214,6 +214,7 @@ jobs: - name: Verify Handle Server is working properly run: | docker exec -i dspace /dspace/bin/make-handle-config + echo "Starting Handle Server..." docker exec -i dspace /dspace/bin/start-handle-server sleep 20 echo "Checking for errors in handle-server.log..." diff --git a/Dockerfile b/Dockerfile index ffce09239d1b..adc5d6125f0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,8 +60,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends host \ && apt-get purge -y --auto-remove \ && rm -rf /var/lib/apt/lists/* -# Expose Tomcat port and AJP port -EXPOSE 8080 8009 +# Expose Tomcat port (8080) and AJP port (8009) and Handle Server HTTP port (8000) +EXPOSE 8080 8009 8000 # Give java extra memory (2GB) ENV JAVA_OPTS=-Xmx2000m From d6d78298b31252155db7d00d7ae8eee8f2c0ac93 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 11:45:03 -0600 Subject: [PATCH 307/479] Add check for Handle Server error.log --- .github/workflows/docker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5197ed3bfe39..1e839e89fceb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -217,6 +217,10 @@ jobs: echo "Starting Handle Server..." docker exec -i dspace /dspace/bin/start-handle-server sleep 20 + echo "Checking for errors in error.log" + result=$(docker exec -i dspace sh -c "cat /dspace/handle-server/logs/error.log* || echo ''") + echo "$result" + echo "$result" | grep -vqz "Exception" echo "Checking for errors in handle-server.log..." result=$(docker exec -i dspace cat /dspace/log/handle-server.log) echo "$result" From 5bb65c6b5647f74161244868c0864e9adb12d12a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 14:27:28 -0600 Subject: [PATCH 308/479] Fix error in Handle Server startup caused by having multiple versions of BouncyCastle in our classpath. Exclude the old version brought in by cnri-servlet-container --- dspace-api/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c3ef2c86044d..c42895ab4c49 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -427,6 +427,16 @@ org.bouncycastle bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + org.bouncycastle + bcprov-jdk15on + From aa71e4840be6fa9c6368e061d3e169e08a89c66a Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Mon, 5 Aug 2024 11:06:06 +0200 Subject: [PATCH 309/479] Fix 9734: Check configured workflow.reviewer.file-edit to show item edit functionality in workflow UI (cherry picked from commit e8ec0c1b1d20dd5e812f38593a24718fff1d8c6e) --- .../processingaction/ScoreReviewAction.java | 15 ++++++++++++++- .../processingaction/SingleUserReviewAction.java | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java index 43a3decacc7e..419bb1236402 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java @@ -8,6 +8,7 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -20,6 +21,8 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.MetadataFieldName; import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xmlworkflow.service.WorkflowRequirementsService; import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo; @@ -34,6 +37,9 @@ public class ScoreReviewAction extends ProcessingAction { private static final Logger log = LogManager.getLogger(ScoreReviewAction.class); + private final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + // Option(s) public static final String SUBMIT_SCORE = "submit_score"; @@ -114,7 +120,14 @@ private boolean checkRequestValid(int score, String review) { @Override public List getOptions() { - return List.of(SUBMIT_SCORE, RETURN_TO_POOL); + List options = new ArrayList<>(); + options.add(SUBMIT_SCORE); + if (configurationService.getBooleanProperty("workflow.reviewer.file-edit", false)) { + options.add(SUBMIT_EDIT_METADATA); + } + options.add(RETURN_TO_POOL); + + return options; } @Override diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java index b3fe896ace24..0b8f2d13648c 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java @@ -21,6 +21,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.workflow.WorkflowException; import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; import org.dspace.xmlworkflow.state.Step; @@ -40,6 +42,9 @@ public class SingleUserReviewAction extends ProcessingAction { private static final Logger log = LogManager.getLogger(SingleUserReviewAction.class); + private final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + public static final int OUTCOME_REJECT = 1; protected static final String SUBMIT_DECLINE_TASK = "submit_decline_task"; @@ -95,6 +100,9 @@ public ActionResult processAccept(Context c, XmlWorkflowItem wfi) throws SQLExce public List getOptions() { List options = new ArrayList<>(); options.add(SUBMIT_APPROVE); + if (configurationService.getBooleanProperty("workflow.reviewer.file-edit", false)) { + options.add(SUBMIT_EDIT_METADATA); + } options.add(SUBMIT_REJECT); options.add(SUBMIT_DECLINE_TASK); return options; From 7fa31e21315db54a5f99e4d34dc91b29353a92b8 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 08:59:19 -0600 Subject: [PATCH 310/479] Ensure we use "pr-testing" images for PRs, but use "latest" images for other builds (e.g. after PR is merged to a branch). (cherry picked from commit aa537c44902f458a684bcbb67b5ff702ded61363) --- .github/workflows/docker.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1e839e89fceb..a0aee14bea38 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,9 +161,10 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server - # Force using "pr-testing" version of all Docker images. The "pr-testing" tag is a temporary tag that we - # assign to all PR-built docker images in reusabe-docker-build.yml - DSPACE_VER: pr-testing + # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, for branch commits, use the + # "latest" tag. NOTE: the "pr-testing" tag is a temporary tag that we assign to all PR-built docker images in + # reusabe-docker-build.yml + DSPACE_VER: ${{ github.event_name == 'pull_request' && 'pr-testing' || 'latest' }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From f0c92ac96b6f6f22445020b9e9690886bb83830c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 16:06:10 -0600 Subject: [PATCH 311/479] Ensure only main branch uses "latest". Other branches should use the tag corresponding to the branch name (cherry picked from commit e0b7241acb167496ebc8c80c73ae69b9f6611a1c) --- .github/workflows/docker.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a0aee14bea38..143b69a2f9fe 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,10 +161,10 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server - # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, for branch commits, use the - # "latest" tag. NOTE: the "pr-testing" tag is a temporary tag that we assign to all PR-built docker images in - # reusabe-docker-build.yml - DSPACE_VER: ${{ github.event_name == 'pull_request' && 'pr-testing' || 'latest' }} + # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the + # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to + # all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: ${{ (github.event_name == 'pull_request' && 'pr-testing') || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From d952fea6a2f6b5dc8a54b4e6ba3deecca2d7a4fe Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 11:30:14 -0600 Subject: [PATCH 312/479] Remove parboiled-java and minor cleanup of unused OAI dependencies --- dspace-oai/pom.xml | 23 +------------------ .../xoai/app/CCElementItemCompilePlugin.java | 2 +- .../main/java/org/dspace/xoai/app/XOAI.java | 2 +- .../tests/integration/xoai/PipelineTest.java | 9 ++++---- 4 files changed, 8 insertions(+), 28 deletions(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index bfa2c8a27381..2f8dae45b236 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -99,22 +99,9 @@ org.springframework.boot spring-boot-starter-web - - - org.parboiled - parboiled-java - - - - org.parboiled - parboiled-java - 1.3.1 - - org.dspace @@ -137,15 +124,7 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - - - org.apache.logging.log4j - log4j-slf4j-impl - runtime - + org.apache.logging.log4j log4j-1.2-api diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java b/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java index 225d56a4c982..370543029d8b 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java @@ -11,7 +11,7 @@ import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.license.factory.LicenseServiceFactory; 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 4f842b8e944c..c6aaaa34b539 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 @@ -9,7 +9,7 @@ import static com.lyncode.xoai.dataprovider.core.Granularity.Second; import static java.util.Objects.nonNull; -import static org.apache.commons.lang.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.solr.common.params.CursorMarkParams.CURSOR_MARK_PARAM; import static org.apache.solr.common.params.CursorMarkParams.CURSOR_MARK_START; import static org.dspace.xoai.util.ItemUtils.retrieveMetadata; 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 0f48824159c2..0f7ffde0bd00 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 @@ -13,13 +13,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import java.io.InputStream; +import java.nio.charset.Charset; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; import com.lyncode.xoai.util.XSLPipeline; +import org.apache.commons.io.IOUtils; import org.dspace.xoai.tests.support.XmlMatcherBuilder; import org.junit.Test; -import org.parboiled.common.FileUtils; public class PipelineTest { private static TransformerFactory factory = TransformerFactory.newInstance(); @@ -28,9 +29,9 @@ public class PipelineTest { public void pipelineTest() throws Exception { 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.newTemplates(new StreamSource(xslt))) - .getTransformed()); + String output = IOUtils.toString(new XSLPipeline(input, true) + .apply(factory.newTemplates(new StreamSource(xslt))) + .getTransformed(), Charset.defaultCharset()); assertThat(output, oai_dc().withXPath("/oai_dc:dc/dc:title", equalTo("Teste"))); From f3d15e5c04227ceb5425e671189ec4eb5ea42656 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 12:02:20 -0600 Subject: [PATCH 313/479] Log4j cleanup. Remove last traces of log4j v1 (and remove log4j1 bridge to avoid them coming back). Create log4j2 settings for Handle Plugin. --- .../app/bulkedit/MetadataExportSearchIT.java | 5 +- .../dspace/builder/OrcidHistoryBuilder.java | 5 +- dspace-oai/pom.xml | 5 -- dspace-rdf/pom.xml | 4 -- dspace-rest/pom.xml | 4 -- .../rest/converter/SearchEventConverter.java | 5 +- .../bitstream/BitstreamLinksetProcessor.java | 5 +- .../BitstreamParentItemProcessor.java | 5 +- .../bitstream/BitstreamTypeProcessor.java | 5 +- .../processor/item/ItemAuthorProcessor.java | 5 +- .../item/ItemContentBitstreamsProcessor.java | 5 +- .../item/ItemDescribedbyProcessor.java | 5 +- .../processor/item/ItemLicenseProcessor.java | 5 +- .../processor/item/ItemLinksetProcessor.java | 5 +- .../processor/item/ItemTypeProcessor.java | 5 +- .../service/impl/LinksetServiceImpl.java | 5 +- dspace-sword/pom.xml | 4 -- dspace-swordv2/pom.xml | 4 -- dspace/bin/start-handle-server | 2 +- dspace/bin/start-handle-server.bat | 2 +- dspace/config/log4j-handle-plugin.properties | 34 ------------- dspace/config/log4j2-handle-plugin.xml | 48 +++++++++++++++++++ pom.xml | 25 ---------- 23 files changed, 89 insertions(+), 108 deletions(-) delete mode 100644 dspace/config/log4j-handle-plugin.properties create mode 100644 dspace/config/log4j2-handle-plugin.xml diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java index 3a972692efeb..63a87a48f554 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java @@ -23,7 +23,8 @@ import com.google.common.io.Files; import com.opencsv.CSVReader; import com.opencsv.exceptions.CsvException; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; @@ -51,7 +52,7 @@ public class MetadataExportSearchIT extends AbstractIntegrationTestWithDatabase private Item[] itemsSubject2 = new Item[numberItemsSubject2]; private String filename; private Collection collection; - private Logger logger = Logger.getLogger(MetadataExportSearchIT.class); + private Logger logger = LogManager.getLogger(MetadataExportSearchIT.class); private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); private SearchService searchService; diff --git a/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java b/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java index 199f412f8506..d811d03f5358 100644 --- a/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java @@ -11,7 +11,8 @@ import java.sql.SQLException; import java.util.Date; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.orcid.OrcidHistory; @@ -24,7 +25,7 @@ */ public class OrcidHistoryBuilder extends AbstractBuilder { - private static final Logger log = Logger.getLogger(OrcidHistoryBuilder.class); + private static final Logger log = LogManager.getLogger(OrcidHistoryBuilder.class); private OrcidHistory orcidHistory; diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 2f8dae45b236..f7acdae99292 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -124,11 +124,6 @@ org.apache.logging.log4j log4j-core - - - org.apache.logging.log4j - log4j-1.2-api - diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 075f2d7038e8..64d88b63c780 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -89,10 +89,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - org.apache.commons diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index d1f3b95aafb0..3f4bdaf1d0d8 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -185,10 +185,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - org.dspace dspace-services diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java index 126d37ba1ace..978ae2ca9230 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java @@ -13,7 +13,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.model.PageRest; import org.dspace.app.rest.model.SearchEventRest; import org.dspace.app.rest.model.SearchResultsRest; @@ -31,7 +32,7 @@ @Component public class SearchEventConverter { /* Log4j logger */ - private static final Logger log = Logger.getLogger(SearchEventConverter.class); + private static final Logger log = LogManager.getLogger(SearchEventConverter.class); @Autowired private ScopeResolver scopeResolver; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java index c65191cb0749..c9ee193b3536 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -25,7 +26,7 @@ */ public class BitstreamLinksetProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamLinksetProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamLinksetProcessor.class); private final BitstreamService bitstreamService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java index 815d7817d4cf..beb318a73481 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -28,7 +29,7 @@ */ public class BitstreamParentItemProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamParentItemProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamParentItemProcessor.class); private final BitstreamService bitstreamService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java index 005a8009836d..1dabf398da9b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -28,7 +29,7 @@ */ public class BitstreamTypeProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamTypeProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamTypeProcessor.class); @Autowired private SimpleMapConverter mapConverterDSpaceToSchemaOrgUri; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java index 1bb215c46864..b96a41b00b2b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java @@ -16,7 +16,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -37,7 +38,7 @@ public class ItemAuthorProcessor extends ItemSignpostingProcessor { /** * log4j category */ - private static final Logger log = Logger.getLogger(ItemAuthorProcessor.class); + private static final Logger log = LogManager.getLogger(ItemAuthorProcessor.class); private final ItemService itemService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java index 61bf371adbdf..40c3d96cf680 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java @@ -11,7 +11,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -33,7 +34,7 @@ public class ItemContentBitstreamsProcessor extends ItemSignpostingProcessor { /** * log4j category */ - private static final Logger log = Logger.getLogger(ItemContentBitstreamsProcessor.class); + private static final Logger log = LogManager.getLogger(ItemContentBitstreamsProcessor.class); public ItemContentBitstreamsProcessor(FrontendUrlService frontendUrlService) { super(frontendUrlService); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java index a16770c4d103..62374d7ee830 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -23,7 +24,7 @@ */ public class ItemDescribedbyProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemDescribedbyProcessor.class); + private static final Logger log = LogManager.getLogger(ItemDescribedbyProcessor.class); private final ConfigurationService configurationService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java index 1a26fa7695b1..a88e9eba37d2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -25,7 +26,7 @@ */ public class ItemLicenseProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemLicenseProcessor.class); + private static final Logger log = LogManager.getLogger(ItemLicenseProcessor.class); private final CreativeCommonsService creativeCommonsService = LicenseServiceFactory.getInstance().getCreativeCommonsService(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java index 9008a28e29a6..1c765047a62d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -23,7 +24,7 @@ */ public class ItemLinksetProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemLinksetProcessor.class); + private static final Logger log = LogManager.getLogger(ItemLinksetProcessor.class); private final ConfigurationService configurationService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java index ddd2da12d59a..6945c33619f7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -27,7 +28,7 @@ */ public class ItemTypeProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemTypeProcessor.class); + private static final Logger log = LogManager.getLogger(ItemTypeProcessor.class); private static final String ABOUT_PAGE_URI = "https://schema.org/AboutPage"; @Autowired diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java index 399b7bd1e6b0..de5556173557 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java @@ -13,7 +13,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.security.BitstreamMetadataReadPermissionEvaluatorPlugin; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.processor.bitstream.BitstreamSignpostingProcessor; @@ -37,7 +38,7 @@ @Service public class LinksetServiceImpl implements LinksetService { - private static final Logger log = Logger.getLogger(LinksetServiceImpl.class); + private static final Logger log = LogManager.getLogger(LinksetServiceImpl.class); @Autowired protected ItemService itemService; diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 01aa68dfbfe6..b72bee24db57 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -101,10 +101,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - xom diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index f575898c122a..be9430b4d123 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -97,10 +97,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - + + + + + ${log4j:configParentLocation}/../log + + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b0d9d1819d3f..9b4dcef37dcf 100644 --- a/pom.xml +++ b/pom.xml @@ -1577,36 +1577,11 @@ log4j-api ${log4j.version} - - org.apache.logging.log4j - log4j-1.2-api - ${log4j.version} - org.apache.logging.log4j log4j-core ${log4j.version} - - org.apache.logging.log4j - log4j-web - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - - - org.apache.logging.log4j - log4j-jul - ${log4j.version} - - - org.slf4j - jul-to-slf4j - ${slf4j.version} - org.apache.pdfbox From 9f8240987b71f1f37d4f791854b85bcbe870b032 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 20 Nov 2024 12:45:05 -0600 Subject: [PATCH 314/479] Remove unused dependencies from several modules --- dspace-services/pom.xml | 5 ----- dspace-sword/pom.xml | 18 ------------------ dspace/modules/server/pom.xml | 5 ----- pom.xml | 14 +------------- 4 files changed, 1 insertion(+), 41 deletions(-) diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 7c85ff7f0f0f..6504ae2e4b2a 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -131,10 +131,6 @@ 6.1.26 test - - org.apache.commons - commons-collections4 - org.apache.commons commons-configuration2 @@ -159,6 +155,5 @@ spring-boot-starter-log4j2 ${spring-boot.version} - diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index b72bee24db57..12325533c5c5 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -25,15 +25,6 @@
    - - - - org.dspace dspace-api @@ -68,11 +59,6 @@ - - jaxen - jaxen - - commons-fileupload @@ -107,10 +93,6 @@ xom 1.3.9 - - commons-io - commons-io - diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 5134ffb94733..cdbec2f60c8b 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -262,11 +262,6 @@ just adding new jar in the classloader dspace-server-webapp war - - org.apache.solr - solr-solrj - ${solr.client.version} - diff --git a/pom.xml b/pom.xml index 9b4dcef37dcf..c0127724c1d5 100644 --- a/pom.xml +++ b/pom.xml @@ -1179,11 +1179,7 @@ - - org.swordapp - sword-common - 1.1 - + spring-core @@ -1484,14 +1480,6 @@ commons-collections4 4.4 - - - commons-collections - commons-collections - 3.2.2 - org.apache.commons commons-configuration2 From f44dba60cba8cacaa785e9bc5329b569f77a0c1e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 11:04:11 -0600 Subject: [PATCH 315/479] Enable all optional modules/controllers to test their deployment in Spring Boot (cherry picked from commit 98768d6f4fcc5181501d02d77ef5056641ac8cd3) --- .github/workflows/docker.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 143b69a2f9fe..7013f1e71a09 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,6 +161,14 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server + # Enable all optional modules / controllers for this test deployment. + # This helps check for errors in deploying these modules via Spring Boot + iiif__P__enabled: true + oai__P__enabled: true + rdf__P__enabled: true + signposting__P__enabled: true + sword-server__P__enabled: true + swordv2-server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to # all PR-built docker images in reusabe-docker-build.yml From 66a9782eeefc4e9211a4efdd1a10b1303640415e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 13:50:16 -0600 Subject: [PATCH 316/479] Fix syntax error in #10040. Env variables cannot have dashes or periods --- .github/workflows/docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7013f1e71a09..daa940f215a1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -167,8 +167,8 @@ jobs: oai__P__enabled: true rdf__P__enabled: true signposting__P__enabled: true - sword-server__P__enabled: true - swordv2-server__P__enabled: true + sword__D__server__P__enabled: true + swordv2__D__server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to # all PR-built docker images in reusabe-docker-build.yml From cf99694a84714221ed506fa9afd54f262c11c1ce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 14:58:08 -0600 Subject: [PATCH 317/479] Fix startup errors for SWORDv2. Requires the log4jv1->v2 bridge to be installed. --- dspace-swordv2/pom.xml | 6 ++++++ pom.xml | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index be9430b4d123..7cae05672470 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -97,6 +97,12 @@ org.apache.logging.log4j log4j-core + + + org.apache.logging.log4j + log4j-1.2-api + + + org.apache.logging.log4j + log4j-1.2-api + ${log4j.version} + org.apache.pdfbox From 54a1c75cbc652bbb60e39157450043087c2d822b Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 28 Nov 2024 13:06:23 +0100 Subject: [PATCH 318/479] 109807: ArXiv mapping fix - author/name to dc.contributor.author https://info.arxiv.org/help/api/basics.html#using --- dspace/config/spring/api/arxiv-integration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/arxiv-integration.xml b/dspace/config/spring/api/arxiv-integration.xml index e963e73a2055..3f7f408dff40 100644 --- a/dspace/config/spring/api/arxiv-integration.xml +++ b/dspace/config/spring/api/arxiv-integration.xml @@ -94,7 +94,7 @@ - + From 38a71cc664fffc5e6bfd7809cc95960a552750eb Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 30 Oct 2024 13:37:53 +0100 Subject: [PATCH 319/479] 119960: Fixed NPE when retrieving a DSpace object with the api/dso/find endpoint without the required permissions (cherry picked from commit dd8b1d91cb74c5afadc1e545b2192b5ebb8848b1) --- .../app/rest/UUIDLookupRestController.java | 10 ++++- .../app/rest/UUIDLookupRestControllerIT.java | 37 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/UUIDLookupRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/UUIDLookupRestController.java index 40c0a79b97be..21631a6737a9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/UUIDLookupRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/UUIDLookupRestController.java @@ -24,9 +24,11 @@ import org.dspace.app.rest.utils.ContextUtil; import org.dspace.app.rest.utils.DSpaceObjectUtils; import org.dspace.app.rest.utils.Utils; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.DSpaceObject; +import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.discovery.SearchServiceException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.Link; @@ -65,6 +67,9 @@ public class UUIDLookupRestController implements InitializingBean { @Autowired private DiscoverableEndpointsService discoverableEndpointsService; + @Autowired + private AuthorizeService authorizeService; + @Autowired private ConverterService converter; @@ -85,13 +90,14 @@ public void afterPropertiesSet() throws Exception { public void getDSObyIdentifier(HttpServletRequest request, HttpServletResponse response, @RequestParam(PARAM) UUID uuid) - throws IOException, SQLException, SearchServiceException { + throws IOException, SQLException, AuthorizeException { Context context = null; try { context = ContextUtil.obtainContext(request); DSpaceObject dso = dspaceObjectUtil.findDSpaceObject(context, uuid); if (dso != null) { + authorizeService.authorizeAction(context, dso, Constants.READ); DSpaceObjectRest dsor = converter.toRest(dso, utils.obtainProjection()); URI link = linkTo(dsor.getController(), dsor.getCategory(), dsor.getTypePlural()).slash(dsor.getId()) .toUri(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java index 8a6debce3ec7..3b0821645861 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java @@ -17,6 +17,8 @@ import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.builder.BitstreamBuilder; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -31,6 +33,7 @@ import org.dspace.eperson.Group; import org.junit.Ignore; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; /** * Integration test for the UUIDLookup endpoint @@ -39,6 +42,9 @@ */ public class UUIDLookupRestControllerIT extends AbstractControllerIntegrationTest { + @Autowired + ResourcePolicyService resourcePolicyService; + @Test /** * Test the proper redirection of a site's uuid @@ -307,4 +313,35 @@ public void testMissingIdentifierParameter() throws Exception { .andExpect(status().isUnprocessableEntity()); } + @Test + public void testUnauthorized() throws Exception { + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context) + .build(); + for (ResourcePolicy rp : resourcePolicyService.find(context, community)) { + resourcePolicyService.delete(context, rp); + } + context.restoreAuthSystemState(); + + getClient().perform(get("/api/dso/find") + .param("uuid", community.getID().toString())) + .andExpect(status().isUnauthorized()); + } + + @Test + public void testForbidden() throws Exception { + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context) + .build(); + for (ResourcePolicy rp : resourcePolicyService.find(context, community)) { + resourcePolicyService.delete(context, rp); + } + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform(get("/api/dso/find") + .param("uuid", community.getID().toString())) + .andExpect(status().isForbidden()); + } + } From 2136dbf690389f881fc73373fda6be87b63249ab Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Tue, 4 Jun 2024 16:02:53 -0400 Subject: [PATCH 320/479] 110719: Port fix to checkLinks that works on redirects (cherry picked from commit 3dab2a7cea812420422af160d86371a59b48374f) --- .../ctask/general/BasicLinkChecker.java | 14 +++++-- .../impl/TestDSpaceRunnableHandler.java | 39 +++++++++++++++++ .../org/dspace/curate/CurationScriptIT.java | 42 +++++++++++++++++++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index fbc6eebdb5b8..388036bd6744 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -132,10 +132,18 @@ protected int getResponseStatus(String url) { try { URL theURL = new URL(url); HttpURLConnection connection = (HttpURLConnection) theURL.openConnection(); - int code = connection.getResponseCode(); - connection.disconnect(); + connection.setInstanceFollowRedirects(true); + int statusCode = connection.getResponseCode(); + if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || + statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { + connection.disconnect(); + String newUrl = connection.getHeaderField("Location"); + if (newUrl != null) { + return getResponseStatus(newUrl); + } - return code; + } + return statusCode; } catch (IOException ioe) { // Must be a bad URL diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/scripts/handler/impl/TestDSpaceRunnableHandler.java b/dspace-server-webapp/src/test/java/org/dspace/app/scripts/handler/impl/TestDSpaceRunnableHandler.java index 1b5b3fa7ac1a..aced81cbdfdb 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/scripts/handler/impl/TestDSpaceRunnableHandler.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/scripts/handler/impl/TestDSpaceRunnableHandler.java @@ -7,6 +7,9 @@ */ package org.dspace.app.scripts.handler.impl; +import java.util.ArrayList; +import java.util.List; + import org.dspace.scripts.handler.impl.CommandLineDSpaceRunnableHandler; /** @@ -17,6 +20,12 @@ public class TestDSpaceRunnableHandler extends CommandLineDSpaceRunnableHandler private Exception exception = null; + private final List infoMessages = new ArrayList<>(); + + private final List errorMessages = new ArrayList<>(); + + private final List warningMessages = new ArrayList<>(); + /** * We're overriding this method so that we can stop the script from doing the System.exit() if * an exception within the script is thrown @@ -33,4 +42,34 @@ public void handleException(String message, Exception e) { public Exception getException() { return exception; } + + @Override + public void logInfo(String message) { + super.logInfo(message); + infoMessages.add(message); + } + + @Override + public void logWarning(String message) { + super.logWarning(message); + warningMessages.add(message); + } + + @Override + public void logError(String message) { + super.logError(message); + errorMessages.add(message); + } + + public List getInfoMessages() { + return infoMessages; + } + + public List getErrorMessages() { + return errorMessages; + } + + public List getWarningMessages() { + return warningMessages; + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java index 3e40a8559482..6bb9acb92482 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java @@ -9,6 +9,7 @@ import static com.jayway.jsonpath.JsonPath.read; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -21,6 +22,7 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.databind.ObjectMapper; +import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.rest.converter.DSpaceRunnableParameterConverter; import org.dspace.app.rest.matcher.ProcessMatcher; import org.dspace.app.rest.model.ParameterValueRest; @@ -28,6 +30,7 @@ import org.dspace.app.rest.model.ScriptRest; import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; @@ -41,7 +44,9 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.eperson.EPerson; import org.dspace.scripts.DSpaceCommandLineParameter; +import org.dspace.scripts.DSpaceRunnable; import org.dspace.scripts.configuration.ScriptConfiguration; +import org.dspace.scripts.factory.ScriptServiceFactory; import org.dspace.scripts.service.ScriptService; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -640,4 +645,41 @@ public void securityCurateTest() throws Exception { ProcessBuilder.deleteProcess(idItemRef.get()); } } + + @Test + public void testURLRedirectCurateTest() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withMetadata("dc", "identifier", "uri", "https://tinyurl.com/2rxpte5s") + .withSubject("ExtraEntry") + .build(); + + String[] args = new String[] {"curate", "-t", "checklinks", "-i", publicItem1.getHandle()}; + TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); + + ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService(); + ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]); + + DSpaceRunnable script = null; + if (scriptConfiguration != null) { + script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); + } + if (script != null) { + script.initialize(args, testDSpaceRunnableHandler, admin); + script.run(); + } + + assertTrue(testDSpaceRunnableHandler.getInfoMessages().contains("200 - OK")); + } } From be905a088750911928b9515f36cc2a24fc2b518f Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 13 Jun 2024 20:48:31 +0200 Subject: [PATCH 321/479] 110719: IT checking redirect links accepted by checklinks curate task (cherry picked from commit e826660cb0bd926de390e04e466d54ceedb67a1e) --- .../org/dspace/curate/CurationScriptIT.java | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java index 6bb9acb92482..8c0744a09cce 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java @@ -9,6 +9,7 @@ import static com.jayway.jsonpath.JsonPath.read; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -22,7 +23,7 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.databind.ObjectMapper; -import org.dspace.app.launcher.ScriptLauncher; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.converter.DSpaceRunnableParameterConverter; import org.dspace.app.rest.matcher.ProcessMatcher; import org.dspace.app.rest.model.ParameterValueRest; @@ -661,12 +662,19 @@ public void testURLRedirectCurateTest() throws Exception { .withTitle("Public item 1") .withIssueDate("2017-10-17") .withAuthor("Smith, Donald").withAuthor("Doe, John") - .withMetadata("dc", "identifier", "uri", "https://tinyurl.com/2rxpte5s") + // Value not starting with http or https + .withMetadata("dc", "identifier", "uri", "demo.dspace.org/home") + // MetadataValueLinkChecker uri field with regular link + .withMetadata("dc", "description", null, "https://google.com") + // MetadataValueLinkChecker uri field with redirect link + .withMetadata("dc", "description", "uri", "https://demo7.dspace.org/handle/123456789/1") + // MetadataValueLinkChecker uri field with non resolving link + .withMetadata("dc", "description", "uri", "https://www.atmire.com/broken-link") .withSubject("ExtraEntry") .build(); String[] args = new String[] {"curate", "-t", "checklinks", "-i", publicItem1.getHandle()}; - TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); + TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler(); ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService(); ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]); @@ -676,10 +684,27 @@ public void testURLRedirectCurateTest() throws Exception { script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); } if (script != null) { - script.initialize(args, testDSpaceRunnableHandler, admin); + script.initialize(args, handler, admin); script.run(); } - assertTrue(testDSpaceRunnableHandler.getInfoMessages().contains("200 - OK")); + // field that should be ignored + assertFalse(checkIfInfoTextLoggedByHandler(handler, "demo.dspace.org/home")); + // redirect links in field that should not be ignored (https) => expect OK + assertTrue(checkIfInfoTextLoggedByHandler(handler, "https://demo7.dspace.org/handle/123456789/1 = 200 - OK")); + // regular link in field that should not be ignored (http) => expect OK + assertTrue(checkIfInfoTextLoggedByHandler(handler, "https://google.com = 200 - OK")); + // nonexistent link in field that should not be ignored => expect 404 + assertTrue(checkIfInfoTextLoggedByHandler(handler, "https://www.atmire.com/broken-link = 404 - FAILED")); } + + boolean checkIfInfoTextLoggedByHandler(TestDSpaceRunnableHandler handler, String text) { + for (String message: handler.getInfoMessages()) { + if (StringUtils.containsIgnoreCase(message, text)) { + return true; + } + } + return false; + } + } From 3ff38ec4b44acbae43292b0b26623623aac64200 Mon Sep 17 00:00:00 2001 From: Nathan Buckingham Date: Tue, 12 Nov 2024 15:59:17 -0500 Subject: [PATCH 322/479] 115778: Adjust redirect check to only follow a limited amount defined in curate.cfg (cherry picked from commit ef381aa1516df0d45227cd6c752b32658a69509b) --- .../dspace/ctask/general/BasicLinkChecker.java | 16 ++++++++++++---- dspace/config/modules/curate.cfg | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index 388036bd6744..a302159ea9a4 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -19,6 +19,8 @@ import org.dspace.content.MetadataValue; import org.dspace.curate.AbstractCurationTask; import org.dspace.curate.Curator; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; /** * A basic link checker that is designed to be extended. By default this link checker @@ -42,6 +44,9 @@ public class BasicLinkChecker extends AbstractCurationTask { // The log4j logger for this class private static Logger log = org.apache.logging.log4j.LogManager.getLogger(BasicLinkChecker.class); + protected static final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + /** * Perform the link checking. @@ -110,7 +115,8 @@ protected List getURLs(Item item) { */ protected boolean checkURL(String url, StringBuilder results) { // Link check the URL - int httpStatus = getResponseStatus(url); + int redirects = 0; + int httpStatus = getResponseStatus(url, redirects); if ((httpStatus >= 200) && (httpStatus < 300)) { results.append(" - " + url + " = " + httpStatus + " - OK\n"); @@ -128,18 +134,20 @@ protected boolean checkURL(String url, StringBuilder results) { * @param url The url to open * @return The HTTP response code (e.g. 200 / 301 / 404 / 500) */ - protected int getResponseStatus(String url) { + protected int getResponseStatus(String url, int redirects) { try { URL theURL = new URL(url); HttpURLConnection connection = (HttpURLConnection) theURL.openConnection(); connection.setInstanceFollowRedirects(true); int statusCode = connection.getResponseCode(); + int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { connection.disconnect(); String newUrl = connection.getHeaderField("Location"); - if (newUrl != null) { - return getResponseStatus(newUrl); + if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { + redirects++; + return getResponseStatus(newUrl, redirects); } } diff --git a/dspace/config/modules/curate.cfg b/dspace/config/modules/curate.cfg index 1d7b87960df1..6e75738de543 100644 --- a/dspace/config/modules/curate.cfg +++ b/dspace/config/modules/curate.cfg @@ -26,3 +26,6 @@ curate.taskqueue.dir = ${dspace.dir}/ctqueues # (optional) directory location of scripted (non-java) tasks # curate.script.dir = ${dspace.dir}/ctscripts + +# Maximum amount of redirects set to 0 for none and -1 for unlimited +curate.checklinks.max-redirect = 0 From 25fb8111f1aa18cd45b331d169dce32789a97068 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Tue, 23 Jul 2024 18:28:43 +0200 Subject: [PATCH 323/479] 116609: Improve running process observability - keep temp process log files in [dspace]/log/processes/ instead of temp dir - reformat file names of process logs - ensure that running and scheduled processes are cleaned up during startup (cherry picked from commit d80f49e0238981c22703abbb1423aa2896eca694) --- .../dspace/scripts/ProcessServiceImpl.java | 56 +++++++++++++++---- .../app/rest/ProcessRestRepositoryIT.java | 8 +-- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index f242ec7acdad..f48fd02a920a 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -45,14 +45,15 @@ import org.dspace.core.LogHelper; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; -import org.dspace.eperson.service.EPersonService; import org.dspace.scripts.service.ProcessService; +import org.dspace.services.ConfigurationService; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; /** * The implementation for the {@link ProcessService} class */ -public class ProcessServiceImpl implements ProcessService { +public class ProcessServiceImpl implements ProcessService, InitializingBean { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ProcessService.class); @@ -72,7 +73,26 @@ public class ProcessServiceImpl implements ProcessService { private MetadataFieldService metadataFieldService; @Autowired - private EPersonService ePersonService; + private ConfigurationService configurationService; + + @Override + public void afterPropertiesSet() throws Exception { + Context context = new Context(); + + // Processes that were running or scheduled when tomcat crashed, should be cleaned up during startup. + List processesToBeFailed = findByStatusAndCreationTimeOlderThan( + context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); + for (Process process : processesToBeFailed) { + context.setCurrentUser(process.getEPerson()); + // Fail the process. + log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", process.getID()); + fail(context, process); + // But still attach its log to the process. + createLogBitstream(context, process); + } + + context.complete(); + } @Override public Process create(Context context, EPerson ePerson, String scriptName, @@ -293,8 +313,8 @@ public int countSearch(Context context, ProcessQueryParameterContainer processQu @Override public void appendLog(int processId, String scriptName, String output, ProcessLogLevel processLogLevel) throws IOException { - File tmpDir = FileUtils.getTempDirectory(); - File tempFile = new File(tmpDir, scriptName + processId + ".log"); + File logsDir = getLogsDirectory(); + File tempFile = new File(logsDir, processId + "-" + scriptName + ".log"); FileWriter out = new FileWriter(tempFile, true); try { try (BufferedWriter writer = new BufferedWriter(out)) { @@ -309,12 +329,15 @@ public void appendLog(int processId, String scriptName, String output, ProcessLo @Override public void createLogBitstream(Context context, Process process) throws IOException, SQLException, AuthorizeException { - File tmpDir = FileUtils.getTempDirectory(); - File tempFile = new File(tmpDir, process.getName() + process.getID() + ".log"); - FileInputStream inputStream = FileUtils.openInputStream(tempFile); - appendFile(context, process, inputStream, Process.OUTPUT_TYPE, process.getName() + process.getID() + ".log"); - inputStream.close(); - tempFile.delete(); + File logsDir = getLogsDirectory(); + File tempFile = new File(logsDir, process.getID() + "-" + process.getName() + ".log"); + if (tempFile.exists()) { + FileInputStream inputStream = FileUtils.openInputStream(tempFile); + appendFile(context, process, inputStream, Process.OUTPUT_TYPE, + process.getID() + "-" + process.getName() + ".log"); + inputStream.close(); + tempFile.delete(); + } } @Override @@ -343,4 +366,15 @@ private String formatLogLine(int processId, String scriptName, String output, Pr return sb.toString(); } + private File getLogsDirectory() { + String pathStr = configurationService.getProperty("dspace.dir") + + File.separator + "log" + File.separator + "processes"; + File logsDir = new File(pathStr); + if (!logsDir.exists()) { + if (!logsDir.mkdirs()) { + throw new RuntimeException("Couldn't create [dspace.dir]/log/processes/ directory."); + } + } + return logsDir; + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java index 670d8e2f35b0..6c018df6d070 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java @@ -856,10 +856,10 @@ public void getProcessOutput() throws Exception { getClient(token).perform(get("/api/system/processes/" + process1.getID() + "/output")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name", - is(process1.getName() + process1.getID() + ".log"))) + is(process1.getID() + "-" + process1.getName() + ".log"))) .andExpect(jsonPath("$.type", is("bitstream"))) .andExpect(jsonPath("$.metadata['dc.title'][0].value", - is(process1.getName() + process1.getID() + ".log"))) + is(process1.getID() + "-" + process1.getName() + ".log"))) .andExpect(jsonPath("$.metadata['dspace.process.filetype'][0].value", is("script_output"))); @@ -869,10 +869,10 @@ public void getProcessOutput() throws Exception { .perform(get("/api/system/processes/" + process1.getID() + "/output")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name", - is(process1.getName() + process1.getID() + ".log"))) + is(process1.getID() + "-" + process1.getName() + ".log"))) .andExpect(jsonPath("$.type", is("bitstream"))) .andExpect(jsonPath("$.metadata['dc.title'][0].value", - is(process1.getName() + process1.getID() + ".log"))) + is(process1.getID() + "-" + process1.getName() + ".log"))) .andExpect(jsonPath("$.metadata['dspace.process.filetype'][0].value", is("script_output"))); From 1ba7ca3bdda705d26d7dbccf33aa19933e810bde Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 25 Jul 2024 09:33:50 +0200 Subject: [PATCH 324/479] 116609: Add tomcat shutdown line to process log (cherry picked from commit 156ad471b575dfc1c2b643ec4b2f677d6f89a314) --- .../src/main/java/org/dspace/scripts/ProcessServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index f48fd02a920a..c34705a64d5d 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -88,6 +88,9 @@ public void afterPropertiesSet() throws Exception { log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", process.getID()); fail(context, process); // But still attach its log to the process. + appendLog(process.getID(), process.getName(), + "Process did not complete before tomcat shutdown.", + ProcessLogLevel.ERROR); createLogBitstream(context, process); } From 3f836ae7a58a635163ca6a3a0a62cf9c514700ad Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 25 Jul 2024 14:13:16 +0200 Subject: [PATCH 325/479] 116687: Never handle exception with null message (cherry picked from commit bdf7069cb752782a4f8df62885c9ae6116be9dde) --- .../rest/scripts/handler/impl/RestDSpaceRunnableHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java index 596ab4429093..ee67baa8ab38 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java @@ -130,7 +130,7 @@ public void handleCompletion() { @Override public void handleException(Exception e) { - handleException(null, e); + handleException(e.getMessage(), e); } @Override From 8bc100375af69d02ea4ead19561f80effbf6362e Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Mon, 29 Jul 2024 13:48:32 +0200 Subject: [PATCH 326/479] 116609: Add try catch to init method in ProcessServiceImpl (cherry picked from commit 070fe689d70d8dbfcde63899d377f2524f38d6db) --- .../dspace/scripts/ProcessServiceImpl.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index c34705a64d5d..ab5147221cfc 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -77,24 +77,29 @@ public class ProcessServiceImpl implements ProcessService, InitializingBean { @Override public void afterPropertiesSet() throws Exception { - Context context = new Context(); - - // Processes that were running or scheduled when tomcat crashed, should be cleaned up during startup. - List processesToBeFailed = findByStatusAndCreationTimeOlderThan( - context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); - for (Process process : processesToBeFailed) { - context.setCurrentUser(process.getEPerson()); - // Fail the process. - log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", process.getID()); - fail(context, process); - // But still attach its log to the process. - appendLog(process.getID(), process.getName(), - "Process did not complete before tomcat shutdown.", - ProcessLogLevel.ERROR); - createLogBitstream(context, process); - } + try { + Context context = new Context(); + + // Processes that were running or scheduled when tomcat crashed, should be cleaned up during startup. + List processesToBeFailed = findByStatusAndCreationTimeOlderThan( + context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); + for (Process process : processesToBeFailed) { + context.setCurrentUser(process.getEPerson()); + // Fail the process. + log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", + process.getID()); + fail(context, process); + // But still attach its log to the process. + appendLog(process.getID(), process.getName(), + "Process did not complete before tomcat shutdown.", + ProcessLogLevel.ERROR); + createLogBitstream(context, process); + } - context.complete(); + context.complete(); + } catch (Exception e) { + log.error("Unable to clean up Processes: ", e); + } } @Override From f8bf278f06af860fe47c92947f15f9be7a68326a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 12:07:18 -0600 Subject: [PATCH 327/479] Ensure login occurs *before* setup-buildx, as some buildx commands appear to be unauthenticated. --- .github/workflows/reusable-docker-build.yml | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 83dfd74b9a1a..d5964454a538 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -105,29 +105,29 @@ jobs: - name: Checkout codebase uses: actions/checkout@v4 - # https://github.com/docker/setup-buildx-action - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 + # 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: ${{ ! matrix.isPr }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures uses: docker/setup-qemu-action@v3 + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + #------------------------------------------------------------ # Build & deploy steps for new commits to a branch (non-PRs) # # These steps build the images, push to DockerHub, and # (if necessary) redeploy demo/sandbox sites. #------------------------------------------------------------ - # 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: ${{ ! matrix.isPr }} - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - # https://github.com/docker/metadata-action # Get Metadata for docker_build_deps step below - name: Sync metadata (tags, labels) from GitHub to Docker for image @@ -257,6 +257,12 @@ jobs: pattern: digests-${{ inputs.build_id }}-* merge-multiple: true + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -268,12 +274,6 @@ jobs: tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - - name: Create manifest list from digests and push working-directory: /tmp/digests run: | From 8d34f06396328c888ff3695230a92ceb9d181eab Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 5 Dec 2024 10:39:34 -0600 Subject: [PATCH 328/479] Update Actions to always deploy the locally created image. non-PRs do not need to redownload images from DockerHub. --- .github/workflows/docker.yml | 24 ++++++++++----------- .github/workflows/reusable-docker-build.yml | 21 ++++++++++++++---- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index daa940f215a1..080ffc1c080b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -149,9 +149,9 @@ jobs: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} - ######################################################################## - # Test Deployment via Docker to ensure images are working properly - ######################################################################## + ################################################################################# + # Test Deployment via Docker to ensure newly built images are working properly + ################################################################################# docker-deploy: # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' if: github.repository == 'dspace/dspace' @@ -171,25 +171,23 @@ jobs: swordv2__D__server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to - # all PR-built docker images in reusabe-docker-build.yml + # all PR-built docker images in reusable-docker-build.yml DSPACE_VER: ${{ (github.event_name == 'pull_request' && 'pr-testing') || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase uses: actions/checkout@v4 - # For PRs, download Docker image artifacts (built by reusable-docker-build.yml for all PRs) - - name: Download Docker image artifacts (for PRs) - if: github.event_name == 'pull_request' + # Download Docker image artifacts (which were just built by reusable-docker-build.yml) + - name: Download Docker image artifacts uses: actions/download-artifact@v4 with: - # Download all Docker images (TAR files) into the /tmp/docker directory - pattern: docker-image-* + # Download all amd64 Docker images (TAR files) into the /tmp/docker directory + pattern: docker-image-*-linux-amd64 path: /tmp/docker merge-multiple: true - # For PRs, load each of the images into Docker by calling "docker image load" for each. - # This ensures we are using the images built from this PR & not the prior versions on DockerHub - - name: Load all downloaded Docker images (for PRs) - if: github.event_name == 'pull_request' + # Load each of the images into Docker by calling "docker image load" for each. + # This ensures we are using the images just built & not any prior versions on DockerHub + - name: Load all downloaded Docker images run: | find /tmp/docker -type f -name "*.tar" -exec docker image load --input "{}" \; docker image ls -a diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index d5964454a538..e6f24bb64f40 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -130,7 +130,7 @@ jobs: #------------------------------------------------------------ # https://github.com/docker/metadata-action # Get Metadata for docker_build_deps step below - - name: Sync metadata (tags, labels) from GitHub to Docker for image + - name: Extract metadata (tags, labels) from GitHub for Docker image if: ${{ ! matrix.isPr }} id: meta_build uses: docker/metadata-action@v5 @@ -151,10 +151,23 @@ jobs: context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} - push: true # Use tags / labels provided by 'docker/metadata-action' above tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} + # Export image to both Docker registry & to a local TAR file + outputs: | + registry + type=docker,dest=/tmp/${{ inputs.build_id }}.tar + + # Upload the local docker image (in TAR file) to a build Artifact + - name: Upload local image to artifact + if: ${{ ! matrix.isPr }} + uses: actions/upload-artifact@v4 + with: + name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} + path: /tmp/${{ inputs.build_id }}.tar + if-no-files-found: error + retention-days: 1 # Export the digest of Docker build locally (for non PRs only) - name: Export Docker build digest @@ -204,7 +217,7 @@ jobs: # brand-new images for automated testing. #-------------------------------------------------------------- # Get Metadata for docker_build_deps step below - - name: Create metadata (tags, labels) for local Docker image + - name: Extract metadata (tags, labels) for local Docker image if: matrix.isPr id: meta_build_pr uses: docker/metadata-action@v5 @@ -233,7 +246,7 @@ jobs: if: matrix.isPr uses: actions/upload-artifact@v4 with: - name: docker-image-${{ inputs.build_id }} + name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} path: /tmp/${{ inputs.build_id }}.tar if-no-files-found: error retention-days: 1 From 1f5defe643bdcf27cca1ea51a00d3a78b937124b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 5 Dec 2024 11:24:31 -0600 Subject: [PATCH 329/479] Ensure non-PRs run image build twice. First for a DockerHub image, then to export a local tarball. Since these are different image types they cannot be combined --- .github/workflows/reusable-docker-build.yml | 41 ++++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index e6f24bb64f40..b8f8d0f91718 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -151,23 +151,10 @@ jobs: context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} + push: true # Use tags / labels provided by 'docker/metadata-action' above tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} - # Export image to both Docker registry & to a local TAR file - outputs: | - registry - type=docker,dest=/tmp/${{ inputs.build_id }}.tar - - # Upload the local docker image (in TAR file) to a build Artifact - - name: Upload local image to artifact - if: ${{ ! matrix.isPr }} - uses: actions/upload-artifact@v4 - with: - name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} - path: /tmp/${{ inputs.build_id }}.tar - if-no-files-found: error - retention-days: 1 # Export the digest of Docker build locally (for non PRs only) - name: Export Docker build digest @@ -187,6 +174,32 @@ jobs: if-no-files-found: error retention-days: 1 + # Build local image (again) and store in a TAR file in /tmp directory + # NOTE: This cannot be combined with push to DockerHub registry above as it's a different type of output. + - name: Build and push image to local image + if: ${{ ! matrix.isPr }} + uses: docker/build-push-action@v5 + with: + build-contexts: | + ${{ inputs.dockerfile_additional_contexts }} + context: ${{ inputs.dockerfile_context }} + file: ${{ inputs.dockerfile_path }} + platforms: ${{ matrix.arch }} + tags: ${{ steps.meta_build.outputs.tags }} + labels: ${{ steps.meta_build.outputs.labels }} + # Export image to a local TAR file + outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar + + # Upload the local docker image (in TAR file) to a build Artifact + - name: Upload local image to artifact + if: ${{ ! matrix.isPr }} + uses: actions/upload-artifact@v4 + with: + name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} + path: /tmp/${{ inputs.build_id }}.tar + if-no-files-found: error + retention-days: 1 + # If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret, # Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch. - name: Redeploy sandbox.dspace.org (based on main branch) From 0fc0f181fd8b49c1b6e278b4bf8aa5b7f8711332 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 6 Dec 2024 11:26:59 -0600 Subject: [PATCH 330/479] Enable caching of Docker builds using GitHub Actions cache --- .github/workflows/reusable-docker-build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index b8f8d0f91718..92dd08623c05 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -155,6 +155,10 @@ jobs: # Use tags / labels provided by 'docker/metadata-action' above tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} + # Use GitHub cache to load cached Docker images and cache the results of this build + # This decreases the number of images we need to fetch from DockerHub + cache-from: type=gha + cache-to: type=gha,mode=max # Export the digest of Docker build locally (for non PRs only) - name: Export Docker build digest @@ -240,6 +244,7 @@ jobs: # for testing in docker.yml tags: pr-testing flavor: ${{ env.TAGS_FLAVOR }} + # Build local image and stores in a TAR file in /tmp directory - name: Build and push image to local image if: matrix.isPr @@ -252,8 +257,13 @@ jobs: platforms: ${{ matrix.arch }} tags: ${{ steps.meta_build_pr.outputs.tags }} labels: ${{ steps.meta_build_pr.outputs.labels }} + # Use GitHub cache to load cached Docker images and cache the results of this build + # This decreases the number of images we need to fetch from DockerHub + cache-from: type=gha + cache-to: type=gha,mode=max # Export image to a local TAR file outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar + # Upload the local docker image (in TAR file) to a build Artifact - name: Upload local image to artifact if: matrix.isPr From 46c169fb7947acfc559ef07f67d0672880da7d0e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 6 Dec 2024 12:51:07 -0600 Subject: [PATCH 331/479] Refactor to use the same local TAR image build for PRs and non-PRs. These TAR images act as a cache for our current build. --- .github/workflows/docker.yml | 4 -- .github/workflows/reusable-docker-build.yml | 76 ++++++--------------- 2 files changed, 20 insertions(+), 60 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 080ffc1c080b..6195aadb4eb3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -169,10 +169,6 @@ jobs: signposting__P__enabled: true sword__D__server__P__enabled: true swordv2__D__server__P__enabled: true - # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the - # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to - # all PR-built docker images in reusable-docker-build.yml - DSPACE_VER: ${{ (github.event_name == 'pull_request' && 'pr-testing') || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 92dd08623c05..3c497410a274 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -122,16 +122,9 @@ jobs: - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 - #------------------------------------------------------------ - # Build & deploy steps for new commits to a branch (non-PRs) - # - # These steps build the images, push to DockerHub, and - # (if necessary) redeploy demo/sandbox sites. - #------------------------------------------------------------ # https://github.com/docker/metadata-action - # Get Metadata for docker_build_deps step below + # Extract metadata used for Docker images in all build steps below - name: Extract metadata (tags, labels) from GitHub for Docker image - if: ${{ ! matrix.isPr }} id: meta_build uses: docker/metadata-action@v5 with: @@ -139,6 +132,12 @@ jobs: tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} + #------------------------------------------------------------ + # Build & deploy steps for new commits to a branch (non-PRs) + # + # These steps build the images, push to DockerHub, and + # (if necessary) redeploy demo/sandbox sites. + #------------------------------------------------------------ # https://github.com/docker/build-push-action - name: Build and push image to DockerHub # Only build & push if not a PR @@ -178,32 +177,6 @@ jobs: if-no-files-found: error retention-days: 1 - # Build local image (again) and store in a TAR file in /tmp directory - # NOTE: This cannot be combined with push to DockerHub registry above as it's a different type of output. - - name: Build and push image to local image - if: ${{ ! matrix.isPr }} - uses: docker/build-push-action@v5 - with: - build-contexts: | - ${{ inputs.dockerfile_additional_contexts }} - context: ${{ inputs.dockerfile_context }} - file: ${{ inputs.dockerfile_path }} - platforms: ${{ matrix.arch }} - tags: ${{ steps.meta_build.outputs.tags }} - labels: ${{ steps.meta_build.outputs.labels }} - # Export image to a local TAR file - outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar - - # Upload the local docker image (in TAR file) to a build Artifact - - name: Upload local image to artifact - if: ${{ ! matrix.isPr }} - uses: actions/upload-artifact@v4 - with: - name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} - path: /tmp/${{ inputs.build_id }}.tar - if-no-files-found: error - retention-days: 1 - # If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret, # Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch. - name: Redeploy sandbox.dspace.org (based on main branch) @@ -227,27 +200,19 @@ jobs: curl -X POST $REDEPLOY_DEMO_URL #------------------------------------------------------------- - # Build steps for PRs only + # Shared Build steps. + # These are used for PRs as well as new commits to a branch (non-PRs) # - # These steps build the images and store as a build artifact. - # These artifacts can then be used by later jobs to run the - # brand-new images for automated testing. + # These steps build the images and cache/store as a build artifact. + # These artifacts can then be used by later jobs to install the + # brand-new images for automated testing. For non-PRs, this cache is + # also used to avoid pulling the images we just built from DockerHub. #-------------------------------------------------------------- - # Get Metadata for docker_build_deps step below - - name: Extract metadata (tags, labels) for local Docker image - if: matrix.isPr - id: meta_build_pr - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - # Hardcode to use custom "pr-testing" tag because that will allow us to spin up this PR - # for testing in docker.yml - tags: pr-testing - flavor: ${{ env.TAGS_FLAVOR }} - # Build local image and stores in a TAR file in /tmp directory - - name: Build and push image to local image - if: matrix.isPr + # Build local image (again) and store in a TAR file in /tmp directory + # NOTE: This build is run for both PRs and non-PRs as it's used to "cache" our built images as artifacts. + # NOTE #2: This cannot be combined with push to DockerHub registry above as it's a different type of output. + - name: Build and push image to local TAR file uses: docker/build-push-action@v5 with: build-contexts: | @@ -255,8 +220,8 @@ jobs: context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} - tags: ${{ steps.meta_build_pr.outputs.tags }} - labels: ${{ steps.meta_build_pr.outputs.labels }} + tags: ${{ steps.meta_build.outputs.tags }} + labels: ${{ steps.meta_build.outputs.labels }} # Use GitHub cache to load cached Docker images and cache the results of this build # This decreases the number of images we need to fetch from DockerHub cache-from: type=gha @@ -265,8 +230,7 @@ jobs: outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar # Upload the local docker image (in TAR file) to a build Artifact - - name: Upload local image to artifact - if: matrix.isPr + - name: Upload local image TAR to artifact uses: actions/upload-artifact@v4 with: name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} From 020fec96d8206572b2517dcd9dc67fa920c10b23 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 6 Dec 2024 14:12:28 -0600 Subject: [PATCH 332/479] Ensure PRs are tagging their images with same tag as the base branch the PR was created against --- .github/workflows/reusable-docker-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 3c497410a274..723c1c77256f 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -54,10 +54,13 @@ env: # 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. + # For a pull request, use the name of the base branch that the PR was created against or "latest" (for main). + # e.g. PR against 'main' will use "latest". a PR against 'dspace-7_x' will use 'dspace-7_x'. IMAGE_TAGS: | type=raw,value=latest,enable=${{ github.ref_name == github.event.repository.default_branch }} type=ref,event=branch,enable=${{ github.ref_name != github.event.repository.default_branch }} type=ref,event=tag + type=raw,value=${{ (github.event.pull_request.base.ref == github.event.repository.default_branch && 'latest') || github.event.pull_request.base.ref }},enable=${{ github.event_name == 'pull_request' }} # Define default tag "flavor" for docker/metadata-action per # https://github.com/docker/metadata-action#flavor-input # We manage the 'latest' tag ourselves to the 'main' branch (see settings above) From 3c0e7158a26b13ae01de500298e94d65e3601c97 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 6 Dec 2024 15:01:08 -0600 Subject: [PATCH 333/479] Ensure each image has a separate cache. This allows later builds of that same image to inherit that cache. --- .github/workflows/reusable-docker-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 723c1c77256f..d9f4c5342b40 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -159,8 +159,8 @@ jobs: labels: ${{ steps.meta_build.outputs.labels }} # Use GitHub cache to load cached Docker images and cache the results of this build # This decreases the number of images we need to fetch from DockerHub - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=gha,scope=${{ inputs.build_id }} + cache-to: type=gha,scope=${{ inputs.build_id }},mode=max # Export the digest of Docker build locally (for non PRs only) - name: Export Docker build digest @@ -227,8 +227,8 @@ jobs: labels: ${{ steps.meta_build.outputs.labels }} # Use GitHub cache to load cached Docker images and cache the results of this build # This decreases the number of images we need to fetch from DockerHub - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=gha,scope=${{ inputs.build_id }} + cache-to: type=gha,scope=${{ inputs.build_id }},mode=max # Export image to a local TAR file outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar From 78855cdbbab98038f61e2bd94922e4fa3db9ad18 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 9 Dec 2024 10:18:32 -0600 Subject: [PATCH 334/479] Ensure we use the main Docker image, and not the "-test" image. --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6195aadb4eb3..45d6fd6b0277 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -169,6 +169,9 @@ jobs: signposting__P__enabled: true sword__D__server__P__enabled: true swordv2__D__server__P__enabled: true + # If this is a PR, used the base branch name. If on main branch, use the "latest" tag. Otherwise, use branch name. + # NOTE: DSPACE_VER is used because our docker compose scripts default to using the "-test" image. + DSPACE_VER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref) || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From b8c88ef284f95e2a8cb8873f00dcc227fcf58e74 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 9 Dec 2024 12:06:14 -0600 Subject: [PATCH 335/479] Ensure PRs against main also use "latest" tag --- .github/workflows/docker.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 45d6fd6b0277..ebbd8cc50984 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -169,9 +169,12 @@ jobs: signposting__P__enabled: true sword__D__server__P__enabled: true swordv2__D__server__P__enabled: true - # If this is a PR, used the base branch name. If on main branch, use the "latest" tag. Otherwise, use branch name. + # If this is a PR against main (default branch), use "latest". + # Else if this is a PR against a different branch, used the base branch name. + # Else if this is a commit on main (default branch), use the "latest" tag. + # Else, just use the branch name. # NOTE: DSPACE_VER is used because our docker compose scripts default to using the "-test" image. - DSPACE_VER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref) || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} + DSPACE_VER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref == github.event.repository.default_branch && 'latest') || (github.event_name == 'pull_request' && github.event.pull_request.base.ref) || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From c017a662e0642d32ae59017451ea8994de48ab44 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 11 Dec 2024 21:26:42 +0100 Subject: [PATCH 336/479] 121971: #9669 - Remove unsupported OpenAIRE date types --- .../oai/metadataFormats/oai_openaire.xsl | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index 3a1d75eb56c6..b6abd9daa056 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -1039,6 +1039,8 @@ + + @@ -1050,24 +1052,6 @@ Available - - Collected - - - Copyrighted - - - Created - - - Submitted - - - Updated - - - Valid - From 04953b94d919bf5e0135aa3311b70767cba20160 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 11 Dec 2024 21:39:08 +0100 Subject: [PATCH 337/479] 121971: #9715 - Only dc.date.issued should have date types Accepted and Issued --- dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index b6abd9daa056..f92367646d1f 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -731,7 +731,7 @@ From 31c79500ceec1c8e36228195b802a289ac9c30c9 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 11 Dec 2024 21:49:37 +0100 Subject: [PATCH 338/479] 121971: #9716 - Only dc.date.embargo should have date type Available --- dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index f92367646d1f..7b141e48b3a0 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -1049,7 +1049,9 @@ - + + + Available From 52e5b35c0653afc17ebd47ce1272549ddf205469 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 12 Dec 2024 09:16:21 +0100 Subject: [PATCH 339/479] 121971: #9664 - Make cclicense step required in openaire submission form --- dspace/config/item-submission.xml | 4 ++++ dspace/config/submission-forms.xml | 23 ----------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index a6cd49bdf1e8..866426afd61d 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -379,6 +379,10 @@ + + + + diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index 39a4778356c0..13c8fc208f6e 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -1133,29 +1133,6 @@ - - - dc - rights - false - - dropdown - Select the access type of the resource. - - - - - - oaire - license - condition - false - - dropdown - Select the license of the resource. - - - dc From e5401685944b6135795462748c791ed7ba7bab7d Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 12 Dec 2024 09:18:40 +0100 Subject: [PATCH 340/479] 121971: #9867 - Remove objectType attribute from openaire crosswalk --- .../crosswalks/oai/metadataFormats/oai_openaire.xsl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index 7b141e48b3a0..6c6c369071a6 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -888,13 +888,13 @@ - + + fulltext - + other - + --> From a6d2c4897b46062cca59add6946eb7a86c648585 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 4 Dec 2024 13:44:44 -0600 Subject: [PATCH 341/479] Add Docker registry to all scripts. Allow it to be configurable for DSpace images (only). Other minor Dockerfile cleanup --- Dockerfile | 17 +++++++++++------ Dockerfile.cli | 17 +++++++++++------ Dockerfile.dependencies | 2 +- Dockerfile.test | 17 +++++++++++------ docker-compose-cli.yml | 2 +- docker-compose.yml | 6 +++--- .../dspace-postgres-pgcrypto-curl/Dockerfile | 2 +- .../docker/dspace-postgres-pgcrypto/Dockerfile | 2 +- .../main/docker/dspace-shibboleth/Dockerfile | 2 +- dspace/src/main/docker/dspace-solr/Dockerfile | 2 +- 10 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index adc5d6125f0a..ff35f2a2e3a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,14 @@ # 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 +# The Docker version tag to build from +ARG DSPACE_VERSION=dspace-7_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -28,15 +33,15 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src WORKDIR /dspace-src # Create the initial install deployment using ANT -ENV ANT_VERSION 1.10.13 -ENV ANT_HOME /tmp/ant-$ANT_VERSION -ENV PATH $ANT_HOME/bin:$PATH +ENV ANT_VERSION=1.10.13 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -50,7 +55,7 @@ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Run tomcat # Create a new tomcat image that does not retain the the build directory contents -FROM tomcat:9-jdk${JDK_VERSION} +FROM docker.io/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' container to /dspace in this container diff --git a/Dockerfile.cli b/Dockerfile.cli index 8a69b32e2dc6..be03e8922b2c 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -6,9 +6,14 @@ # 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 +# The Docker version tag to build from +ARG DSPACE_VERSION=dspace-7_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -24,15 +29,15 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src WORKDIR /dspace-src # Create the initial install deployment using ANT -ENV ANT_VERSION 1.10.13 -ENV ANT_HOME /tmp/ant-$ANT_VERSION -ENV PATH $ANT_HOME/bin:$PATH +ENV ANT_VERSION=1.10.13 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -45,7 +50,7 @@ RUN mkdir $ANT_HOME && \ RUN ant init_installation update_configs update_code # Step 3 - Run jdk -FROM eclipse-temurin:${JDK_VERSION} +FROM docker.io/eclipse-temurin:${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 123206ea5887..794dfa9a66a8 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build +FROM docker.io/maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory diff --git a/Dockerfile.test b/Dockerfile.test index d88699ca52ab..08b6b3018b80 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -8,9 +8,14 @@ # 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 +# The Docker version tag to build from +ARG DSPACE_VERSION=dspace-7_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -27,15 +32,15 @@ RUN mvn --no-transfer-progress package -Pdspace-rest && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src WORKDIR /dspace-src # Create the initial install deployment using ANT -ENV ANT_VERSION 1.10.12 -ENV ANT_HOME /tmp/ant-$ANT_VERSION -ENV PATH $ANT_HOME/bin:$PATH +ENV ANT_VERSION=1.10.12 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -49,7 +54,7 @@ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Run tomcat # Create a new tomcat image that does not retain the the build directory contents -FROM tomcat:9-jdk${JDK_VERSION} +FROM docker.io/tomcat:9-jdk${JDK_VERSION} ENV DSPACE_INSTALL=/dspace ENV TOMCAT_INSTALL=/usr/local/tomcat # Copy the /dspace directory from 'ant_build' containger to /dspace in this container diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index d6a194617e02..ce7676bdc6c3 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -6,7 +6,7 @@ networks: external: true services: dspace-cli: - image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" container_name: dspace-cli build: context: . diff --git a/docker-compose.yml b/docker-compose.yml index 23fce37db245..a16c3677503c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: # 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:-dspace-7_x-test}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-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_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-7_x}" 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_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}" build: context: ./dspace/src/main/docker/dspace-solr/ # Provide path to Solr configs necessary to build Docker image diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index aabf87df3eda..791242f5bdc7 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -13,7 +13,7 @@ ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace -FROM postgres:${POSTGRES_VERSION} +FROM docker.io/postgres:${POSTGRES_VERSION} ENV POSTGRES_DB dspace ENV POSTGRES_USER dspace diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 2298cd4e76ea..116d2dbcda31 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -13,7 +13,7 @@ ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace -FROM postgres:${POSTGRES_VERSION} +FROM docker.io/postgres:${POSTGRES_VERSION} ENV POSTGRES_DB dspace ENV POSTGRES_USER dspace diff --git a/dspace/src/main/docker/dspace-shibboleth/Dockerfile b/dspace/src/main/docker/dspace-shibboleth/Dockerfile index 79f2921bd7d6..25fb510e3d39 100644 --- a/dspace/src/main/docker/dspace-shibboleth/Dockerfile +++ b/dspace/src/main/docker/dspace-shibboleth/Dockerfile @@ -10,7 +10,7 @@ # Build from Ubuntu as it has easy Apache tooling (e.g. a2enmod script is debian only). # Apache & mod_shib are required for DSpace to act as an SP # See also https://wiki.lyrasis.org/display/DSDOC7x/Authentication+Plugins#AuthenticationPlugins-ShibbolethAuthentication -FROM ubuntu:20.04 +FROM docker.io/ubuntu:20.04 # Apache ENVs (default values) ENV APACHE_RUN_USER www-data diff --git a/dspace/src/main/docker/dspace-solr/Dockerfile b/dspace/src/main/docker/dspace-solr/Dockerfile index eb8e93493fa8..0c011d43105f 100644 --- a/dspace/src/main/docker/dspace-solr/Dockerfile +++ b/dspace/src/main/docker/dspace-solr/Dockerfile @@ -12,7 +12,7 @@ ARG SOLR_VERSION=8.11 -FROM solr:${SOLR_VERSION}-slim +FROM docker.io/solr:${SOLR_VERSION}-slim ENV AUTHORITY_CONFIGSET_PATH=/opt/solr/server/solr/configsets/authority/conf \ OAI_CONFIGSET_PATH=/opt/solr/server/solr/configsets/oai/conf \ From 2ee328ff18aa973dd2ed82abd062d8a923cbddf5 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 4 Dec 2024 13:45:33 -0600 Subject: [PATCH 342/479] Minor Dockerfile cleanup. Use new syntax for ENV variables --- .../docker/dspace-postgres-pgcrypto-curl/Dockerfile | 6 +++--- .../main/docker/dspace-postgres-pgcrypto/Dockerfile | 6 +++--- dspace/src/main/docker/dspace-shibboleth/Dockerfile | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index 791242f5bdc7..f314fdb79e80 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -15,9 +15,9 @@ ARG POSTGRES_PASSWORD=dspace FROM docker.io/postgres:${POSTGRES_VERSION} -ENV POSTGRES_DB dspace -ENV POSTGRES_USER dspace -ENV POSTGRES_PASSWORD ${POSTGRES_PASSWORD} +ENV POSTGRES_DB=dspace +ENV POSTGRES_USER=dspace +ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Install curl which is necessary to load SQL file RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 116d2dbcda31..4f639f361024 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -15,9 +15,9 @@ ARG POSTGRES_PASSWORD=dspace FROM docker.io/postgres:${POSTGRES_VERSION} -ENV POSTGRES_DB dspace -ENV POSTGRES_USER dspace -ENV POSTGRES_PASSWORD ${POSTGRES_PASSWORD} +ENV POSTGRES_DB=dspace +ENV POSTGRES_USER=dspace +ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Copy over script which will initialize database and install pgcrypto extension COPY install-pgcrypto.sh /docker-entrypoint-initdb.d/ diff --git a/dspace/src/main/docker/dspace-shibboleth/Dockerfile b/dspace/src/main/docker/dspace-shibboleth/Dockerfile index 25fb510e3d39..15a436d7b6f7 100644 --- a/dspace/src/main/docker/dspace-shibboleth/Dockerfile +++ b/dspace/src/main/docker/dspace-shibboleth/Dockerfile @@ -13,12 +13,12 @@ FROM docker.io/ubuntu:20.04 # Apache ENVs (default values) -ENV APACHE_RUN_USER www-data -ENV APACHE_RUN_GROUP www-data -ENV APACHE_LOCK_DIR /var/lock/apache2 -ENV APACHE_LOG_DIR /var/log/apache2 -ENV APACHE_PID_FILE /var/run/apache2/apache2.pid -ENV APACHE_SERVER_NAME localhost +ENV APACHE_RUN_USER=www-data +ENV APACHE_RUN_GROUP=www-data +ENV APACHE_LOCK_DIR=/var/lock/apache2 +ENV APACHE_LOG_DIR=/var/log/apache2 +ENV APACHE_PID_FILE=/var/run/apache2/apache2.pid +ENV APACHE_SERVER_NAME=localhost # Ensure Apache2, mod_shib & shibboleth daemon are installed. # Also install ssl-cert to provide a local SSL cert for use with Apache From 2a6c60e1392cf643b4ae4c8512ce92c7aba4b2eb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 12 Dec 2024 16:50:02 -0600 Subject: [PATCH 343/479] Refactor Docker build process to use ghcr.io for build, and then copy to docker.io once build completes --- .github/workflows/docker.yml | 1 + .github/workflows/reusable-docker-build.yml | 189 ++++++++++++++------ 2 files changed, 131 insertions(+), 59 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ebbd8cc50984..fceea6403d2a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,6 +15,7 @@ on: permissions: contents: read # to fetch code (actions/checkout) + packages: write # to write images to GitHub Container Registry (GHCR) jobs: #################################################### diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index d9f4c5342b40..36546af3a5da 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -74,7 +74,11 @@ env: # Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org # (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch) DEPLOY_DEMO_BRANCH: 'dspace-8_x' + DEPLOY_SANDBOX_BRANCH: 'main' DEPLOY_ARCH: 'linux/amd64' + # Registry used during building of Docker images. (All images are later copied to docker.io registry) + # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. + DOCKER_BUILD_REGISTRY: ghcr.io jobs: docker-build: @@ -99,6 +103,7 @@ jobs: # This step converts the slashes in the "arch" matrix values above into dashes & saves to env.ARCH_NAME # E.g. "linux/amd64" becomes "linux-amd64" # This is necessary because all upload artifacts CANNOT have special chars (like slashes) + # NOTE: The regex-like syntax below is Bash Parameter Substitution - name: Prepare run: | platform=${{ matrix.arch }} @@ -109,13 +114,14 @@ jobs: uses: actions/checkout@v4 # 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 + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} + # Only login if not a PR, as PRs only trigger a Docker build and not a push if: ${{ ! matrix.isPr }} uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures @@ -131,19 +137,20 @@ jobs: id: meta_build uses: docker/metadata-action@v5 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }} tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - #------------------------------------------------------------ - # Build & deploy steps for new commits to a branch (non-PRs) + #-------------------------------------------------------------------- + # First, for all branch commits (non-PRs) we build the image & upload + # to GitHub Container Registry (GHCR). After uploading the image + # to GHCR, we store the image digest in an artifact, so we can + # create a merged manifest later (see 'docker-build_manifest' job). # - # These steps build the images, push to DockerHub, and - # (if necessary) redeploy demo/sandbox sites. - #------------------------------------------------------------ + # NOTE: We use GHCR in order to avoid aggressive rate limits at DockerHub. + #-------------------------------------------------------------------- # https://github.com/docker/build-push-action - - name: Build and push image to DockerHub - # Only build & push if not a PR + - name: Build and push image to ${{ env.DOCKER_BUILD_REGISTRY }} if: ${{ ! matrix.isPr }} id: docker_build uses: docker/build-push-action@v5 @@ -152,6 +159,9 @@ jobs: ${{ inputs.dockerfile_additional_contexts }} context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} + # Tell DSpace's Docker files to use the build registry instead of DockerHub + build-args: + DOCKER_REGISTRY=${{ env.DOCKER_BUILD_REGISTRY }} platforms: ${{ matrix.arch }} push: true # Use tags / labels provided by 'docker/metadata-action' above @@ -162,7 +172,7 @@ jobs: cache-from: type=gha,scope=${{ inputs.build_id }} cache-to: type=gha,scope=${{ inputs.build_id }},mode=max - # Export the digest of Docker build locally (for non PRs only) + # Export the digest of Docker build locally - name: Export Docker build digest if: ${{ ! matrix.isPr }} run: | @@ -170,7 +180,8 @@ jobs: digest="${{ steps.docker_build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - # Upload digest to an artifact, so that it can be used in manifest below + # Upload digest to an artifact, so that it can be used in combined manifest below + # (The purpose of the combined manifest is to list both amd64 and arm64 builds under same tag) - name: Upload Docker build digest to artifact if: ${{ ! matrix.isPr }} uses: actions/upload-artifact@v4 @@ -180,48 +191,31 @@ jobs: if-no-files-found: error retention-days: 1 - # If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret, - # Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch. - - name: Redeploy sandbox.dspace.org (based on main branch) - if: | - !matrix.isPR && - env.REDEPLOY_SANDBOX_URL != '' && - matrix.arch == env.DEPLOY_ARCH && - github.ref_name == github.event.repository.default_branch - run: | - curl -X POST $REDEPLOY_SANDBOX_URL - - # If this build is NOT a PR and passed in a REDEPLOY_DEMO_URL secret, - # Then redeploy https://demo.dspace.org if this build is for our deployment architecture and demo branch. - - name: Redeploy demo.dspace.org (based on maintenance branch) - if: | - !matrix.isPR && - env.REDEPLOY_DEMO_URL != '' && - matrix.arch == env.DEPLOY_ARCH && - github.ref_name == env.DEPLOY_DEMO_BRANCH - run: | - curl -X POST $REDEPLOY_DEMO_URL - - #------------------------------------------------------------- - # Shared Build steps. - # These are used for PRs as well as new commits to a branch (non-PRs) + #------------------------------------------------------------------------------ + # Second, we build the image again in order to store it in a local TAR file. + # This TAR of the image is cached/saved as an artifact, so that it can be used + # by later jobs to install the brand-new images for automated testing. + # This TAR build is performed BOTH for PRs and for branch commits (non-PRs). # - # These steps build the images and cache/store as a build artifact. - # These artifacts can then be used by later jobs to install the - # brand-new images for automated testing. For non-PRs, this cache is - # also used to avoid pulling the images we just built from DockerHub. - #-------------------------------------------------------------- - + # (This approach has the advantage of avoiding having to download the newly built + # image from DockerHub or GHCR during automated testing.) + # + # See the 'docker-deploy' job in docker.yml as an example of where this TAR is used. + #------------------------------------------------------------------------------- # Build local image (again) and store in a TAR file in /tmp directory - # NOTE: This build is run for both PRs and non-PRs as it's used to "cache" our built images as artifacts. - # NOTE #2: This cannot be combined with push to DockerHub registry above as it's a different type of output. + # This step is only done for AMD64, as that's the only image we use in our automated testing (at this time). + # NOTE: This step cannot be combined with the build above as it's a different type of output. - name: Build and push image to local TAR file + if: ${{ matrix.arch == 'linux/amd64'}} uses: docker/build-push-action@v5 with: build-contexts: | ${{ inputs.dockerfile_additional_contexts }} context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} + # Tell DSpace's Docker files to use the build registry instead of DockerHub + build-args: + DOCKER_REGISTRY=${{ env.DOCKER_BUILD_REGISTRY }} platforms: ${{ matrix.arch }} tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} @@ -233,7 +227,9 @@ jobs: outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar # Upload the local docker image (in TAR file) to a build Artifact + # This step is only done for AMD64, as that's the only image we use in our automated testing (at this time). - name: Upload local image TAR to artifact + if: ${{ matrix.arch == 'linux/amd64'}} uses: actions/upload-artifact@v4 with: name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} @@ -241,10 +237,12 @@ jobs: if-no-files-found: error retention-days: 1 - # Merge Docker digests (from various architectures) into a manifest. - # This runs after all Docker builds complete above, and it tells hub.docker.com - # that these builds should be all included in the manifest for this tag. - # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) + ########################################################################################## + # Merge Docker digests (from various architectures) into a single manifest. + # This runs after all Docker builds complete above. The purpose is to include all builds + # under a single manifest for this tag. + # (e.g. both linux/amd64 and linux/arm64 should be listed under the same tagged Docker image) + ########################################################################################## docker-build_manifest: # Only run if this is NOT a PR if: ${{ github.event_name != 'pull_request' }} @@ -260,11 +258,12 @@ jobs: pattern: digests-${{ inputs.build_id }}-* merge-multiple: true - - name: Login to Docker Hub + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -273,16 +272,88 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }} tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - - name: Create manifest list from digests and push + - name: Create manifest list from digests and push to ${{ env.DOCKER_BUILD_REGISTRY }} working-directory: /tmp/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) + $(printf '${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect manifest in ${{ env.DOCKER_BUILD_REGISTRY }} + run: | + docker buildx imagetools inspect ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} + + ########################################################################################## + # Copy images / manifest to DockerHub. + # This MUST run after *both* images (AMD64 and ARM64) are built and uploaded to GitHub + # Container Registry (GHCR). Attempting to run this in parallel to GHCR builds can result + # in a race condition...i.e. the copy to DockerHub may fail if GHCR image has been updated + # at the moment when the copy occurs. + ########################################################################################## + docker-copy_to_dockerhub: + # Only run if this is NOT a PR + if: ${{ github.event_name != 'pull_request' }} + runs-on: ubuntu-latest + needs: + - docker-build_manifest + + steps: + # 'regctl' is used to more easily copy the image to DockerHub and obtain the digest from DockerHub + # See https://github.com/regclient/regclient/blob/main/docs/regctl.md + - name: Install regctl for Docker registry tools + uses: regclient/actions/regctl-installer@main + with: + release: 'v0.8.0' - - name: Inspect image + # This recreates Docker tags for DockerHub + - name: Add Docker metadata for image + id: meta_dockerhub + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} + + # Login to source registry first, as this is where we are copying *from* + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Login to DockerHub, since this is where we are copying *to* + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + # Copy the image from source to DockerHub + - name: Copy image from ${{ env.DOCKER_BUILD_REGISTRY }} to docker.io + run: | + regctl image copy ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta_dockerhub.outputs.version }} docker.io/${{ env.IMAGE_NAME }}:${{ steps.meta_dockerhub.outputs.version }} + + #-------------------------------------------------------------------- + # Finally, check whether demo.dspace.org or sandbox.dspace.org need + # to be redeployed based on these new DockerHub images. + #-------------------------------------------------------------------- + # If this build is for the branch that Sandbox uses and passed in a REDEPLOY_SANDBOX_URL secret, + # Then redeploy https://sandbox.dspace.org + - name: Redeploy sandbox.dspace.org (based on main branch) + if: | + env.REDEPLOY_SANDBOX_URL != '' && + github.ref_name == env.DEPLOY_SANDBOX_BRANCH + run: | + curl -X POST $REDEPLOY_SANDBOX_URL + # If this build is for the branch that Demo uses and passed in a REDEPLOY_DEMO_URL secret, + # Then redeploy https://demo.dspace.org + - name: Redeploy demo.dspace.org (based on maintenance branch) + if: | + env.REDEPLOY_DEMO_URL != '' && + github.ref_name == env.DEPLOY_DEMO_BRANCH run: | - docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} + curl -X POST $REDEPLOY_DEMO_URL \ No newline at end of file From 296c9a12f489d779c15bf07fd8c38fac6bc6d233 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 16 Dec 2024 12:05:41 -0600 Subject: [PATCH 344/479] PRs must also login to ghcr.io in order to read private images for the build process --- .github/workflows/reusable-docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 36546af3a5da..3b74f250b539 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -114,9 +114,9 @@ jobs: uses: actions/checkout@v4 # https://github.com/docker/login-action + # NOTE: This login occurs for BOTH non-PRs or PRs. PRs *must* also login to access private images from GHCR + # during the build process - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} - # Only login if not a PR, as PRs only trigger a Docker build and not a push - if: ${{ ! matrix.isPr }} uses: docker/login-action@v3 with: registry: ${{ env.DOCKER_BUILD_REGISTRY }} From a27f1ed1757b193cf6f831a0fd27585b655bacc2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 16 Dec 2024 12:28:16 -0600 Subject: [PATCH 345/479] Ensure "docker-deploy" job also uses ghcr.io by default. --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fceea6403d2a..815aec5cf6b4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -176,6 +176,9 @@ jobs: # Else, just use the branch name. # NOTE: DSPACE_VER is used because our docker compose scripts default to using the "-test" image. DSPACE_VER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref == github.event.repository.default_branch && 'latest') || (github.event_name == 'pull_request' && github.event.pull_request.base.ref) || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} + # Docker Registry to use for Docker compose scripts below. + # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. + DOCKER_REGISTRY: ghcr.io steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From 64cb3bda0034d8e3051c31290a3eeea8a560d939 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Wed, 7 Aug 2024 16:34:38 -0300 Subject: [PATCH 346/479] Changes Group2GroupCache computation --- .../org/dspace/eperson/Group2GroupCache.java | 3 +- .../org/dspace/eperson/GroupServiceImpl.java | 72 +++++++++--------- .../eperson/dao/Group2GroupCacheDAO.java | 74 +++++++++++++++++-- .../dao/impl/Group2GroupCacheDAOImpl.java | 34 +++++++++ 4 files changed, 143 insertions(+), 40 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java b/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java index 09bdf34d4cad..58781cd402fa 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java +++ b/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java @@ -14,6 +14,7 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.persistence.UniqueConstraint; import org.hibernate.proxy.HibernateProxyHelper; @@ -23,7 +24,7 @@ * @author kevinvandevelde at atmire.com */ @Entity -@Table(name = "group2groupcache") +@Table(name = "group2groupcache", uniqueConstraints = { @UniqueConstraint(columnNames = {"parent_id", "child_id"}) }) public class Group2GroupCache implements Serializable { @Id diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 3fb20e2f1e6f..b52f17a5c692 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -20,6 +20,7 @@ import java.util.UUID; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -673,15 +674,14 @@ protected boolean isEPersonInGroup(Context context, Group group, EPerson ePerson /** - * Regenerate the group cache AKA the group2groupcache table in the database - - * meant to be called when a group is added or removed from another group + * Returns a set with pairs of parent and child group UUIDs, representing the new cache table rows. * - * @param context The relevant DSpace Context. - * @param flushQueries flushQueries Flush all pending queries + * @param context The relevant DSpace Context. + * @param flushQueries flushQueries Flush all pending queries + * @return Pairs of parent and child group UUID of the new cache. * @throws SQLException An exception that provides information on a database access error or other errors. */ - protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { - + private Set> computeNewCache(Context context, boolean flushQueries) throws SQLException { Map> parents = new HashMap<>(); List> group2groupResults = groupDAO.getGroup2GroupResults(context, flushQueries); @@ -689,19 +689,8 @@ protected void rethinkGroupCache(Context context, boolean flushQueries) throws S UUID parent = group2groupResult.getLeft(); UUID child = group2groupResult.getRight(); - // if parent doesn't have an entry, create one - if (!parents.containsKey(parent)) { - Set children = new HashSet<>(); - - // add child id to the list - children.add(child); - parents.put(parent, children); - } else { - // parent has an entry, now add the child to the parent's record - // of children - Set children = parents.get(parent); - children.add(child); - } + parents.putIfAbsent(parent, new HashSet<>()); + parents.get(parent).add(child); } // now parents is a hash of all of the IDs of groups that are parents @@ -714,27 +703,42 @@ protected void rethinkGroupCache(Context context, boolean flushQueries) throws S parent.getValue().addAll(myChildren); } - // empty out group2groupcache table - group2GroupCacheDAO.deleteAll(context); - - // write out new one + // write out new cache IN MEMORY ONLY and returns it + Set> newCache = new HashSet<>(); for (Map.Entry> parent : parents.entrySet()) { UUID key = parent.getKey(); - for (UUID child : parent.getValue()) { + newCache.add(Pair.of(key, child)); + } + } + return newCache; + } - Group parentGroup = find(context, key); - Group childGroup = find(context, child); + /** + * Regenerate the group cache AKA the group2groupcache table in the database - + * meant to be called when a group is added or removed from another group + * + * @param context The relevant DSpace Context. + * @param flushQueries flushQueries Flush all pending queries + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { + // current cache in the database + var oldCache = group2GroupCacheDAO.getCache(context); - if (parentGroup != null && childGroup != null && group2GroupCacheDAO - .find(context, parentGroup, childGroup) == null) { - Group2GroupCache group2GroupCache = group2GroupCacheDAO.create(context, new Group2GroupCache()); - group2GroupCache.setParent(parentGroup); - group2GroupCache.setChild(childGroup); - group2GroupCacheDAO.save(context, group2GroupCache); - } - } + // correct cache, computed from the Group table + var newCache = computeNewCache(context, flushQueries); + + var toDelete = SetUtils.difference(oldCache, newCache); + var toCreate = SetUtils.difference(newCache, oldCache); + + for (var pair : toDelete ) { + group2GroupCacheDAO.deleteFromCache(context, pair.getLeft(), pair.getRight()); + } + + for (var pair : toCreate ) { + group2GroupCacheDAO.addToCache(context, pair.getLeft(), pair.getRight()); } } diff --git a/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java b/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java index 7db569a59e2b..d41d52c7e618 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java +++ b/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java @@ -9,7 +9,10 @@ import java.sql.SQLException; import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.core.Context; import org.dspace.core.GenericDAO; import org.dspace.eperson.Group; @@ -25,13 +28,74 @@ */ public interface Group2GroupCacheDAO extends GenericDAO { - public List findByParent(Context context, Group group) throws SQLException; + /** + * Returns the current cache table as a set of UUID pairs. + * @param context The relevant DSpace Context. + * @return Set of UUID pairs, where the first element is the parent UUID and the second one is the child UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Set> getCache(Context context) throws SQLException; - public List findByChildren(Context context, Iterable groups) throws SQLException; + /** + * Returns all cache entities that are children of a given parent Group entity. + * @param context The relevant DSpace Context. + * @param group Parent group to perform the search. + * @return List of cached groups that are children of the parent group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + List findByParent(Context context, Group group) throws SQLException; - public Group2GroupCache findByParentAndChild(Context context, Group parent, Group child) throws SQLException; + /** + * Returns all cache entities that are parents of at least one group from a children groups list. + * @param context The relevant DSpace Context. + * @param groups Children groups to perform the search. + * @return List of cached groups that are parents of at least one group from the children groups list. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + List findByChildren(Context context, Iterable groups) throws SQLException; - public Group2GroupCache find(Context context, Group parent, Group child) throws SQLException; + /** + * Returns the cache entity given specific parent and child groups. + * @param context The relevant DSpace Context. + * @param parent Parent group. + * @param child Child gruoup. + * @return Cached group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Group2GroupCache findByParentAndChild(Context context, Group parent, Group child) throws SQLException; - public void deleteAll(Context context) throws SQLException; + /** + * Returns the cache entity given specific parent and child groups. + * @param context The relevant DSpace Context. + * @param parent Parent group. + * @param child Child gruoup. + * @return Cached group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Group2GroupCache find(Context context, Group parent, Group child) throws SQLException; + + /** + * Completely deletes the current cache table. + * @param context The relevant DSpace Context. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void deleteAll(Context context) throws SQLException; + + /** + * Deletes a specific cache row given parent and child groups UUIDs. + * @param context The relevant DSpace Context. + * @param parent Parent group UUID. + * @param child Child group UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void deleteFromCache(Context context, UUID parent, UUID child) throws SQLException; + + /** + * Adds a single row to the cache table given parent and child groups UUIDs. + * @param context The relevant DSpace Context. + * @param parent Parent group UUID. + * @param child Child group UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void addToCache(Context context, UUID parent, UUID child) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java b/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java index 83fb48aaf03d..42ca2bb5d48c 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java @@ -8,14 +8,18 @@ package org.dspace.eperson.dao.impl; import java.sql.SQLException; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.UUID; import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.core.AbstractHibernateDAO; import org.dspace.core.Context; import org.dspace.eperson.Group; @@ -35,6 +39,16 @@ protected Group2GroupCacheDAOImpl() { super(); } + @Override + public Set> getCache(Context context) throws SQLException { + Query query = createQuery( + context, + "SELECT new org.apache.commons.lang3.tuple.ImmutablePair(g.parent.id, g.child.id) FROM Group2GroupCache g" + ); + List> results = query.getResultList(); + return new HashSet>(results); + } + @Override public List findByParent(Context context, Group group) throws SQLException { CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); @@ -90,4 +104,24 @@ public Group2GroupCache find(Context context, Group parent, Group child) throws public void deleteAll(Context context) throws SQLException { createQuery(context, "delete from Group2GroupCache").executeUpdate(); } + + @Override + public void deleteFromCache(Context context, UUID parent, UUID child) throws SQLException { + Query query = getHibernateSession(context).createNativeQuery( + "delete from group2groupcache g WHERE g.parent_id = :parent AND g.child_id = :child" + ); + query.setParameter("parent", parent); + query.setParameter("child", child); + query.executeUpdate(); + } + + @Override + public void addToCache(Context context, UUID parent, UUID child) throws SQLException { + Query query = getHibernateSession(context).createNativeQuery( + "insert into group2groupcache (parent_id, child_id) VALUES (:parent, :child)" + ); + query.setParameter("parent", parent); + query.setParameter("child", child); + query.executeUpdate(); + } } From 05bac14716e1b011ab56abc7b9d662e9d689e546 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Thu, 8 Aug 2024 15:52:03 -0300 Subject: [PATCH 347/479] Refactor 'var' variables to explicit types --- .../java/org/dspace/eperson/GroupServiceImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index b52f17a5c692..4cec4c9c0d93 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -725,19 +725,19 @@ private Set> computeNewCache(Context context, boolean flushQuer */ protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { // current cache in the database - var oldCache = group2GroupCacheDAO.getCache(context); + Set> oldCache = group2GroupCacheDAO.getCache(context); // correct cache, computed from the Group table - var newCache = computeNewCache(context, flushQueries); + Set> newCache = computeNewCache(context, flushQueries); - var toDelete = SetUtils.difference(oldCache, newCache); - var toCreate = SetUtils.difference(newCache, oldCache); + SetUtils.SetView> toDelete = SetUtils.difference(oldCache, newCache); + SetUtils.SetView> toCreate = SetUtils.difference(newCache, oldCache); - for (var pair : toDelete ) { + for (Pair pair : toDelete ) { group2GroupCacheDAO.deleteFromCache(context, pair.getLeft(), pair.getRight()); } - for (var pair : toCreate ) { + for (Pair pair : toCreate ) { group2GroupCacheDAO.addToCache(context, pair.getLeft(), pair.getRight()); } } From aa027aefae2e39558273fcdbd511c288f8b0def3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 17 Dec 2024 13:11:43 -0600 Subject: [PATCH 348/479] Improve Apache Ant download process. Switch to using curl so that we can retry the request if it initially fails. (cherry picked from commit e236634a4c758a6c0d81e929b2f36d49e81b0835) --- Dockerfile | 10 ++++------ Dockerfile.cli | 10 ++++------ Dockerfile.test | 10 ++++------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index ff35f2a2e3a7..102f00abe1f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,14 +42,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.13 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME + curl --silent --show-error --location --fail --retry 5 --output /tmp/apache-ant.tar.gz \ + https://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && \ + tar -zx --strip-components=1 -f /tmp/apache-ant.tar.gz -C $ANT_HOME && \ + rm /tmp/apache-ant.tar.gz # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code update_webapps diff --git a/Dockerfile.cli b/Dockerfile.cli index be03e8922b2c..e208e3d92145 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -38,14 +38,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.13 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME + curl --silent --show-error --location --fail --retry 5 --output /tmp/apache-ant.tar.gz \ + https://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && \ + tar -zx --strip-components=1 -f /tmp/apache-ant.tar.gz -C $ANT_HOME && \ + rm /tmp/apache-ant.tar.gz # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code diff --git a/Dockerfile.test b/Dockerfile.test index 08b6b3018b80..cc73c655b637 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -41,14 +41,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.12 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME + curl --silent --show-error --location --fail --retry 5 --output /tmp/apache-ant.tar.gz \ + https://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && \ + tar -zx --strip-components=1 -f /tmp/apache-ant.tar.gz -C $ANT_HOME && \ + rm /tmp/apache-ant.tar.gz # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code update_webapps From 58af9fd224ba37a8f3a9c8b354c04d772d6f3ee2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 17 Dec 2024 14:13:21 -0600 Subject: [PATCH 349/479] Significantly speed up build of dspace-dependencies by only copying over POM files (cherry picked from commit 6d7a3fcb725bbb5e42790ba5c57292477f2f3f01) --- Dockerfile.dependencies | 58 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 794dfa9a66a8..d3ca9d5e3c87 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -6,7 +6,7 @@ # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 -# Step 1 - Run Maven Build +# Step 1 - Download all Dependencies FROM docker.io/maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app @@ -19,16 +19,60 @@ RUN chown -Rv dspace: /app # Switch to dspace user & run below commands as that user USER dspace -# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents) -ADD --chown=dspace . /app/ +# This next part may look odd, but it speeds up the build of this image *significantly*. +# Copy ONLY the POMs to this image (from local machine). This will allow us to download all dependencies *without* +# performing any code compilation steps. + +# Parent POM +ADD --chown=dspace pom.xml /app/ +RUN mkdir -p /app/dspace + +# 'dspace' module POM. Includes 'additions' ONLY, as it's the only submodule that is required to exist. +ADD --chown=dspace dspace/pom.xml /app/dspace/ +RUN mkdir -p /app/dspace/modules/ +ADD --chown=dspace dspace/modules/pom.xml /app/dspace/modules/ +RUN mkdir -p /app/dspace/modules/additions +ADD --chown=dspace dspace/modules/additions/pom.xml /app/dspace/modules/additions/ + +# 'dspace-api' module POM +RUN mkdir -p /app/dspace-api +ADD --chown=dspace dspace-api/pom.xml /app/dspace-api/ + +# 'dspace-iiif' module POM +RUN mkdir -p /app/dspace-iiif +ADD --chown=dspace dspace-iiif/pom.xml /app/dspace-iiif/ + +# 'dspace-oai' module POM +RUN mkdir -p /app/dspace-oai +ADD --chown=dspace dspace-oai/pom.xml /app/dspace-oai/ + +# 'dspace-rdf' module POM +RUN mkdir -p /app/dspace-rdf +ADD --chown=dspace dspace-rdf/pom.xml /app/dspace-rdf/ + +# 'dspace-server-webapp' module POM +RUN mkdir -p /app/dspace-server-webapp +ADD --chown=dspace dspace-server-webapp/pom.xml /app/dspace-server-webapp/ + +# 'dspace-services' module POM +RUN mkdir -p /app/dspace-services +ADD --chown=dspace dspace-services/pom.xml /app/dspace-services/ + +# 'dspace-sword' module POM +RUN mkdir -p /app/dspace-sword +ADD --chown=dspace dspace-sword/pom.xml /app/dspace-sword/ + +# 'dspace-swordv2' module POM +RUN mkdir -p /app/dspace-swordv2 +ADD --chown=dspace dspace-swordv2/pom.xml /app/dspace-swordv2/ # Trigger the installation of all maven dependencies (hide download progress messages) # Maven flags here ensure that we skip final assembly, skip building test environment and skip all code verification checks. -# These flags speed up this installation as much as reasonably possible. -ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true" -RUN mvn --no-transfer-progress install ${MAVEN_FLAGS} +# These flags speed up this installation and skip tasks we cannot perform as we don't have the full source code. +ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxjc.skip=true -Dxml.skip=true" +RUN mvn --no-transfer-progress verify ${MAVEN_FLAGS} -# Clear the contents of the /app directory (including all maven builds), so no artifacts remain. +# Clear the contents of the /app directory (including all maven target folders), so no artifacts remain. # This ensures when dspace:dspace is built, it will use the Maven local cache (~/.m2) for dependencies USER root RUN rm -rf /app/* From e5568157c87370531e6b86cbf582e160504858fb Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 08:47:54 -0400 Subject: [PATCH 350/479] More information about failed DOI registrations. (cherry picked from commit b8f4ab0eb3e90b080eaae057fb5ee673d3477dc2) --- .../dspace/identifier/doi/DOIOrganiser.java | 3 ++- .../identifier/doi/DataCiteConnector.java | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 088e2b1cbc87..12f6d7c4fca4 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -577,7 +577,8 @@ public void update(DOI doiRow) { } } catch (IdentifierException ex) { if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register the identifier online. ", ex); + LOG.error("Registering DOI {} for object {}: the registrar returned an error.", + doiRow.getDoi(), dso.getID(), ex); } DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index a15e3f7fdbfe..5bb37add2d9a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -464,6 +464,10 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) log.warn("While reserving the DOI {}, we got a http status code " + "{} and the message \"{}\".", doi, Integer.toString(resp.statusCode), resp.getContent()); + Format format = Format.getCompactFormat(); + format.setEncoding("UTF-8"); + XMLOutputter xout = new XMLOutputter(format); + log.info("We send the following XML:\n{}", xout.outputString(root)); throw new DOIIdentifierException("Unable to parse an answer from " + "DataCite API. Please have a look into DSpace logs.", DOIIdentifierException.BAD_ANSWER); @@ -635,6 +639,14 @@ protected DataCiteResponse sendGetRequest(String doi, String path) return sendHttpRequest(httpget, doi); } + /** + * Send a DataCite metadata document to the registrar. + * + * @param doi identify the object. + * @param metadataRoot describe the object. The root element of the document. + * @return the registrar's response. + * @throws DOIIdentifierException passed through. + */ protected DataCiteResponse sendMetadataPostRequest(String doi, Element metadataRoot) throws DOIIdentifierException { Format format = Format.getCompactFormat(); @@ -643,6 +655,14 @@ protected DataCiteResponse sendMetadataPostRequest(String doi, Element metadataR return sendMetadataPostRequest(doi, xout.outputString(new Document(metadataRoot))); } + /** + * Send a DataCite metadata document to the registrar. + * + * @param doi identify the object. + * @param metadata describe the object. + * @return the registrar's response. + * @throws DOIIdentifierException passed through. + */ protected DataCiteResponse sendMetadataPostRequest(String doi, String metadata) throws DOIIdentifierException { // post mds/metadata/ @@ -690,7 +710,7 @@ protected DataCiteResponse sendMetadataPostRequest(String doi, String metadata) * properties such as request URI and method type. * @param doi DOI string to operate on * @return response from DataCite - * @throws DOIIdentifierException if DOI error + * @throws DOIIdentifierException if registrar returns an error. */ protected DataCiteResponse sendHttpRequest(HttpUriRequest req, String doi) throws DOIIdentifierException { From fcc650e1a6fbc1311e905a349fc5ed3957dd1db7 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Sat, 26 Oct 2024 23:33:13 +0200 Subject: [PATCH 351/479] Add limit, offset, and a new parameter to calculate the total entry count in the Solr query used for the metadata navigation index (cherry picked from commit e71de8a4d075d897f68c145233ac19ba0ec3718b) --- .../java/org/dspace/browse/BrowseEngine.java | 30 ++-------------- .../java/org/dspace/browse/SolrBrowseDAO.java | 32 ++++++++++++----- .../org/dspace/discovery/DiscoverResult.java | 11 ++++++ .../org/dspace/discovery/SolrServiceImpl.java | 36 ++++++++++++++++++- 4 files changed, 73 insertions(+), 36 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java index 351c36248209..be7a34086a46 100644 --- a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java +++ b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java @@ -422,9 +422,6 @@ private BrowseInfo browseByValue(BrowserScope bs) } } - // this is the total number of results in answer to the query - int total = getTotalResults(true); - // set the ordering field (there is only one option) dao.setOrderField("sort_value"); @@ -444,6 +441,9 @@ private BrowseInfo browseByValue(BrowserScope bs) dao.setOffset(offset); dao.setLimit(scope.getResultsPerPage()); + // this is the total number of results in answer to the query + int total = getTotalResults(true); + // Holder for the results List results = null; @@ -680,33 +680,9 @@ private int getTotalResults(boolean distinct) // tell the browse query whether we are distinct dao.setDistinct(distinct); - // ensure that the select is set to "*" - String[] select = {"*"}; - dao.setCountValues(select); - - // FIXME: it would be nice to have a good way of doing this in the DAO - // now reset all of the fields that we don't want to have constraining - // our count, storing them locally to reinstate later - String focusField = dao.getJumpToField(); - String focusValue = dao.getJumpToValue(); - int limit = dao.getLimit(); - int offset = dao.getOffset(); - - dao.setJumpToField(null); - dao.setJumpToValue(null); - dao.setLimit(-1); - dao.setOffset(-1); - // perform the query and get the result int count = dao.doCountQuery(); - // now put back the values we removed for this method - dao.setJumpToField(focusField); - dao.setJumpToValue(focusValue); - dao.setLimit(limit); - dao.setOffset(offset); - dao.setCountValues(null); - log.debug(LogHelper.getHeader(context, "get_total_results_return", "return=" + count)); return count; diff --git a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java index f99aab852bf5..1917dec423ec 100644 --- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java @@ -13,6 +13,8 @@ import java.util.Comparator; import java.util.List; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.util.ClientUtils; @@ -180,18 +182,33 @@ private DiscoverResult getSolrResponse() throws BrowseException { addDefaultFilterQueries(query); if (distinct) { DiscoverFacetField dff; + + // To get the number of distinct values we use the next "json.facet" query param + // {"entries_count": {"type":"terms","field": "_filter", "limit":0, "numBuckets":true}}" + ObjectNode jsonFacet = JsonNodeFactory.instance.objectNode(); + ObjectNode entriesCount = JsonNodeFactory.instance.objectNode(); + entriesCount.put("type", "terms"); + entriesCount.put("field", facetField + "_filter"); + entriesCount.put("limit", 0); + entriesCount.put("numBuckets", true); + jsonFacet.set("entries_count", entriesCount); + if (StringUtils.isNotBlank(startsWith)) { dff = new DiscoverFacetField(facetField, - DiscoveryConfigurationParameters.TYPE_TEXT, -1, - DiscoveryConfigurationParameters.SORT.VALUE, startsWith); + DiscoveryConfigurationParameters.TYPE_TEXT, limit, + DiscoveryConfigurationParameters.SORT.VALUE, startsWith, offset); + + // Add the prefix to the json facet query + entriesCount.put("prefix", startsWith); } else { dff = new DiscoverFacetField(facetField, - DiscoveryConfigurationParameters.TYPE_TEXT, -1, - DiscoveryConfigurationParameters.SORT.VALUE); + DiscoveryConfigurationParameters.TYPE_TEXT, limit, + DiscoveryConfigurationParameters.SORT.VALUE, offset); } query.addFacetField(dff); query.setFacetMinCount(1); query.setMaxResults(0); + query.addProperty("json.facet", jsonFacet.toString()); } else { query.setMaxResults(limit/* > 0 ? limit : 20*/); if (offset > 0) { @@ -248,8 +265,7 @@ public int doCountQuery() throws BrowseException { DiscoverResult resp = getSolrResponse(); int count = 0; if (distinct) { - List facetResults = resp.getFacetResult(facetField); - count = facetResults.size(); + count = (int) resp.getTotalEntries(); } else { // we need to cast to int to respect the BrowseDAO contract... count = (int) resp.getTotalSearchResults(); @@ -266,8 +282,8 @@ public List doValueQuery() throws BrowseException { DiscoverResult resp = getSolrResponse(); List facet = resp.getFacetResult(facetField); int count = doCountQuery(); - int start = offset > 0 ? offset : 0; - int max = limit > 0 ? limit : count; //if negative, return everything + int start = 0; + int max = facet.size(); List result = new ArrayList<>(); if (ascending) { for (int i = start; i < (start + max) && i < count; i++) { diff --git a/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java b/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java index 00236d2bfe32..a56804e3e7ea 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java +++ b/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java @@ -32,6 +32,9 @@ public class DiscoverResult { private List indexableObjects; private Map> facetResults; + // Total count of facet entries calculated for a metadata browsing query + private long totalEntries; + /** * A map that contains all the documents sougth after, the key is a string representation of the Indexable Object */ @@ -64,6 +67,14 @@ public void setTotalSearchResults(long totalSearchResults) { this.totalSearchResults = totalSearchResults; } + public long getTotalEntries() { + return totalEntries; + } + + public void setTotalEntries(long totalEntries) { + this.totalEntries = totalEntries; + } + public int getStart() { return start; } diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index cd3797e3e34e..9339b574b578 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -1055,6 +1055,8 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) } //Resolve our facet field values resolveFacetFields(context, query, result, skipLoadingResponse, solrQueryResponse); + //Add total entries count for metadata browsing + resolveEntriesCount(result, solrQueryResponse); } // If any stale entries are found in the current page of results, // we remove those stale entries and rerun the same query again. @@ -1080,7 +1082,39 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) return result; } - + /** + * Stores the total count of entries for metadata index browsing. The count is calculated by the + * json.facet parameter with the following value: + * + *
    
    +     * {
    +     *     "entries_count": {
    +     *         "type": "terms",
    +     *         "field": "facetNameField_filter",
    +     *         "limit": 0,
    +     *         "prefix": "prefix_value",
    +     *         "numBuckets": true
    +     *     }
    +     * }
    +     * 
    + * + * This value is returned in the facets field of the Solr response. + * + * @param result DiscoverResult object where the total entries count will be stored + * @param solrQueryResponse QueryResponse object containing the solr response + */ + private void resolveEntriesCount(DiscoverResult result, QueryResponse solrQueryResponse) { + + Object facetsObj = solrQueryResponse.getResponse().get("facets"); + if (facetsObj instanceof NamedList) { + NamedList facets = (NamedList) facetsObj; + Object bucketsInfoObj = facets.get("entries_count"); + if (bucketsInfoObj instanceof NamedList) { + NamedList bucketsInfo = (NamedList) bucketsInfoObj; + result.setTotalEntries((int) bucketsInfo.get("numBuckets")); + } + } + } private void resolveFacetFields(Context context, DiscoverQuery query, DiscoverResult result, boolean skipLoadingResponse, QueryResponse solrQueryResponse) throws SQLException { From 95742fe4f38842fc0747188ec941b05364ec7797 Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:11:08 +0200 Subject: [PATCH 352/479] fix(submission): Submission scope naming fixed According to the documentation, the value for the property is 'submission' in the 'submission-forms.xml'. Without this change, an empty input field will never be marked as an error, even if the field is marked as 'required'. (cherry picked from commit 02f52c7d5c245eaae09dbd636fc8c7935d7cc242) --- dspace-api/src/main/java/org/dspace/app/util/DCInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index 11f9aadd869b..a3d1ba208b56 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -163,7 +163,7 @@ public class DCInput { * The scope of the input sets, this restricts hidden metadata fields from * view by the end user during submission. */ - public static final String SUBMISSION_SCOPE = "submit"; + public static final String SUBMISSION_SCOPE = "submission"; /** * Class constructor for creating a DCInput object based on the contents of From 31faf809d15d968a021a64720b620faf5f47bf0e Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:50:59 +0100 Subject: [PATCH 353/479] fix(submission): Submission scope naming fixed Corrected wording in related code comment (cherry picked from commit ec2187ea6532d7db5f5ffa38e20e971a49b90871) --- dspace-api/src/main/java/org/dspace/app/util/DCInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index a3d1ba208b56..0a1e77ee7292 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -262,7 +262,7 @@ protected void initRegex(String regex) { /** * Is this DCInput for display in the given scope? The scope should be - * either "workflow" or "submit", as per the input forms definition. If the + * either "workflow" or "submission", as per the input forms definition. If the * internal visibility is set to "null" then this will always return true. * * @param scope String identifying the scope that this input's visibility From 973beee2ce5f8dc993ef5eb91711b595c7786789 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 18:44:15 -0300 Subject: [PATCH 354/479] fix: properly type in field id, adjust use of getProperty and add error handling when dbPath is null (when property usage-statistics.dbfile is commented (cherry picked from commit 412d5751f29f31bbdd872820a62b74970333f066) --- .../org/dspace/statistics/util/StatisticsImporter.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java index 95736a8bd6d9..1bd97a6f5ec9 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java @@ -357,7 +357,7 @@ protected void load(String filename, Context context, boolean verbose) { SolrInputDocument sid = new SolrInputDocument(); sid.addField("ip", ip); sid.addField("type", dso.getType()); - sid.addField("id", dso.getID()); + sid.addField("id", dso.getID().toString()); sid.addField("time", DateFormatUtils.format(date, SolrLoggerServiceImpl.DATE_FORMAT_8601)); sid.addField("continent", continent); sid.addField("country", country); @@ -471,13 +471,13 @@ public static void main(String[] args) throws Exception { boolean verbose = line.hasOption('v'); // Find our solr server - String sserver = configurationService.getProperty("solr-statistics", "server"); + String sserver = configurationService.getProperty("solr-statistics.server"); if (verbose) { System.out.println("Writing to solr server at: " + sserver); } solr = new HttpSolrClient.Builder(sserver).build(); - String dbPath = configurationService.getProperty("usage-statistics", "dbfile"); + String dbPath = configurationService.getProperty("usage-statistics.dbfile"); try { File dbFile = new File(dbPath); geoipLookup = new DatabaseReader.Builder(dbFile).build(); @@ -492,6 +492,10 @@ public static void main(String[] args) throws Exception { "Unable to load GeoLite Database file (" + dbPath + ")! You may need to reinstall it. See the DSpace " + "installation instructions for more details.", e); + } catch (NullPointerException e) { + log.error( + "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite Database file and/or uncomment the property in the config file!", + e); } From 3a223e3fefec9172538e7defa34ce982b0d72762 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 20:16:47 -0300 Subject: [PATCH 355/479] fix line length checkstyle (cherry picked from commit 338f3b1d3efaa0f7f3852654121cf1f93adeae7e) --- .../java/org/dspace/statistics/util/StatisticsImporter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java index 1bd97a6f5ec9..354c803fe2ae 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java @@ -494,7 +494,8 @@ public static void main(String[] args) throws Exception { e); } catch (NullPointerException e) { log.error( - "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite Database file and/or uncomment the property in the config file!", + "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite " + + "Database file and/or uncomment the property in the config file!", e); } From c86d082d5f6fe4a76eae018bbe6890cc0b72ff33 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 13:10:14 -0300 Subject: [PATCH 356/479] fix: set default configFile (cherry picked from commit a5e8d7aa15bc56c268ba67f19657f19edaa45253) --- .../java/org/dspace/app/statistics/LogAnalyser.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java index 2e4ed69b268e..c787261419f8 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java @@ -281,10 +281,14 @@ public class LogAnalyser { */ private static String fileTemplate = "dspace\\.log.*"; + private static final ConfigurationService configurationService = + DSpaceServicesFactory.getInstance().getConfigurationService(); + /** * the configuration file from which to configure the analyser */ - private static String configFile; + private static String configFile = configurationService.getProperty("dspace.dir") + + File.separator + "config" + File.separator + "dstat.cfg"; /** * the output file to which to write aggregation data @@ -616,8 +620,6 @@ public static String processLogs(Context context, String myLogDir, } // now do the host name and url lookup - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); hostName = Utils.getHostName(configurationService.getProperty("dspace.ui.url")); name = configurationService.getProperty("dspace.name").trim(); url = configurationService.getProperty("dspace.ui.url").trim(); @@ -658,8 +660,6 @@ public static void setParameters(String myLogDir, String myFileTemplate, String myConfigFile, String myOutFile, Date myStartDate, Date myEndDate, boolean myLookUp) { - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); if (myLogDir != null) { logDir = myLogDir; @@ -673,9 +673,6 @@ public static void setParameters(String myLogDir, String myFileTemplate, if (myConfigFile != null) { configFile = myConfigFile; - } else { - configFile = configurationService.getProperty("dspace.dir") - + File.separator + "config" + File.separator + "dstat.cfg"; } if (myStartDate != null) { From 1ce4e08333559bffadde8d266a1009c1c875df33 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:45:16 -0600 Subject: [PATCH 357/479] [Port dspace-7_x] Fix issue with submission sections visibility (#10140) * README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) (cherry picked from commit 83460afb3700a2ddaabce16069b2094e4edbe8fa) * Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) (cherry picked from commit 7a6785b1c353cbc45c4dfaad89f5252064ef1e4e) * [DURACOM-291] Expose section scope attribute (cherry picked from commit 4107f937fda1b49da2b6a81dc91026ebf4b1cd37) * README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) (cherry picked from commit d98499a39416d026c0b45199e90ddb9bbfc1cd9c) * Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) (cherry picked from commit 6a707548ffa6929e8a36775063ce05e2dc02a586) --------- Co-authored-by: Christian Clauss Co-authored-by: Giuseppe Digilio --- .../dspace/app/rest/converter/SubmissionSectionConverter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java index 0391cbce7a2d..3cd263493b5d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java @@ -10,6 +10,7 @@ import java.sql.SQLException; import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.model.ScopeEnum; import org.dspace.app.rest.model.SubmissionSectionRest; import org.dspace.app.rest.model.SubmissionVisibilityRest; import org.dspace.app.rest.model.VisibilityEnum; @@ -41,6 +42,7 @@ public SubmissionSectionRest convert(SubmissionStepConfig step, Projection proje sp.setHeader(step.getHeading()); sp.setSectionType(step.getType()); sp.setId(step.getId()); + sp.setScope(ScopeEnum.fromString(step.getScope())); sp.setVisibility(new SubmissionVisibilityRest(VisibilityEnum.fromString(step.getVisibility()), VisibilityEnum.fromString(step.getVisibilityOutside()))); return sp; From 51df5c1fe67b7660dafa85d57f688347ca7f024d Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 2 Dec 2024 15:35:10 +0100 Subject: [PATCH 358/479] 119664: Search event scope fix (cherry picked from commit 48956d90b7619bdb336632b454ac62ca62967a39) --- .../org/dspace/app/rest/converter/SearchEventConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java index 978ae2ca9230..f2fb12f2bda9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java @@ -67,8 +67,8 @@ public UsageSearchEvent convert(Context context, HttpServletRequest request, Sea if (searchEventRest.getScope() != null) { IndexableObject scopeObject = scopeResolver.resolveScope(context, String.valueOf(searchEventRest.getScope())); - if (scopeObject instanceof DSpaceObject) { - usageSearchEvent.setScope((DSpaceObject) scopeObject); + if (scopeObject != null && scopeObject.getIndexedObject() instanceof DSpaceObject) { + usageSearchEvent.setScope((DSpaceObject) scopeObject.getIndexedObject()); } } usageSearchEvent.setConfiguration(searchEventRest.getConfiguration()); From 8849212895d987abaca92865178c6b4d2f99452d Mon Sep 17 00:00:00 2001 From: Chris Wilper Date: Wed, 28 Apr 2021 09:29:36 -0400 Subject: [PATCH 359/479] Add Context method to uncache all entities (cherry picked from commit 8ea664adb2161b04ab8b6171bbb426d8875fe27f) --- .../main/java/org/dspace/core/Context.java | 14 +++++++- .../java/org/dspace/core/DBConnection.java | 32 ++++++++++++------- .../dspace/core/HibernateDBConnection.java | 5 +++ .../java/org/dspace/core/ContextTest.java | 25 +++++++++++++++ .../core/HibernateDBConnectionTest.java | 22 +++++++++++++ 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 02a3fee09f8a..dab6ab7fbd66 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -883,7 +883,19 @@ public E reloadEntity(E entity) throws SQLException } /** - * Remove an entity from the cache. This is necessary when batch processing a large number of items. + * Remove all entities from the cache and reload the current user entity. This is useful when batch processing + * a large number of entities when the calling code requires the cache to be completely cleared before continuing. + * + * @throws SQLException if a database error occurs. + */ + public void uncacheEntities() throws SQLException { + dbConnection.uncacheEntities(); + reloadContextBoundEntities(); + } + + /** + * Remove an entity from the cache. This is useful when batch processing a large number of entities + * when the calling code needs to retain some items in the cache while removing others. * * @param entity The entity to reload * @param The class of the entity. The entity must implement the {@link ReloadableEntity} interface. diff --git a/dspace-api/src/main/java/org/dspace/core/DBConnection.java b/dspace-api/src/main/java/org/dspace/core/DBConnection.java index 66e4a65dbfe1..c9c4ce0953e4 100644 --- a/dspace-api/src/main/java/org/dspace/core/DBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/DBConnection.java @@ -124,28 +124,38 @@ public interface DBConnection { public long getCacheSize() throws SQLException; /** - * Reload a DSpace object from the database. This will make sure the object + * Reload an entity from the database. This will make sure the object * is valid and stored in the cache. The returned object should be used * henceforth instead of the passed object. * - * @param type of {@link entity} - * @param entity The DSpace object to reload + * @param type of entity. + * @param entity The entity to reload. * @return the reloaded entity. - * @throws java.sql.SQLException passed through. + * @throws SQLException passed through. */ public E reloadEntity(E entity) throws SQLException; /** - * Remove a DSpace object from the session cache when batch processing a - * large number of objects. + * Remove all entities from the session cache. * - *

    Objects removed from cache are not saved in any way. Therefore, if you - * have modified an object, you should be sure to {@link commit()} changes + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified any entities, you should be sure to {@link #commit()} changes * before calling this method. * - * @param Type of {@link entity} - * @param entity The DSpace object to decache. - * @throws java.sql.SQLException passed through. + * @throws SQLException passed through. + */ + public void uncacheEntities() throws SQLException; + + /** + * Remove an entity from the session cache. + * + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified the entity, you should be sure to {@link #commit()} changes + * before calling this method. + * + * @param Type of entity. + * @param entity The entity to decache. + * @throws SQLException passed through. */ public void uncacheEntity(E entity) throws SQLException; diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index b371af80eede..bd00b844ba9a 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -243,6 +243,11 @@ private void configureDatabaseMode() throws SQLException { } } + @Override + public void uncacheEntities() throws SQLException { + getSession().clear(); + } + /** * Evict an entity from the hibernate cache. *

    diff --git a/dspace-api/src/test/java/org/dspace/core/ContextTest.java b/dspace-api/src/test/java/org/dspace/core/ContextTest.java index c6cd849d2110..ccc1d2f732cc 100644 --- a/dspace-api/src/test/java/org/dspace/core/ContextTest.java +++ b/dspace-api/src/test/java/org/dspace/core/ContextTest.java @@ -558,4 +558,29 @@ protected void init() { cleanupContext(instance); } + @Test + public void testUncacheEntities() throws Throwable { + // To set up the test, ensure the cache contains more than the current user entity + groupService.findByName(context, Group.ANONYMOUS); + assertTrue("Cache size should be greater than one", context.getDBConnection().getCacheSize() > 1); + + context.uncacheEntities(); + + assertThat("Cache size should be one (current user)", context.getDBConnection().getCacheSize(), equalTo(1L)); + context.reloadEntity(context.getCurrentUser()); + assertThat("Cache should only contain the current user", context.getDBConnection().getCacheSize(), equalTo(1L)); + } + + @Test + public void testUncacheEntity() throws Throwable { + // Remember the cache size after loading an entity + Group group = groupService.findByName(context, Group.ANONYMOUS); + long oldCacheSize = context.getDBConnection().getCacheSize(); + + // Uncache the entity + context.uncacheEntity(group); + + long newCacheSize = context.getDBConnection().getCacheSize(); + assertThat("Cache size should be reduced by one", newCacheSize, equalTo(oldCacheSize - 1)); + } } diff --git a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java index 093f693d567f..302844ce62ac 100644 --- a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java +++ b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java @@ -205,6 +205,28 @@ public void testReloadEntityAfterCommit() throws SQLException { .contains(person)); } + /** + * Test of uncacheEntities method + */ + @Test + public void testUncacheEntities() throws SQLException { + // Get DBConnection associated with DSpace Context + HibernateDBConnection dbConnection = (HibernateDBConnection) context.getDBConnection(); + EPerson person = context.getCurrentUser(); + + assertTrue("Current user should be cached in session", dbConnection.getSession() + .contains(person)); + + dbConnection.uncacheEntities(); + assertFalse("Current user should be gone from cache", dbConnection.getSession() + .contains(person)); + + // Test ability to reload an uncached entity + person = dbConnection.reloadEntity(person); + assertTrue("Current user should be cached back in session", dbConnection.getSession() + .contains(person)); + } + /** * Test of uncacheEntity method */ From e856ae3291831feac74f01133f7e235d8513cba0 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Sun, 27 Oct 2024 08:56:10 +0100 Subject: [PATCH 360/479] Uncache all entities during OAI indexing to reduce memory usage (cherry picked from commit 9af2e2e17cf289a2a6921d27a21d90db6ca8b9f9) --- dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java | 5 +++++ 1 file changed, 5 insertions(+) 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 c6aaaa34b539..2d2679577802 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 @@ -334,6 +334,11 @@ private int index(Iterator iterator) throws DSpaceSolrIndexerException { server.add(list); server.commit(); list.clear(); + try { + context.uncacheEntities(); + } catch (SQLException ex) { + log.error("Error uncaching entities", ex); + } } } System.out.println("Total: " + i + " items"); From 964e4bf476b9cb49ce07c5d05242c63e9ae638d4 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 23 Dec 2024 12:26:58 +0100 Subject: [PATCH 361/479] remove usage of deprecated constructor call (cherry picked from commit 45cdb4d9d47d3151a4672e99152a0b3bedb6cd18) --- dspace-api/src/main/java/org/dspace/curate/Curation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java index 4d70286e79e0..ea47d817c07d 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curation.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -185,7 +185,7 @@ private Curator initCurator() throws FileNotFoundException { Curator curator = new Curator(handler); OutputStream reporterStream; if (null == this.reporter) { - reporterStream = new NullOutputStream(); + reporterStream = NullOutputStream.NULL_OUTPUT_STREAM; } else if ("-".equals(this.reporter)) { reporterStream = System.out; } else { From 214c669e8063f93eca2f878610647cc3b87346bd Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 11 Dec 2024 15:35:26 +0100 Subject: [PATCH 362/479] fix missing +1 offset (cherry picked from commit ab00de05b46fe98306e0047af7d6d98bba9d0077) --- dspace-oai/src/main/resources/static/style.xsl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dspace-oai/src/main/resources/static/style.xsl b/dspace-oai/src/main/resources/static/style.xsl index 17eb865e8f1f..67aeb975b26b 100644 --- a/dspace-oai/src/main/resources/static/style.xsl +++ b/dspace-oai/src/main/resources/static/style.xsl @@ -522,15 +522,14 @@ - + - - + - - + From 6c82e2dba40e50e72ab8459995802a5d7f526a53 Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Tue, 7 Jan 2025 15:51:28 +0100 Subject: [PATCH 363/479] Bugfix: Enforce unique item id in workspace table (#9340) * 106798 Enforce values in item_id column of workspaceitem table to be unique, both at database level and at WorkspaceItemService level * 106798 Removed Oracle SQL migration * 106798 workspaceitem table migration: delete duplicate rows before introducing uniqueness constraint * 106798: update migration for H2 --------- Co-authored-by: Koen Pauwels Co-authored-by: wout --- .../content/WorkspaceItemServiceImpl.java | 8 +++ ...paceitem_add_item_id_unique_constraint.sql | 21 +++++++ ...paceitem_add_item_id_unique_constraint.sql | 21 +++++++ .../org/dspace/content/WorkspaceItemTest.java | 12 ++++ .../org/dspace/workflow/MockWorkflowItem.java | 62 +++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql create mode 100644 dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java index 1da9e6e44a6a..543f5a55efe2 100644 --- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java @@ -178,6 +178,14 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b @Override public WorkspaceItem create(Context c, WorkflowItem workflowItem) throws SQLException, AuthorizeException { + WorkspaceItem potentialDuplicate = findByItem(c, workflowItem.getItem()); + if (potentialDuplicate != null) { + throw new IllegalArgumentException(String.format( + "A workspace item referring to item %s already exists (%d)", + workflowItem.getItem().getID(), + potentialDuplicate.getID() + )); + } WorkspaceItem workspaceItem = workspaceItemDAO.create(c, new WorkspaceItem()); workspaceItem.setItem(workflowItem.getItem()); workspaceItem.setCollection(workflowItem.getCollection()); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql new file mode 100644 index 000000000000..38389bf2d19b --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql @@ -0,0 +1,21 @@ +-- +-- 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/ +-- + +-- In the workspaceitem table, if there are multiple rows referring to the same item ID, keep only the first of them. +DELETE FROM workspaceitem WHERE EXISTS ( + SELECT item_id + FROM workspaceitem + GROUP BY item_id + HAVING COUNT(workspace_item_id) > 1 +) AND workspaceitem.workspace_item_id NOT IN ( + SELECT MIN(workspace_item_id) AS workspace_item_id + FROM workspaceitem + GROUP BY item_id +); +-- Identify which rows have duplicates, and compute their replacements. +ALTER TABLE workspaceitem ADD CONSTRAINT unique_item_id UNIQUE(item_id); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql new file mode 100644 index 000000000000..20eb0f9119d3 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql @@ -0,0 +1,21 @@ +-- +-- 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/ +-- + +-- In the workspaceitem table, if there are multiple rows referring to the same item ID, keep only the first of them. +WITH dedup AS ( + SELECT item_id, MIN(workspace_item_id) AS workspace_item_id + FROM workspaceitem + GROUP BY item_id + HAVING COUNT(workspace_item_id) > 1 +) +DELETE FROM workspaceitem +USING dedup +WHERE workspaceitem.item_id = dedup.item_id AND workspaceitem.workspace_item_id <> dedup.workspace_item_id; + +-- Enforce uniqueness of item_id in workspaceitem table. +ALTER TABLE workspaceitem ADD CONSTRAINT unique_item_id UNIQUE(item_id); diff --git a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java index d018a15f9765..15d4720c9378 100644 --- a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java @@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -39,6 +40,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; +import org.dspace.workflow.MockWorkflowItem; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -468,4 +470,14 @@ public void testSetPublishedBefore() { assertTrue("testSetPublishedBefore 0", wi.isPublishedBefore()); } + @Test + public void testDuplicateItemID() throws Exception { + context.turnOffAuthorisationSystem(); + Item item = wi.getItem(); + MockWorkflowItem wfItem = new MockWorkflowItem(); + wfItem.item = item; + wfItem.collection = collection; + assertThrows(IllegalArgumentException.class, () -> workspaceItemService.create(context, wfItem)); + context.restoreAuthSystemState(); + } } diff --git a/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java b/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java new file mode 100644 index 000000000000..d36ecf7331b8 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java @@ -0,0 +1,62 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.workflow; + +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.eperson.EPerson; + +public class MockWorkflowItem implements WorkflowItem { + public Integer id; + public Item item; + public Collection collection; + public EPerson submitter; + boolean hasMultipleFiles; + boolean hasMultipleTitles; + boolean isPublishedBefore; + + public Integer getID() { + return id; + } + + public Item getItem() { + return item; + } + + public Collection getCollection() { + return collection; + } + + public EPerson getSubmitter() { + return submitter; + } + + public boolean hasMultipleFiles() { + return hasMultipleFiles; + } + + public void setMultipleFiles(boolean b) { + hasMultipleFiles = b; + } + + public boolean hasMultipleTitles() { + return hasMultipleTitles; + } + + public void setMultipleTitles(boolean b) { + hasMultipleTitles = b; + } + + public boolean isPublishedBefore() { + return isPublishedBefore; + } + + public void setPublishedBefore(boolean b) { + isPublishedBefore = b; + } +} From 22511a17b67c8034b039634e7bf98d9e9a58ae5d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Nov 2024 12:03:31 -0600 Subject: [PATCH 364/479] Refactor identifier ITs to ensure they unregister all utilized IdentifierProviders which are non-default. Cannot use "getApplicationContext().refresh()" as that seems to result in empty test database in Hibernate 6.6. (cherry picked from commit cfca2adbb1d597761b3169a5a46170de8cabfa4f) --- .../general/CreateMissingIdentifiersIT.java | 48 +++---------- .../AbstractIdentifierProviderIT.java | 68 +++++++++++++++++++ .../VersionedHandleIdentifierProviderIT.java | 49 ++----------- 3 files changed, 84 insertions(+), 81 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 8038a7153325..4934404c3b3e 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,10 +10,7 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; @@ -21,13 +18,11 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; -import org.dspace.identifier.IdentifierProvider; -import org.dspace.identifier.IdentifierServiceImpl; +import org.dspace.identifier.AbstractIdentifierProviderIT; +import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; -import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.junit.After; import org.junit.Test; /** @@ -36,30 +31,19 @@ * @author mwood */ public class CreateMissingIdentifiersIT - extends AbstractIntegrationTestWithDatabase { - private ServiceManager serviceManager; - private IdentifierServiceImpl identifierService; + extends AbstractIdentifierProviderIT { + private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; - @Override - public void setUp() throws Exception { - super.setUp(); - context.turnOffAuthorisationSystem(); - - serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); - identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); - // Clean out providers to avoid any being used for creation of community and collection - identifierService.setProviders(new ArrayList<>()); - } + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @Test public void testPerform() throws IOException { // Must remove any cached named plugins before creating a new one CoreServiceFactory.getInstance().getPluginService().clearNamedPluginClasses(); - ConfigurationService configurationService = kernelImpl.getConfigurationService(); // Define a new task dynamically configurationService.setProperty(P_TASK_DEF, CreateMissingIdentifiers.class.getCanonicalName() + " = " + TASK_NAME); @@ -76,7 +60,7 @@ public void testPerform() .build(); /* - * Curate with regular test configuration -- should succeed. + * Curate with default Handle Provider */ curator.curate(context, item); int status = curator.getStatus(TASK_NAME); @@ -92,22 +76,10 @@ public void testPerform() curator.getResult(TASK_NAME)); assertEquals("Curation should fail", Curator.CURATE_ERROR, curator.getStatus(TASK_NAME)); - } - - @Override - @After - public void destroy() throws Exception { - super.destroy(); - DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); - } - - private void registerProvider(Class type) { - // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); - IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService.setProviders(List.of(identifierProvider)); + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests which may depend on it) + registerProvider(VersionedHandleIdentifierProvider.class); } } diff --git a/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java new file mode 100644 index 000000000000..6ec9efddf5e2 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java @@ -0,0 +1,68 @@ +/** + * 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.identifier; + +import java.util.ArrayList; +import java.util.List; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.kernel.ServiceManager; +import org.dspace.services.factory.DSpaceServicesFactory; + +/** + * AbstractIdentifierProviderIT which contains a few useful utility methods for IdentifierProvider Integration Tests + */ +public class AbstractIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { + + protected final ServiceManager serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + protected final IdentifierServiceImpl identifierService = + serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + + /** + * Register a specific IdentifierProvider into the current IdentifierService (replacing any existing providers). + * This method will also ensure the IdentifierProvider service is registered in the DSpace Service Manager. + * @param type IdentifierProvider Class + */ + protected void registerProvider(Class type) { + // Register our new provider + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } + + identifierService.setProviders(List.of(identifierProvider)); + } + + /** + * Unregister a specific IdentifierProvider from the current IdentifierService (removing all existing providers). + * This method will also ensure the IdentifierProvider service is unregistered in the DSpace Service Manager, + * which ensures it does not conflict with other IdentifierProvider services. + * @param type IdentifierProvider Class + */ + protected void unregisterProvider(Class type) { + // Find the provider service + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + // If found, unregister it + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().unregisterService(type.getName()); + } + + // Overwrite the identifier-service's providers with an empty list + identifierService.setProviders(new ArrayList<>()); + } + +} + + + diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 57acf1f1c453..8c0f7e5b5e10 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -11,10 +11,7 @@ import static org.junit.Assert.assertTrue; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -22,15 +19,10 @@ import org.dspace.builder.VersionBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; -import org.dspace.kernel.ServiceManager; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.junit.After; import org.junit.Before; import org.junit.Test; -public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { - private ServiceManager serviceManager; - private IdentifierServiceImpl identifierService; +public class VersionedHandleIdentifierProviderIT extends AbstractIdentifierProviderIT { private String firstHandle; @@ -44,12 +36,6 @@ public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTest public void setUp() throws Exception { super.setUp(); context.turnOffAuthorisationSystem(); - - serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); - identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); - // Clean out providers to avoid any being used for creation of community and collection - identifierService.setProviders(new ArrayList<>()); - parentCommunity = CommunityBuilder.createCommunity(context) .withName("Parent Community") .build(); @@ -58,33 +44,6 @@ public void setUp() throws Exception { .build(); } - @After - @Override - public void destroy() throws Exception { - super.destroy(); - // After this test has finished running, refresh application context and - // set the expected 'default' versioned handle provider back to ensure other tests don't fail - DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); - } - - private void registerProvider(Class type) { - // Register our new provider - IdentifierProvider identifierProvider = - (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(type.getName(), type); - if (identifierProvider == null) { - DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); - identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(type.getName(), type); - } - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService = DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(IdentifierServiceImpl.class).get(0); - identifierService.setProviders(new ArrayList<>()); - identifierService.setProviders(List.of(identifierProvider)); - } - private void createVersions() throws SQLException, AuthorizeException { itemV1 = ItemBuilder.createItem(context, collection) .withTitle("First version") @@ -96,7 +55,6 @@ private void createVersions() throws SQLException, AuthorizeException { @Test public void testDefaultVersionedHandleProvider() throws Exception { - registerProvider(VersionedHandleIdentifierProvider.class); createVersions(); // Confirm the original item only has its original handle @@ -125,6 +83,11 @@ public void testCanonicalVersionedHandleProvider() throws Exception { assertEquals(firstHandle, itemV3.getHandle()); assertEquals(2, itemV3.getHandles().size()); containsHandle(itemV3, firstHandle + ".3"); + + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests) + registerProvider(VersionedHandleIdentifierProvider.class); } private void containsHandle(Item item, String handle) { From 41207d5dee23c78d9f8a8ef93be89ed331607b13 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 8 Nov 2024 10:17:31 -0600 Subject: [PATCH 365/479] Update CreateMissingIdentifiers to better identify when CanonicalHandles provider is enabled. Update CreateMissingIdentifiersIT to verify that we are accurately resetting to our default IdentifierProvider (cherry picked from commit 2385c13f2d48048306a1280d88da5d8c022cc24f) --- .../general/CreateMissingIdentifiers.java | 28 +++++++++---------- .../identifier/IdentifierServiceImpl.java | 5 ++++ .../identifier/service/IdentifierService.java | 6 ++++ .../general/CreateMissingIdentifiersIT.java | 17 +++++------ 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java b/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java index 9639461426ef..0734d60946bc 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.sql.SQLException; +import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,7 +26,6 @@ import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; import org.dspace.identifier.factory.IdentifierServiceFactory; import org.dspace.identifier.service.IdentifierService; -import org.dspace.services.factory.DSpaceServicesFactory; /** * Ensure that an object has all of the identifiers that it should, minting them @@ -45,20 +45,6 @@ public int perform(DSpaceObject dso) return Curator.CURATE_SKIP; } - // XXX Temporary escape when an incompatible provider is configured. - // XXX Remove this when the provider is fixed. - boolean compatible = DSpaceServicesFactory - .getInstance() - .getServiceManager() - .getServiceByName( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - IdentifierProvider.class) == null; - if (!compatible) { - setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles"); - return Curator.CURATE_ERROR; - } - // XXX End of escape - String typeText = Constants.typeText[dso.getType()]; // Get a Context @@ -75,6 +61,18 @@ public int perform(DSpaceObject dso) .getInstance() .getIdentifierService(); + // XXX Temporary escape when an incompatible provider is configured. + // XXX Remove this when the provider is fixed. + List providerList = identifierService.getProviders(); + boolean compatible = + providerList.stream().noneMatch(p -> p instanceof VersionedHandleIdentifierProviderWithCanonicalHandles); + + if (!compatible) { + setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles"); + return Curator.CURATE_ERROR; + } + // XXX End of escape + // Register any missing identifiers. try { identifierService.register(context, dso); diff --git a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java index b98aea24fa08..e6dcfcdda693 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java @@ -57,6 +57,11 @@ public void setProviders(List providers) { } } + @Override + public List getProviders() { + return this.providers; + } + /** * Reserves identifiers for the item * diff --git a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java index 23005b657508..45bf3c6dea8a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java +++ b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java @@ -19,6 +19,7 @@ import org.dspace.identifier.IdentifierException; import org.dspace.identifier.IdentifierNotFoundException; import org.dspace.identifier.IdentifierNotResolvableException; +import org.dspace.identifier.IdentifierProvider; /** * @author Fabio Bolognesi (fabio at atmire dot com) @@ -194,4 +195,9 @@ void register(Context context, DSpaceObject dso, String identifier) void delete(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException; + /** + * Get List of currently enabled IdentifierProviders + * @return List of enabled IdentifierProvider objects. + */ + List getProviders(); } diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 4934404c3b3e..3b50258a5a23 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -60,14 +60,7 @@ public void testPerform() .build(); /* - * Curate with default Handle Provider - */ - curator.curate(context, item); - int status = curator.getStatus(TASK_NAME); - assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status); - - /* - * Now install an incompatible provider to make the task fail. + * First, install an incompatible provider to make the task fail. */ registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); @@ -81,5 +74,13 @@ public void testPerform() unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); // Re-register the default provider (for later tests which may depend on it) registerProvider(VersionedHandleIdentifierProvider.class); + + /* + * Now, verify curate with default Handle Provider works + * (and that our re-registration of the default provider above was successful) + */ + curator.curate(context, item); + int status = curator.getStatus(TASK_NAME); + assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status); } } From 97a5439e3aa23bfde4f9741282d48c10a873b387 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Thu, 9 Jan 2025 14:09:24 +0100 Subject: [PATCH 366/479] switch IT search core to MockSolrSearchCore https://github.com/DSpace/DSpace/issues/10188 (cherry picked from commit 6d781e8f83a912863af8d998d982e030db64a2ce) --- .../org/dspace/content/VersioningWithRelationshipsIT.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 3acc4ca146ee..9ff452f7895f 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -59,7 +59,7 @@ import org.dspace.content.virtual.VirtualMetadataConfiguration; import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; -import org.dspace.discovery.SolrSearchCore; +import org.dspace.discovery.MockSolrSearchCore; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -79,8 +79,9 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getInstallItemService(); private final ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - private final SolrSearchCore solrSearchCore = - DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); + private final MockSolrSearchCore solrSearchCore = + DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(null, MockSolrSearchCore.class); + protected Community community; protected Collection collection; protected EntityType publicationEntityType; From 49b3deef77055a18daba21c0bf2812bb2daa3265 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 9 Jan 2025 15:42:25 -0600 Subject: [PATCH 367/479] Refactor AbstractIntegrationTestWithDatabase to use Builders to create test EPersons. (cherry picked from commit 0b8b7be22b88a411898bcb975e238e7cd96f40ab) --- .../AbstractIntegrationTestWithDatabase.java | 48 ++++++++----------- .../AbstractWebClientIntegrationTest.java | 16 +++++++ 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java index e27fb19a68eb..d5bb6ab8a4c8 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java +++ b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java @@ -17,8 +17,8 @@ import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; import org.dspace.authority.AuthoritySearchService; import org.dspace.authority.MockAuthoritySolrServiceImpl; -import org.dspace.authorize.AuthorizeException; import org.dspace.builder.AbstractBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.Community; import org.dspace.core.Context; import org.dspace.core.I18nUtil; @@ -114,19 +114,16 @@ public void setUp() throws Exception { EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); eperson = ePersonService.findByEmail(context, "test@email.com"); if (eperson == null) { - // This EPerson creation should only happen once (i.e. for first test run) - log.info("Creating initial EPerson (email=test@email.com) for Unit Tests"); - eperson = ePersonService.create(context); - eperson.setFirstName(context, "first"); - eperson.setLastName(context, "last"); - eperson.setEmail("test@email.com"); - eperson.setCanLogIn(true); - eperson.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - ePersonService.setPassword(eperson, password); - // actually save the eperson to unit testing DB - ePersonService.update(context, eperson); + // Create test EPerson for usage in all tests + log.info("Creating Test EPerson (email=test@email.com) for Integration Tests"); + eperson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("first", "last") + .withEmail("test@email.com") + .withCanLogin(true) + .withLanguage(I18nUtil.getDefaultLocale().getLanguage()) + .withPassword(password) + .build(); } - // Set our global test EPerson as the current user in DSpace context.setCurrentUser(eperson); @@ -135,26 +132,23 @@ public void setUp() throws Exception { admin = ePersonService.findByEmail(context, "admin@email.com"); if (admin == null) { - // This EPerson creation should only happen once (i.e. for first test run) - log.info("Creating initial EPerson (email=admin@email.com) for Unit Tests"); - admin = ePersonService.create(context); - admin.setFirstName(context, "first (admin)"); - admin.setLastName(context, "last (admin)"); - admin.setEmail("admin@email.com"); - admin.setCanLogIn(true); - admin.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - ePersonService.setPassword(admin, password); - // actually save the eperson to unit testing DB - ePersonService.update(context, admin); + // Create test Administrator for usage in all tests + log.info("Creating Test Admin EPerson (email=admin@email.com) for Integration Tests"); + admin = EPersonBuilder.createEPerson(context) + .withNameInMetadata("first (admin)", "last (admin)") + .withEmail("admin@email.com") + .withCanLogin(true) + .withLanguage(I18nUtil.getDefaultLocale().getLanguage()) + .withPassword(password) + .build(); + + // Add Test Administrator to the ADMIN group in test database GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); Group adminGroup = groupService.findByName(context, Group.ADMIN); groupService.addMember(context, adminGroup, admin); } context.restoreAuthSystemState(); - } catch (AuthorizeException ex) { - log.error("Error creating initial eperson or default groups", ex); - fail("Error creating initial eperson or default groups in AbstractUnitTest init()"); } catch (SQLException ex) { log.error(ex.getMessage(), ex); fail("SQL Error on AbstractUnitTest init()"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index df6afcb16e32..fadebff7de5b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -12,6 +12,7 @@ import org.dspace.app.rest.Application; import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; import org.dspace.app.rest.utils.DSpaceKernelInitializer; +import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -64,6 +65,21 @@ public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWit @Autowired protected ApplicationContext applicationContext; + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + // Because AbstractWebClientIntegrationTest starts a new webserver, we need to ensure *everything* created by + // the "super.setUp()" script is committed to the test database. Otherwise, the new webserver may not see the + // created test data in the database. + // NOTE: This commit() does NOT occur in AbstractIntegrationTestDatabase because it will remove newly created + // Hibernate entities from the current Hibernate session. For most ITs we don't want that as it may result + // in "could not initialize proxy - no Session" errors when using those entities in other tests (or other tests + // would need to reload each test entity back into the Hibernate session) + context.commit(); + } + /** * Get client TestRestTemplate for making HTTP requests to test webserver * @return TestRestTemplate From 5973c67eff7180496ee761a32fa1f9636b02c93e Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 16:58:00 +0200 Subject: [PATCH 368/479] put DOIs in dc.identifier.doi (cherry picked from commit 3d1bef9d0e3fe820da9705427c129b5cb49d66fb) --- dspace/config/spring/api/scopus-integration.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/spring/api/scopus-integration.xml b/dspace/config/spring/api/scopus-integration.xml index 47c5a4fbb900..6f7556574d09 100644 --- a/dspace/config/spring/api/scopus-integration.xml +++ b/dspace/config/spring/api/scopus-integration.xml @@ -128,7 +128,7 @@ - + @@ -264,4 +264,4 @@ - \ No newline at end of file + From 5d0e9871e9ec4cbacda577f9f2ae8cefcd961e27 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 17:37:07 +0200 Subject: [PATCH 369/479] fix broken test (cherry picked from commit 2eff833fab361d4ed3c7b602b681823911f989af) --- .../dspace/app/rest/ScopusImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index 7f6cb53ce400..e81f662ad5a9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -124,7 +124,7 @@ private ArrayList getRecords() { ArrayList records = new ArrayList<>(); //define first record List metadatums = new ArrayList(); - MetadatumDTO doi = createMetadatumDTO("dc", "identifier", null, "10.3934/mine.2023004"); + MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023004"); MetadatumDTO title = createMetadatumDTO("dc","title", null, "Hardy potential versus lower order terms in Dirichlet problems: regularizing effects"); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Journal"); @@ -171,7 +171,7 @@ private ArrayList getRecords() { //define second record List metadatums2 = new ArrayList(); - MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", null, "10.3934/mine.2023001"); + MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023001"); MetadatumDTO title2 = createMetadatumDTO("dc","title", null, "Large deviations for a binary collision model: energy evaporation"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2023-01-01"); From 09a722a029ebc6e50835c360af8977d97cb1c033 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 17 Oct 2024 20:17:06 +0200 Subject: [PATCH 370/479] use dc.relation.hasversion for externally generated DOIs (cherry picked from commit 29067b6572b538650e4686e983edafcc355d87c5) --- dspace/config/spring/api/scopus-integration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/scopus-integration.xml b/dspace/config/spring/api/scopus-integration.xml index 6f7556574d09..d3e8150c74b1 100644 --- a/dspace/config/spring/api/scopus-integration.xml +++ b/dspace/config/spring/api/scopus-integration.xml @@ -128,7 +128,7 @@ - + From d109eac2a0ad170b656e36028d84fc294ae6eb35 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 17 Oct 2024 20:19:04 +0200 Subject: [PATCH 371/479] use dc.relation.hasversion instead of dc.identifier.doi (cherry picked from commit d61dc8d911a4d226362d9a554226da5c304ad722) --- .../dspace/app/rest/ScopusImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index e81f662ad5a9..f9164ddc5994 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -124,7 +124,7 @@ private ArrayList getRecords() { ArrayList records = new ArrayList<>(); //define first record List metadatums = new ArrayList(); - MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023004"); + MetadatumDTO doi = createMetadatumDTO("dc", "relation", "hasversion", "10.3934/mine.2023004"); MetadatumDTO title = createMetadatumDTO("dc","title", null, "Hardy potential versus lower order terms in Dirichlet problems: regularizing effects"); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Journal"); @@ -171,7 +171,7 @@ private ArrayList getRecords() { //define second record List metadatums2 = new ArrayList(); - MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023001"); + MetadatumDTO doi2 = createMetadatumDTO("dc", "relation", "hasversion", "10.3934/mine.2023001"); MetadatumDTO title2 = createMetadatumDTO("dc","title", null, "Large deviations for a binary collision model: energy evaporation"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2023-01-01"); From 62596ad86313053f56fa316fba62ff1156cbeb37 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Tue, 8 Oct 2024 13:42:07 +0200 Subject: [PATCH 372/479] option to exclude the submitter being indexed to solr in archived items https://github.com/DSpace/DSpace/issues/9660 --- .../indexobject/ItemIndexFactoryImpl.java | 8 +- .../org/dspace/discovery/DiscoveryIT.java | 109 ++++++++++++++++++ dspace/config/modules/discovery.cfg | 7 +- 3 files changed, 120 insertions(+), 4 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 7cdb8b93d80e..5fb2f779783c 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 @@ -154,9 +154,11 @@ public SolrInputDocument buildDocument(Context context, IndexableItem indexableI doc.addField("latestVersion", isLatestVersion(context, item)); EPerson submitter = item.getSubmitter(); - if (submitter != null) { - addFacetIndex(doc, "submitter", submitter.getID().toString(), - submitter.getFullName()); + if (submitter != null && !(DSpaceServicesFactory.getInstance().getConfigurationService().getBooleanProperty( + "discovery.index.item.submitter.enabled", false))) { + doc.addField("submitter_authority", submitter.getID().toString()); + } else if (submitter != null) { + addFacetIndex(doc, "submitter", submitter.getID().toString(), submitter.getFullName()); } // Add the item metadata diff --git a/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java b/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java index 55be531418ae..159f7a0ac5ae 100644 --- a/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java +++ b/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java @@ -8,6 +8,10 @@ package org.dspace.discovery; import static org.dspace.discovery.SolrServiceWorkspaceWorkflowRestrictionPlugin.DISCOVER_WORKSPACE_CONFIGURATION_NAME; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -21,6 +25,10 @@ import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocument; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; @@ -99,6 +107,9 @@ public class DiscoveryIT extends AbstractIntegrationTestWithDatabase { MetadataAuthorityService metadataAuthorityService = ContentAuthorityServiceFactory.getInstance() .getMetadataAuthorityService(); + MockSolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(null, MockSolrSearchCore.class); + @Override @Before public void setUp() throws Exception { @@ -796,6 +807,104 @@ public void searchWithDefaultSortServiceTest() throws SearchServiceException { } } + /** + * Test designed to check if the submitter is not indexed in all in solr documents for items + * and the submitter authority is still indexed + * @throws SearchServiceException + */ + @Test + public void searchWithNoSubmitterTest() throws SearchServiceException { + + configurationService.setProperty("discovery.index.item.submitter.enabled", false); + DiscoveryConfiguration defaultConf = SearchUtils.getDiscoveryConfiguration(context, "default", null); + + // Populate the testing objects: create items in eperson's workspace and perform search in it + int numberItems = 10; + context.turnOffAuthorisationSystem(); + EPerson submitter = null; + try { + submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org") + .withNameInMetadata("Peter", "Funny").build(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + context.setCurrentUser(submitter); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + for (int i = 0; i < numberItems; i++) { + ItemBuilder.createItem(context, collection) + .withTitle("item " + i) + .build(); + } + context.restoreAuthSystemState(); + + // Build query with default parameters (except for workspaceConf) + QueryResponse result = null; + try { + result = solrSearchCore.getSolr().query(new SolrQuery(String.format( + "search.resourcetype:\"Item\""))); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(result.getResults().size(), numberItems); + for (SolrDocument doc : result.getResults()) { + assertThat(doc.getFieldNames(), + not(hasItems("submitter_keyword", "submitter_ac", "submitter_acid", "submitter_filter"))); + assertThat(doc.getFieldNames(), hasItem("submitter_authority")); + } + } + + /** + * Test designed to check if the submitter is indexed in all in solr documents for items + * @throws SearchServiceException + */ + @Test + public void searchWithSubmitterTest() throws SearchServiceException { + + configurationService.setProperty("discovery.index.item.submitter.enabled", true); + DiscoveryConfiguration defaultConf = SearchUtils.getDiscoveryConfiguration(context, "default", null); + + // Populate the testing objects: create items in eperson's workspace and perform search in it + int numberItems = 10; + context.turnOffAuthorisationSystem(); + EPerson submitter = null; + try { + submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org") + .withNameInMetadata("Peter", "Funny").build(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + context.setCurrentUser(submitter); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + for (int i = 0; i < numberItems; i++) { + ItemBuilder.createItem(context, collection) + .withTitle("item " + i) + .build(); + } + context.restoreAuthSystemState(); + + // Build query with default parameters (except for workspaceConf) + QueryResponse result = null; + try { + result = solrSearchCore.getSolr().query(new SolrQuery(String.format( + "search.resourcetype:\"Item\""))); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(result.getResults().size(), numberItems); + for (SolrDocument doc : result.getResults()) { + for (String fieldname : doc.getFieldNames()) { + assertThat(doc.getFieldNames(), hasItems("submitter_keyword","submitter_ac", "submitter_filter", + "submitter_authority")); + } + } + } + private void assertSearchQuery(String resourceType, int size) throws SearchServiceException { assertSearchQuery(resourceType, size, size, 0, -1); } diff --git a/dspace/config/modules/discovery.cfg b/dspace/config/modules/discovery.cfg index 72088ddc49fa..cd8e8636c2e3 100644 --- a/dspace/config/modules/discovery.cfg +++ b/dspace/config/modules/discovery.cfg @@ -24,6 +24,11 @@ discovery.search.server = ${solr.server}/${solr.multicorePrefix}search # discovery.index.ignore-authority = false discovery.index.projection=dc.title,dc.contributor.*,dc.date.issued +# Restricts the indexing of the submitter for archived items +# By default the submitter information from the corresponding eperson is not indexed. +# If you set this value to true, than the submitter information is indexed and you will need to reindex search core +# discovery.index.item.submitter.enabled = false + # Allow auto-reindexing. # If any database migrations are applied to your database (via Flyway), then a # reindex flag is always written to '[dspace]/solr/search/conf/reindex.flag'. @@ -48,4 +53,4 @@ discovery.facet.namedtype.workflow.pooled = 004workflow\n|||\nWaiting for Contro # Set the number of retry of a query when stale objects are found. # Set to -1 if stale objects should be ignored. Set to 0 if you want to avoid extra query but take the chance to cleanup # the index each time that stale objects are found. Default 3 -discovery.removestale.attempts = 3 \ No newline at end of file +discovery.removestale.attempts = 3 From e840ca27315ce8fb2772e8bcdf6c34c042a6cc99 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Oct 2024 14:55:30 -0500 Subject: [PATCH 373/479] Create dependabot.yml --- .github/dependabot.yml | 115 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..6f4f3ae601a0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,115 @@ +#------------------- +# DSpace's dependabot rules. Enables maven updates for all dependencies on a weekly basis +# for main and any maintenance branches. Security updates only apply to main. +#------------------- +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + # Allow up to 10 open PRs for dependencies + open-pull-requests-limit: 10 + # Group together some upgrades in a single PR + groups: + # Group together all Build Tools in a single PR + build-tools: + applies-to: version-updates + patterns: + - "org.apache.maven.plugins:*" + - "*:*-maven-plugin" + - "*:maven-*-plugin" + - "com.github.spotbugs:spotbugs" + - "com.google.code.findbugs:*" + - "com.google.errorprone:*" + - "com.puppycrawl.tools:checkstyle" + - "org.sonatype.plugins:*" + update-types: + - "minor" + - "patch" + test-tools: + applies-to: version-updates + patterns: + - "junit:*" + - "com.github.stefanbirker:system-rules" + - "com.h2database:*" + - "io.findify:s3mock*" + - "io.netty:*" + - "org.hamcrest:*" + - "org.mock-server:*" + - "org.mockito:*" + update-types: + - "minor" + - "patch" + # Group together all Apache Commons deps in a single PR + apache-commons: + applies-to: version-updates + patterns: + - "org.apache.commons:*" + - "commons-*:commons-*" + update-types: + - "minor" + - "patch" + # Group together all fasterxml deps in a single PR + fasterxml: + applies-to: version-updates + patterns: + - "com.fasterxml:*" + - "com.fasterxml.*:*" + update-types: + - "minor" + - "patch" + # Group together all Hibernate deps in a single PR + hibernate: + applies-to: version-updates + patterns: + - "org.hibernate.*:*" + update-types: + - "minor" + - "patch" + # Group together all Jakarta deps in a single PR + jakarta: + applies-to: version-updates + patterns: + - "jakarta.*:*" + - "org.eclipse.angus:jakarta.mail" + - "org.glassfish.jaxb:jaxb-runtime" + update-types: + - "minor" + - "patch" + # Group together all Google deps in a single PR + google-apis: + applies-to: version-updates + patterns: + - "com.google.apis:*" + - "com.google.api-client:*" + - "com.google.http-client:*" + - "com.google.oauth-client:*" + update-types: + - "minor" + - "patch" + # Group together all Spring deps in a single PR + spring: + applies-to: version-updates + patterns: + - "org.springframework:*" + - "org.springframework.*:*" + update-types: + - "minor" + - "patch" + # Group together all WebJARs deps in a single PR + webjars: + applies-to: version-updates + patterns: + - "org.webjars:*" + - "org.webjars.*:*" + update-types: + - "minor" + - "patch" + ignore: + # Don't try to auto-update any DSpace dependencies + - dependency-name: "org.dspace:*" + - dependency-name: "org.dspace.*:*" + # Ignore all major version updates for all dependencies. We'll only automate minor/patch updates. + - dependency-name: "*" + update-types: ["version-update:semver-major"] From fe28962e9672c01477059bcc2d2c8787a781dadf Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 15:01:41 -0500 Subject: [PATCH 374/479] Exclude spring from build-tools group in dependabot.yml --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6f4f3ae601a0..b6412b25b660 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,9 @@ updates: - "com.google.errorprone:*" - "com.puppycrawl.tools:checkstyle" - "org.sonatype.plugins:*" + exclude-patterns: + # Exclude anything from Spring, as that is in a separate group + - "org.springframework.*:*" update-types: - "minor" - "patch" From 0b46a0963c5cfe8189fe3b8764eb6450ec41664a Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 2 Apr 2024 11:15:49 +0200 Subject: [PATCH 375/479] 113811: cli logs should be written to a different file (cherry picked from commit d30468a09fbbd288df6cf2556e1a630f040801d1) --- dspace/bin/dspace | 2 + dspace/config/log4j2-cli.xml | 105 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 dspace/config/log4j2-cli.xml diff --git a/dspace/bin/dspace b/dspace/bin/dspace index 7d5d678d0f6e..24644aae9112 100644 --- a/dspace/bin/dspace +++ b/dspace/bin/dspace @@ -39,6 +39,8 @@ if [ "$JAVA_OPTS" = "" ]; then JAVA_OPTS="-Xmx256m -Dfile.encoding=UTF-8" fi +export JAVA_OPTS="$JAVA_OPTS -Dlog4j2.configurationFile=$DSPACEDIR/config/log4j2-cli.xml" + # Now invoke Java java $JAVA_OPTS \ -classpath $FULLPATH \ diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml new file mode 100644 index 000000000000..24d36fd12efb --- /dev/null +++ b/dspace/config/log4j2-cli.xml @@ -0,0 +1,105 @@ + + + + + + + ${log4j:configParentLocation}/../log + + + INFO + + + INFO + + + + + + + + + yyyy-MM-dd + + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 04ec6381cba0472f4fdbc0918588180393a89c5d Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Fri, 6 Sep 2024 10:15:08 +0200 Subject: [PATCH 376/479] Modifying it so that the cli file content is stored in a file using the date (cherry picked from commit 529c3a77c15f401fe623b2c9d04a438754a1d72d) --- dspace/config/log4j2-cli.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 24d36fd12efb..73acab877f1f 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -25,8 +25,6 @@ From 1a6088388fa4bc0d01ecdea0b1fee7108a27bce5 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 4 Dec 2024 11:08:37 +0100 Subject: [PATCH 377/479] apply fix to windows env and remove duplicate logging for checksum checker (cherry picked from commit 9f39a3d6a53f9a815509d6a47abd176b07a6a86c) --- dspace/bin/dspace.bat | 3 +++ dspace/config/log4j2.xml | 27 --------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/dspace/bin/dspace.bat b/dspace/bin/dspace.bat index 2288127c447a..768b1061762d 100644 --- a/dspace/bin/dspace.bat +++ b/dspace/bin/dspace.bat @@ -38,6 +38,9 @@ REM If JAVA_OPTS specified, use those options REM Otherwise, default Java to using 256MB of memory if "%JAVA_OPTS%"=="" set "JAVA_OPTS=-Xmx256m -Dfile.encoding=UTF-8" +REM Add log4j2 configuration to JAVA_OPTS +set "JAVA_OPTS=%JAVA_OPTS% -Dlog4j2.configurationFile=%cd%\config\log4j2-cli.xml" + REM Execute Java java %JAVA_OPTS% -classpath "%DSPACE_CLASSPATH%" org.dspace.app.launcher.ScriptLauncher %* diff --git a/dspace/config/log4j2.xml b/dspace/config/log4j2.xml index 6e9a43e4f0fe..4f212d128dc6 100644 --- a/dspace/config/log4j2.xml +++ b/dspace/config/log4j2.xml @@ -44,26 +44,6 @@ --> - - - - - - yyyy-MM-dd - - - @@ -75,13 +55,6 @@ - - - - - From 1492ed7f61b5685e0e8e0f4365460887465bef88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:01:16 +0000 Subject: [PATCH 378/479] Bump the build-tools group with 21 updates Bumps the build-tools group with 21 updates: | Package | From | To | | --- | --- | --- | | [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) | `2.10.0` | `2.36.0` | | [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `8.38` | `8.45.1` | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.8.2` | `4.8.6` | | [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) | `3.0.0-M3` | `3.5.0` | | [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | `3.8.1` | `3.13.0` | | [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) | `3.2.0` | `3.4.2` | | [org.apache.maven.plugins:maven-war-plugin](https://github.com/apache/maven-war-plugin) | `3.2.3` | `3.4.0` | | [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) | `3.3.1` | `3.6.0` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.8.2.0` | `4.8.6.6` | | [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) | `3.1.0` | `3.4.0` | | [org.apache.maven.plugins:maven-assembly-plugin](https://github.com/apache/maven-assembly-plugin) | `3.6.0` | `3.7.1` | | [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) | `3.1.2` | `3.8.1` | | [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) | `3.1.0` | `3.3.1` | | org.sonatype.plugins:nexus-staging-maven-plugin | `1.6.8` | `1.7.0` | | [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) | `3.2.0` | `3.11.2` | | [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) | `3.2.1` | `3.3.1` | | [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.5` | `0.8.12` | | [org.codehaus.mojo:xml-maven-plugin](https://github.com/mojohaus/xml-maven-plugin) | `1.0.2` | `1.1.0` | | [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | `2.0.0` | `2.5.0` | | [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) | `3.4.0` | `3.6.0` | | [org.codehaus.mojo:buildnumber-maven-plugin](https://github.com/mojohaus/buildnumber-maven-plugin) | `3.2.0` | `3.2.1` | Updates `com.google.errorprone:error_prone_core` from 2.10.0 to 2.36.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.36.0) Updates `com.puppycrawl.tools:checkstyle` from 8.38 to 8.45.1 - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.38...checkstyle-8.45.1) Updates `com.github.spotbugs:spotbugs` from 4.8.2 to 4.8.6 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.8.2...4.8.6) Updates `org.apache.maven.plugins:maven-enforcer-plugin` from 3.0.0-M3 to 3.5.0 - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.0.0-M3...enforcer-3.5.0) Updates `org.apache.maven.plugins:maven-compiler-plugin` from 3.8.1 to 3.13.0 - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.13.0) Updates `org.apache.maven.plugins:maven-jar-plugin` from 3.2.0 to 3.4.2 - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.0...maven-jar-plugin-3.4.2) Updates `org.apache.maven.plugins:maven-war-plugin` from 3.2.3 to 3.4.0 - [Commits](https://github.com/apache/maven-war-plugin/compare/maven-war-plugin-3.2.3...maven-war-plugin-3.4.0) Updates `org.apache.maven.plugins:maven-checkstyle-plugin` from 3.3.1 to 3.6.0 - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.1...maven-checkstyle-plugin-3.6.0) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.8.2.0 to 4.8.6.6 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.2.0...spotbugs-maven-plugin-4.8.6.6) Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.1.0 to 3.4.0 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.1.0...maven-clean-plugin-3.4.0) Updates `org.apache.maven.plugins:maven-assembly-plugin` from 3.6.0 to 3.7.1 - [Release notes](https://github.com/apache/maven-assembly-plugin/releases) - [Commits](https://github.com/apache/maven-assembly-plugin/compare/maven-assembly-plugin-3.6.0...maven-assembly-plugin-3.7.1) Updates `org.apache.maven.plugins:maven-dependency-plugin` from 3.1.2 to 3.8.1 - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.1.2...maven-dependency-plugin-3.8.1) Updates `org.apache.maven.plugins:maven-resources-plugin` from 3.1.0 to 3.3.1 - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.1.0...maven-resources-plugin-3.3.1) Updates `org.sonatype.plugins:nexus-staging-maven-plugin` from 1.6.8 to 1.7.0 Updates `org.apache.maven.plugins:maven-javadoc-plugin` from 3.2.0 to 3.11.2 - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.2.0...maven-javadoc-plugin-3.11.2) Updates `org.apache.maven.plugins:maven-source-plugin` from 3.2.1 to 3.3.1 - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.1) Updates `org.jacoco:jacoco-maven-plugin` from 0.8.5 to 0.8.12 - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.5...v0.8.12) Updates `org.codehaus.mojo:xml-maven-plugin` from 1.0.2 to 1.1.0 - [Release notes](https://github.com/mojohaus/xml-maven-plugin/releases) - [Commits](https://github.com/mojohaus/xml-maven-plugin/compare/xml-maven-plugin-1.0.2...1.1.0) Updates `org.codehaus.mojo:license-maven-plugin` from 2.0.0 to 2.5.0 - [Release notes](https://github.com/mojohaus/license-maven-plugin/releases) - [Commits](https://github.com/mojohaus/license-maven-plugin/compare/license-maven-plugin-2.0.0...2.5.0) Updates `org.codehaus.mojo:build-helper-maven-plugin` from 3.4.0 to 3.6.0 - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.4.0...3.6.0) Updates `org.codehaus.mojo:buildnumber-maven-plugin` from 3.2.0 to 3.2.1 - [Release notes](https://github.com/mojohaus/buildnumber-maven-plugin/releases) - [Commits](https://github.com/mojohaus/buildnumber-maven-plugin/compare/3.2.0...3.2.1) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-war-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-assembly-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-resources-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.codehaus.mojo:xml-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:license-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:buildnumber-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 4 ++-- pom.xml | 38 +++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c42895ab4c49..cf81408a2659 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -102,7 +102,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.4.0 + 3.6.0 validate @@ -116,7 +116,7 @@ org.codehaus.mojo buildnumber-maven-plugin - 3.2.0 + 3.2.1 UNKNOWN_REVISION diff --git a/pom.xml b/pom.xml index 50faf1f7372e..f496ed7e879d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ 8.11.3 3.10.8 - 2.10.0 + 2.36.0 2.16.0 2.16.0 @@ -89,7 +89,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.5.0 enforce-java @@ -140,7 +140,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 11 @@ -173,7 +173,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.4.2 @@ -187,7 +187,7 @@ org.apache.maven.plugins maven-war-plugin - 3.2.3 + 3.4.0 false @@ -257,7 +257,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.3.1 + 3.6.0 verify-style @@ -287,14 +287,14 @@ com.puppycrawl.tools checkstyle - 8.38 + 8.45.1 com.github.spotbugs spotbugs-maven-plugin - 4.8.2.0 + 4.8.6.6 Max Low @@ -304,7 +304,7 @@ com.github.spotbugs spotbugs - 4.8.2 + 4.8.6 @@ -320,7 +320,7 @@ maven-clean-plugin - 3.1.0 + 3.4.0 @@ -334,17 +334,17 @@ maven-assembly-plugin - 3.6.0 + 3.7.1 org.apache.maven.plugins maven-dependency-plugin - 3.1.2 + 3.8.1 org.apache.maven.plugins maven-resources-plugin - 3.1.0 + 3.3.1 @@ -356,13 +356,13 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.7.0 org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.11.2 false @@ -372,7 +372,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.1 @@ -391,7 +391,7 @@ org.jacoco jacoco-maven-plugin - 0.8.5 + 0.8.12 @@ -471,7 +471,7 @@ org.codehaus.mojo xml-maven-plugin - 1.0.2 + 1.1.0 validate-ALL-xml-and-xsl @@ -679,7 +679,7 @@ org.codehaus.mojo license-maven-plugin - 2.0.0 + 2.5.0 false From d7b0e26179f9b0812afb722fd13efa00eae4fa57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:02:16 +0000 Subject: [PATCH 379/479] Bump the apache-commons group with 11 updates Bumps the apache-commons group with 11 updates: | Package | From | To | | --- | --- | --- | | commons-beanutils:commons-beanutils | `1.9.4` | `1.10.0` | | commons-cli:commons-cli | `1.6.0` | `1.9.0` | | [commons-codec:commons-codec](https://github.com/apache/commons-codec) | `1.16.0` | `1.17.2` | | org.apache.commons:commons-configuration2 | `2.10.1` | `2.11.0` | | org.apache.commons:commons-dbcp2 | `2.11.0` | `2.13.0` | | commons-io:commons-io | `2.15.1` | `2.18.0` | | org.apache.commons:commons-lang3 | `3.14.0` | `3.17.0` | | commons-logging:commons-logging | `1.3.0` | `1.3.4` | | org.apache.commons:commons-compress | `1.26.0` | `1.27.1` | | org.apache.commons:commons-text | `1.10.0` | `1.13.0` | | commons-validator:commons-validator | `1.7` | `1.9.0` | Updates `commons-beanutils:commons-beanutils` from 1.9.4 to 1.10.0 Updates `commons-cli:commons-cli` from 1.6.0 to 1.9.0 Updates `commons-codec:commons-codec` from 1.16.0 to 1.17.2 - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.16.0...rel/commons-codec-1.17.2) Updates `org.apache.commons:commons-configuration2` from 2.10.1 to 2.11.0 Updates `org.apache.commons:commons-dbcp2` from 2.11.0 to 2.13.0 Updates `commons-io:commons-io` from 2.15.1 to 2.18.0 Updates `org.apache.commons:commons-lang3` from 3.14.0 to 3.17.0 Updates `commons-logging:commons-logging` from 1.3.0 to 1.3.4 Updates `org.apache.commons:commons-compress` from 1.26.0 to 1.27.1 Updates `org.apache.commons:commons-text` from 1.10.0 to 1.13.0 Updates `commons-validator:commons-validator` from 1.7 to 1.9.0 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-cli:commons-cli dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-codec:commons-codec dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-configuration2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-dbcp2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-lang3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-logging:commons-logging dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-text dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-validator:commons-validator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..242c03d1b178 100644 --- a/pom.xml +++ b/pom.xml @@ -1463,17 +1463,17 @@ commons-beanutils commons-beanutils - 1.9.4 + 1.10.0 commons-cli commons-cli - 1.6.0 + 1.9.0 commons-codec commons-codec - 1.16.0 + 1.17.2 org.apache.commons @@ -1483,12 +1483,12 @@ org.apache.commons commons-configuration2 - 2.10.1 + 2.11.0 org.apache.commons commons-dbcp2 - 2.11.0 + 2.13.0 commons-fileupload @@ -1498,24 +1498,24 @@ commons-io commons-io - 2.15.1 + 2.18.0 org.apache.commons commons-lang3 - 3.14.0 + 3.17.0 commons-logging commons-logging - 1.3.0 + 1.3.4 org.apache.commons commons-compress - 1.26.0 + 1.27.1 org.apache.commons @@ -1525,12 +1525,12 @@ org.apache.commons commons-text - 1.10.0 + 1.13.0 commons-validator commons-validator - 1.7 + 1.9.0 joda-time From 4a20a4c37fa43c33eb6ba11ea49422e15e786212 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:02:28 +0000 Subject: [PATCH 380/479] Bump the fasterxml group with 4 updates Bumps the fasterxml group with 4 updates: [com.fasterxml:classmate](https://github.com/FasterXML/java-classmate), [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson). Updates `com.fasterxml:classmate` from 1.6.0 to 1.7.0 - [Commits](https://github.com/FasterXML/java-classmate/compare/classmate-1.6.0...classmate-1.7.0) Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.18.2) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.18.2) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml:classmate dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..a7a87a7938ff 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,8 @@ 3.10.8 2.10.0 - 2.16.0 - 2.16.0 + 2.18.2 + 2.18.2 1.3.2 2.3.1 2.3.9 @@ -1742,7 +1742,7 @@ com.fasterxml classmate - 1.6.0 + 1.7.0 com.fasterxml.jackson.core From bec1ee84e51e6a22766c58348791292fe9d6f36d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:03:40 +0000 Subject: [PATCH 381/479] Bump org.webjars.npm:json-editor__json-editor in the webjars group Bumps the webjars group with 1 update: [org.webjars.npm:json-editor__json-editor](https://github.com/json-editor/json-editor). Updates `org.webjars.npm:json-editor__json-editor` from 2.6.1 to 2.15.1 - [Changelog](https://github.com/json-editor/json-editor/blob/master/CHANGELOG.md) - [Commits](https://github.com/json-editor/json-editor/compare/2.6.1...2.15.1) --- updated-dependencies: - dependency-name: org.webjars.npm:json-editor__json-editor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: webjars ... Signed-off-by: dependabot[bot] --- 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 35fa473fc170..2715338ed0fa 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -345,7 +345,7 @@ org.webjars.npm json-editor__json-editor - 2.6.1 + 2.15.1 From d9945b27b94f372979ac3268d2c85887edb00fa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:03:55 +0000 Subject: [PATCH 383/479] Bump jersey.version from 2.39.1 to 2.46 Bumps `jersey.version` from 2.39.1 to 2.46. Updates `org.glassfish.jersey.core:jersey-client` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.inject:jersey-hk2` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.core:jersey-server` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.containers:jersey-container-servlet` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.media:jersey-media-json-jackson` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.media:jersey-media-jaxb` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.ext:jersey-spring5` from 2.39.1 to 2.46 --- updated-dependencies: - dependency-name: org.glassfish.jersey.core:jersey-client dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.inject:jersey-hk2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.core:jersey-server dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.containers:jersey-container-servlet dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.media:jersey-media-json-jackson dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.media:jersey-media-jaxb dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.ext:jersey-spring5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..1e83cbbb9bb9 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ https://jena.apache.org/documentation/migrate_jena2_jena3.html --> 2.13.0 - 2.39.1 + 2.46 UTF-8 From 9d17009cf390ea403e439bc5287bf6c118c88836 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:04:17 +0000 Subject: [PATCH 384/479] Bump jetty.version from 9.4.54.v20240208 to 9.4.57.v20241219 Bumps `jetty.version` from 9.4.54.v20240208 to 9.4.57.v20241219. Updates `org.eclipse.jetty:jetty-server` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-alpn-java-server` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-deploy` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-http` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-io` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-servlet` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-servlets` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-util` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-webapp` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-xml` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty.http2:http2-common` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty.http2:http2-server` from 9.4.54.v20240208 to 9.4.57.v20241219 --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-alpn-java-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-deploy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-http dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-io dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlet dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlets dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-webapp dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-xml dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty.http2:http2-common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty.http2:http2-server dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..6575c7e5277f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.3.9 1.1.1 - 9.4.54.v20240208 + 9.4.57.v20241219 2.23.1 2.0.31 1.19.0 From a01983c23027642163f7a8f88525e5638784f428 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 17:02:14 -0500 Subject: [PATCH 385/479] Fix checkstyle.xml syntax for bump to 8.45.1 --- checkstyle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checkstyle.xml b/checkstyle.xml index e0fa808d83cb..a33fc4831950 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -92,7 +92,7 @@ For more information on CheckStyle configurations below, see: http://checkstyle. - + From eee743a72d779d7af940a0ecf83393f26c2b4de2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 17:02:46 -0500 Subject: [PATCH 386/479] Minor checkstyle fixes after bump to 8.45.1. All are indentation / spacing fixes which are more strict now. --- .../rest/AuthenticationRestController.java | 2 +- .../app/rest/model/AuthorizationRest.java | 6 +-- .../dspace/app/rest/model/BitstreamRest.java | 15 ++----- .../app/rest/model/BrowseIndexRest.java | 10 +---- .../org/dspace/app/rest/model/BundleRest.java | 15 ++----- .../app/rest/model/ClaimedTaskRest.java | 5 +-- .../dspace/app/rest/model/CollectionRest.java | 40 ++++------------- .../dspace/app/rest/model/CommunityRest.java | 25 +++-------- .../dspace/app/rest/model/EPersonRest.java | 5 +-- .../dspace/app/rest/model/EntityTypeRest.java | 5 +-- .../app/rest/model/ExternalSourceRest.java | 5 +-- .../org/dspace/app/rest/model/GroupRest.java | 15 ++----- .../org/dspace/app/rest/model/ItemRest.java | 45 ++++--------------- .../app/rest/model/OrcidHistoryRest.java | 2 +- .../dspace/app/rest/model/PoolTaskRest.java | 5 +-- .../dspace/app/rest/model/ProcessRest.java | 15 ++----- .../app/rest/model/RelationshipRest.java | 5 +-- .../app/rest/model/ResearcherProfileRest.java | 4 +- .../app/rest/model/VersionHistoryRest.java | 10 +---- .../dspace/app/rest/model/VersionRest.java | 10 +---- .../model/VocabularyEntryDetailsRest.java | 6 +-- .../dspace/app/rest/model/VocabularyRest.java | 4 +- .../rest/model/WorkflowDefinitionRest.java | 10 +---- .../app/rest/model/WorkflowItemRest.java | 20 ++------- .../app/rest/model/WorkflowStepRest.java | 5 +-- .../app/rest/model/WorkspaceItemRest.java | 20 ++------- .../org/dspace/app/rest/utils/RegexUtils.java | 2 +- .../app/rest/matcher/RegistrationMatcher.java | 2 +- 28 files changed, 70 insertions(+), 243 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java index e8b8eb8e70da..2fbc116843ca 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java @@ -224,7 +224,7 @@ private AuthenticationTokenResource shortLivedTokenResponse(HttpServletRequest r * @return ResponseEntity */ @RequestMapping(value = "/login", method = { RequestMethod.GET, RequestMethod.PUT, RequestMethod.PATCH, - RequestMethod.DELETE }) + RequestMethod.DELETE }) public ResponseEntity login() { return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body("Only POST is allowed for login requests."); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java index 95f288831303..7aec2cb4b76e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java @@ -18,9 +18,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON), - @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE), - @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT) + @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON), + @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE), + @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT) }) public class AuthorizationRest extends BaseObjectRest { public static final String NAME = "authorization"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java index 8e9efc2680b7..77b558c0faab 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java @@ -16,18 +16,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = BitstreamRest.BUNDLE, - method = "getBundle" - ), - @LinkRest( - name = BitstreamRest.FORMAT, - method = "getFormat" - ), - @LinkRest( - name = BitstreamRest.THUMBNAIL, - method = "getThumbnail" - ) + @LinkRest(name = BitstreamRest.BUNDLE, method = "getBundle"), + @LinkRest(name = BitstreamRest.FORMAT, method = "getFormat"), + @LinkRest(name = BitstreamRest.THUMBNAIL, method = "getThumbnail") }) public class BitstreamRest extends DSpaceObjectRest { public static final String PLURAL_NAME = "bitstreams"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java index f7978f00fdf5..176b268e4be1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java @@ -20,14 +20,8 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = BrowseIndexRest.LINK_ITEMS, - method = "listBrowseItems" - ), - @LinkRest( - name = BrowseIndexRest.LINK_ENTRIES, - method = "listBrowseEntries" - ) + @LinkRest(name = BrowseIndexRest.LINK_ITEMS, method = "listBrowseItems"), + @LinkRest(name = BrowseIndexRest.LINK_ENTRIES, method = "listBrowseEntries") }) public class BrowseIndexRest extends BaseObjectRest { private static final long serialVersionUID = -4870333170249999559L; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java index dd4a80d488a8..7ef1ceed92c9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java @@ -16,18 +16,9 @@ * @author Jelle Pelgrims (jelle.pelgrims at atmire.com) */ @LinksRest(links = { - @LinkRest( - name = BundleRest.ITEM, - method = "getItem" - ), - @LinkRest( - name = BundleRest.BITSTREAMS, - method = "getBitstreams" - ), - @LinkRest( - name = BundleRest.PRIMARY_BITSTREAM, - method = "getPrimaryBitstream" - ) + @LinkRest(name = BundleRest.ITEM, method = "getItem"), + @LinkRest(name = BundleRest.BITSTREAMS, method = "getBitstreams"), + @LinkRest(name = BundleRest.PRIMARY_BITSTREAM, method = "getPrimaryBitstream") }) public class BundleRest extends DSpaceObjectRest { public static final String NAME = "bundle"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java index dd97fef44d0b..d9c907697573 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java @@ -16,10 +16,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = ClaimedTaskRest.STEP, - method = "getStep" - ) + @LinkRest(name = ClaimedTaskRest.STEP, method = "getStep") }) public class ClaimedTaskRest extends BaseObjectRest { public static final String NAME = "claimedtask"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java index 3f5ae3bb34c2..28ff9bcceb6b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java @@ -15,38 +15,14 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = CollectionRest.LICENSE, - method = "getLicense" - ), - @LinkRest( - name = CollectionRest.LOGO, - method = "getLogo" - ), - @LinkRest( - name = CollectionRest.MAPPED_ITEMS, - method = "getMappedItems" - ), - @LinkRest( - name = CollectionRest.PARENT_COMMUNITY, - method = "getParentCommunity" - ), - @LinkRest( - name = CollectionRest.ADMIN_GROUP, - method = "getAdminGroup" - ), - @LinkRest( - name = CollectionRest.SUBMITTERS_GROUP, - method = "getSubmittersGroup" - ), - @LinkRest( - name = CollectionRest.ITEM_READ_GROUP, - method = "getItemReadGroup" - ), - @LinkRest( - name = CollectionRest.BITSTREAM_READ_GROUP, - method = "getBitstreamReadGroup" - ), + @LinkRest(name = CollectionRest.LICENSE, method = "getLicense"), + @LinkRest(name = CollectionRest.LOGO, method = "getLogo"), + @LinkRest(name = CollectionRest.MAPPED_ITEMS, method = "getMappedItems"), + @LinkRest(name = CollectionRest.PARENT_COMMUNITY, method = "getParentCommunity"), + @LinkRest(name = CollectionRest.ADMIN_GROUP, method = "getAdminGroup"), + @LinkRest(name = CollectionRest.SUBMITTERS_GROUP, method = "getSubmittersGroup"), + @LinkRest(name = CollectionRest.ITEM_READ_GROUP, method = "getItemReadGroup"), + @LinkRest(name = CollectionRest.BITSTREAM_READ_GROUP, method = "getBitstreamReadGroup"), }) public class CollectionRest extends DSpaceObjectRest { public static final String NAME = "collection"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java index 86dc4b2c3900..08758ef9cc27 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java @@ -15,26 +15,11 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = CommunityRest.COLLECTIONS, - method = "getCollections" - ), - @LinkRest( - name = CommunityRest.LOGO, - method = "getLogo" - ), - @LinkRest( - name = CommunityRest.SUBCOMMUNITIES, - method = "getSubcommunities" - ), - @LinkRest( - name = CommunityRest.PARENT_COMMUNITY, - method = "getParentCommunity" - ), - @LinkRest( - name = CommunityRest.ADMIN_GROUP, - method = "getAdminGroup" - ) + @LinkRest(name = CommunityRest.COLLECTIONS, method = "getCollections"), + @LinkRest(name = CommunityRest.LOGO, method = "getLogo"), + @LinkRest(name = CommunityRest.SUBCOMMUNITIES, method = "getSubcommunities"), + @LinkRest(name = CommunityRest.PARENT_COMMUNITY, method = "getParentCommunity"), + @LinkRest(name = CommunityRest.ADMIN_GROUP, method = "getAdminGroup") }) public class CommunityRest extends DSpaceObjectRest { public static final String NAME = "community"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java index 7b4c683322a9..0c4f4d7b290b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java @@ -20,10 +20,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = EPersonRest.GROUPS, - method = "getGroups" - ) + @LinkRest(name = EPersonRest.GROUPS, method = "getGroups") }) public class EPersonRest extends DSpaceObjectRest { public static final String NAME = "eperson"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java index f777b3a29c18..f62ef2767ea9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java @@ -15,10 +15,7 @@ * Refer to {@link org.dspace.content.EntityType} for explanation of the properties */ @LinksRest(links = { - @LinkRest( - name = EntityTypeRest.RELATION_SHIP_TYPES, - method = "getEntityTypeRelationship" - ) + @LinkRest(name = EntityTypeRest.RELATION_SHIP_TYPES, method = "getEntityTypeRelationship") }) public class EntityTypeRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java index 639c2cf72e2e..2cd3712bdd53 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java @@ -13,10 +13,7 @@ * This class serves as a REST representation for an External Source */ @LinksRest(links = { - @LinkRest( - name = ExternalSourceRest.ENTITY_TYPES, - method = "getSupportedEntityTypes" - ) + @LinkRest(name = ExternalSourceRest.ENTITY_TYPES, method = "getSupportedEntityTypes") }) public class ExternalSourceRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java index 3f60b2d61fd6..332f5a5055fb 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java @@ -18,18 +18,9 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @LinksRest(links = { - @LinkRest( - name = GroupRest.SUBGROUPS, - method = "getGroups" - ), - @LinkRest( - name = GroupRest.EPERSONS, - method = "getMembers" - ), - @LinkRest( - name = GroupRest.OBJECT, - method = "getParentObject" - ) + @LinkRest(name = GroupRest.SUBGROUPS, method = "getGroups"), + @LinkRest(name = GroupRest.EPERSONS, method = "getMembers"), + @LinkRest(name = GroupRest.OBJECT, method = "getParentObject") }) public class GroupRest extends DSpaceObjectRest { public static final String NAME = "group"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java index 1254ef8f9372..bd0530be5dc4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java @@ -17,42 +17,15 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = ItemRest.ACCESS_STATUS, - method = "getAccessStatus" - ), - @LinkRest( - name = ItemRest.BUNDLES, - method = "getBundles" - ), - @LinkRest( - name = ItemRest.IDENTIFIERS, - method = "getIdentifiers" - ), - @LinkRest( - name = ItemRest.MAPPED_COLLECTIONS, - method = "getMappedCollections" - ), - @LinkRest( - name = ItemRest.OWNING_COLLECTION, - method = "getOwningCollection" - ), - @LinkRest( - name = ItemRest.RELATIONSHIPS, - method = "getRelationships" - ), - @LinkRest( - name = ItemRest.VERSION, - method = "getItemVersion" - ), - @LinkRest( - name = ItemRest.TEMPLATE_ITEM_OF, - method = "getTemplateItemOf" - ), - @LinkRest( - name = ItemRest.THUMBNAIL, - method = "getThumbnail" - ) + @LinkRest(name = ItemRest.ACCESS_STATUS, method = "getAccessStatus"), + @LinkRest(name = ItemRest.BUNDLES, method = "getBundles"), + @LinkRest(name = ItemRest.IDENTIFIERS, method = "getIdentifiers"), + @LinkRest(name = ItemRest.MAPPED_COLLECTIONS, method = "getMappedCollections"), + @LinkRest(name = ItemRest.OWNING_COLLECTION, method = "getOwningCollection"), + @LinkRest(name = ItemRest.RELATIONSHIPS, method = "getRelationships"), + @LinkRest(name = ItemRest.VERSION, method = "getItemVersion"), + @LinkRest(name = ItemRest.TEMPLATE_ITEM_OF, method = "getTemplateItemOf"), + @LinkRest(name = ItemRest.THUMBNAIL, method = "getThumbnail") }) public class ItemRest extends DSpaceObjectRest { public static final String NAME = "item"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java index 02e0f4706208..abfc0a31dc6a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java @@ -38,7 +38,7 @@ public class OrcidHistoryRest extends BaseObjectRest { private String responseMessage; - public OrcidHistoryRest(){} + public OrcidHistoryRest() {} @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java index c32c2c95783a..320558f94671 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java @@ -17,10 +17,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = PoolTaskRest.STEP, - method = "getStep" - ) + @LinkRest(name = PoolTaskRest.STEP, method = "getStep") }) public class PoolTaskRest extends BaseObjectRest { public static final String NAME = "pooltask"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java index 8216e1617195..f7d8088d8a30 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java @@ -21,18 +21,9 @@ * This class serves as a REST representation for the {@link Process} class */ @LinksRest(links = { - @LinkRest( - name = ProcessRest.FILES, - method = "getFilesFromProcess" - ), - @LinkRest( - name = ProcessRest.FILE_TYPES, - method = "getFileTypesFromProcess" - ), - @LinkRest( - name = ProcessRest.OUTPUT, - method = "getOutputFromProcess" - ) + @LinkRest(name = ProcessRest.FILES, method = "getFilesFromProcess"), + @LinkRest(name = ProcessRest.FILE_TYPES, method = "getFileTypesFromProcess"), + @LinkRest(name = ProcessRest.OUTPUT, method = "getOutputFromProcess") }) public class ProcessRest extends BaseObjectRest { public static final String NAME = "process"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java index dd35a0726e9d..d4050cda9a34 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java @@ -19,10 +19,7 @@ * Refer to {@link org.dspace.content.Relationship} for explanation about the properties */ @LinksRest(links = { - @LinkRest( - name = RelationshipRest.RELATIONSHIP_TYPE, - method = "getRelationshipType" - ) + @LinkRest(name = RelationshipRest.RELATIONSHIP_TYPE, method = "getRelationshipType") }) public class RelationshipRest extends BaseObjectRest { public static final String NAME = "relationship"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java index 4224cfeeb924..1ebde12e854f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java @@ -20,8 +20,8 @@ * */ @LinksRest(links = { - @LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"), - @LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson") + @LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"), + @LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson") }) public class ResearcherProfileRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java index e93e131aadb7..51d926082e33 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java @@ -13,14 +13,8 @@ * The REST object for the {@link org.dspace.versioning.VersionHistory} object */ @LinksRest(links = { - @LinkRest( - name = VersionHistoryRest.VERSIONS, - method = "getVersions" - ), - @LinkRest( - name = VersionHistoryRest.DRAFT_VERSION, - method = "getDraftVersion" - ) + @LinkRest(name = VersionHistoryRest.VERSIONS, method = "getVersions"), + @LinkRest(name = VersionHistoryRest.DRAFT_VERSION, method = "getDraftVersion") }) public class VersionHistoryRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java index abdc64295fdc..b4a7ce2b016c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java @@ -16,14 +16,8 @@ * The REST object for the {@link org.dspace.versioning.Version} objects */ @LinksRest(links = { - @LinkRest( - name = VersionRest.VERSION_HISTORY, - method = "getVersionHistory" - ), - @LinkRest( - name = VersionRest.ITEM, - method = "getVersionItem" - ) + @LinkRest(name = VersionRest.VERSION_HISTORY, method = "getVersionHistory"), + @LinkRest(name = VersionRest.ITEM, method = "getVersionItem") }) public class VersionRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java index 30e5eb71cbff..964c67dbbe8e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java @@ -18,9 +18,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = VocabularyEntryDetailsRest.PARENT, method = "getParent"), - @LinkRest(name = VocabularyEntryDetailsRest.CHILDREN, method = "getChildren") - }) + @LinkRest(name = VocabularyEntryDetailsRest.PARENT, method = "getParent"), + @LinkRest(name = VocabularyEntryDetailsRest.CHILDREN, method = "getChildren") +}) public class VocabularyEntryDetailsRest extends BaseObjectRest { public static final String PLURAL_NAME = "vocabularyEntryDetails"; public static final String NAME = "vocabularyEntryDetail"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java index cc848b945b2f..45d13076acb2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java @@ -15,9 +15,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = VocabularyRest.ENTRIES, - method = "filter" - ), + @LinkRest(name = VocabularyRest.ENTRIES, method = "filter"), }) public class VocabularyRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java index 7c2de7071bde..9ea4e2ae8c22 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java @@ -18,14 +18,8 @@ * @author Maria Verdonck (Atmire) on 11/12/2019 */ @LinksRest(links = { - @LinkRest( - name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, - method = "getCollections" - ), - @LinkRest( - name = WorkflowDefinitionRest.STEPS, - method = "getSteps" - ) + @LinkRest(name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, method = "getCollections"), + @LinkRest(name = WorkflowDefinitionRest.STEPS, method = "getSteps") }) public class WorkflowDefinitionRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java index 4a840a0bb77b..e94cfd54644d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java @@ -15,22 +15,10 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkflowItemRest.STEP, - method = "getStep" - ), - @LinkRest( - name = WorkflowItemRest.SUBMITTER, - method = "getWorkflowItemSubmitter" - ), - @LinkRest( - name = WorkflowItemRest.ITEM, - method = "getWorkflowItemItem" - ), - @LinkRest( - name = WorkflowItemRest.COLLECTION, - method = "getWorkflowItemCollection" - ) + @LinkRest(name = WorkflowItemRest.STEP, method = "getStep"), + @LinkRest(name = WorkflowItemRest.SUBMITTER, method = "getWorkflowItemSubmitter"), + @LinkRest(name = WorkflowItemRest.ITEM, method = "getWorkflowItemItem"), + @LinkRest(name = WorkflowItemRest.COLLECTION, method = "getWorkflowItemCollection") }) public class WorkflowItemRest extends AInprogressSubmissionRest { public static final String NAME = "workflowitem"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java index 648cffbca80d..91128fd05a7d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java @@ -18,10 +18,7 @@ * @author Maria Verdonck (Atmire) on 10/01/2020 */ @LinksRest(links = { - @LinkRest( - name = WorkflowStepRest.ACTIONS, - method = "getActions" - ), + @LinkRest(name = WorkflowStepRest.ACTIONS, method = "getActions"), }) public class WorkflowStepRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java index b40d1a4494f4..7693771fb9f2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java @@ -15,22 +15,10 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkspaceItemRest.SUPERVISION_ORDERS, - method = "getSupervisionOrders" - ), - @LinkRest( - name = WorkspaceItemRest.SUBMITTER, - method = "getWorkspaceItemSubmitter" - ), - @LinkRest( - name = WorkspaceItemRest.ITEM, - method = "getWorkspaceItemItem" - ), - @LinkRest( - name = WorkspaceItemRest.COLLECTION, - method = "getWorkspaceItemCollection" - ) + @LinkRest(name = WorkspaceItemRest.SUPERVISION_ORDERS, method = "getSupervisionOrders"), + @LinkRest(name = WorkspaceItemRest.SUBMITTER, method = "getWorkspaceItemSubmitter"), + @LinkRest(name = WorkspaceItemRest.ITEM, method = "getWorkspaceItemItem"), + @LinkRest(name = WorkspaceItemRest.COLLECTION, method = "getWorkspaceItemCollection") }) public class WorkspaceItemRest extends AInprogressSubmissionRest { public static final String NAME = "workspaceitem"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java index 8739f6b8d57f..856e36457057 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java @@ -13,7 +13,7 @@ */ public class RegexUtils { - private RegexUtils(){} + private RegexUtils() {} /** * Regular expression in the request mapping to accept UUID as identifier diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java index a154091a2eff..2a4cee8375be 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java @@ -17,7 +17,7 @@ public class RegistrationMatcher { - private RegistrationMatcher(){} + private RegistrationMatcher() {} public static Matcher matchRegistration(String email, UUID epersonUuid) { return allOf( From 5d7b42603d5d68bbffef0226fca3116e7029e401 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 09:34:02 -0600 Subject: [PATCH 387/479] Add newly required "should-stop" flag to errorprone config. See https://errorprone.info/docs/installation --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index f496ed7e879d..f3b09f3a9dd9 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ true -XDcompilePolicy=simple + --should-stop=ifError=FLOW -Xplugin:ErrorProne -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED From f7ee509423591c33621e9b6abf80135c08f16522 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 09:44:56 -0600 Subject: [PATCH 388/479] Fix duplicate code warning from errorprone. This "else if" clause is the same as the "else" and can be removed --- .../src/main/java/org/dspace/discovery/SolrServiceImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 9339b574b578..34efe96ae7f8 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -1445,8 +1445,6 @@ protected String transformFacetField(DiscoverFacetField facetFieldConfig, String } else { return field + "_acid"; } - } else if (facetFieldConfig.getType().equals(DiscoveryConfigurationParameters.TYPE_STANDARD)) { - return field; } else { return field; } From 1581c737e2a84fa217c334b2022b80f2fdd2ff15 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 30 Oct 2024 14:49:04 -0500 Subject: [PATCH 389/479] Update jsoneditor.js reference in Hal Browser --- dspace-server-webapp/src/main/webapp/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/webapp/index.html b/dspace-server-webapp/src/main/webapp/index.html index c780286107d8..0b80f806767e 100644 --- a/dspace-server-webapp/src/main/webapp/index.html +++ b/dspace-server-webapp/src/main/webapp/index.html @@ -321,7 +321,7 @@

    Embedded Resources

    - +