diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7d63f45982c1..39a6f41429fd 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 2a5abfceb1d8..3a563c6fa39c 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: 17
distribution: 'temurin'
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 9f1e407cff4b..a9ff8760e763 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:
@@ -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
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..0c7538a8f113 100644
--- a/.github/workflows/reusable-docker-build.yml
+++ b/.github/workflows/reusable-docker-build.yml
@@ -68,9 +68,9 @@ env:
# 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)
+ # Current DSpace branches (and architecture) which are deployed to demo.dspace.org & sandbox.dspace.org respectively
DEPLOY_DEMO_BRANCH: 'dspace-7_x'
+ DEPLOY_SANDBOX_BRANCH: 'dspace-8.0-testathon'
DEPLOY_ARCH: 'linux/amd64'
jobs:
@@ -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
@@ -152,9 +160,9 @@ 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 }}
+ name: digests-${{ inputs.build_id }}-${{ env.ARCH_NAME }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
@@ -166,13 +174,13 @@ jobs:
!matrix.isPR &&
env.REDEPLOY_SANDBOX_URL != '' &&
matrix.arch == env.DEPLOY_ARCH &&
- github.ref_name == github.event.repository.default_branch
+ github.ref_name == env.DEPLOY_SANDBOX_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)
+ - name: Redeploy demo.dspace.org (based on maintenance branch)
if: |
!matrix.isPR &&
env.REDEPLOY_DEMO_URL != '' &&
@@ -192,10 +200,12 @@ 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
+ # 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
diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml
index e9544d3274fa..5c094dee2a2e 100644
--- a/dspace-api/pom.xml
+++ b/dspace-api/pom.xml
@@ -458,7 +458,12 @@
commons-validator
- com.sun.mail
+ jakarta.mail
+ jakarta.mail-api
+ provided
+
+
+ org.eclipse.angusjakarta.mail
diff --git a/dspace-api/src/main/java/org/dspace/app/ldn/model/Citation.java b/dspace-api/src/main/java/org/dspace/app/ldn/model/Citation.java
index c6629f5e7b91..7abe5c8ef44a 100644
--- a/dspace-api/src/main/java/org/dspace/app/ldn/model/Citation.java
+++ b/dspace-api/src/main/java/org/dspace/app/ldn/model/Citation.java
@@ -18,7 +18,7 @@ public class Citation extends Base {
@JsonProperty("ietf:cite-as")
private String ietfCiteAs;
- @JsonProperty("url")
+ @JsonProperty("ietf:item")
private Url url;
/**
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/main/java/org/dspace/content/DSpaceObjectServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java
index 2119959073f0..ba4bce0de7d3 100644
--- a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java
@@ -629,6 +629,7 @@ public void update(Context context, T dso) throws SQLException, AuthorizeExcepti
// E.g. for an Author relationship,
// the place should be updated using the same principle as dc.contributor.author.
StringUtils.startsWith(metadataValue.getAuthority(), Constants.VIRTUAL_AUTHORITY_PREFIX)
+ && metadataValue instanceof RelationshipMetadataValue
&& ((RelationshipMetadataValue) metadataValue).isUseForPlace()
) {
int mvPlace = getMetadataValuePlace(fieldToLastPlace, metadataValue);
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..9b28203827e0 100644
--- a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java
@@ -8,6 +8,7 @@
package org.dspace.content;
import java.sql.SQLException;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -51,12 +52,7 @@ public Entity findByItemId(Context context, UUID itemId, Integer limit, Integer
@Override
public EntityType getType(Context context, Entity entity) throws SQLException {
Item item = entity.getItem();
- List list = itemService.getMetadata(item, "dspace", "entity", "type", Item.ANY, false);
- if (!list.isEmpty()) {
- return entityTypeService.findByEntityType(context, list.get(0).getValue());
- } else {
- return null;
- }
+ return itemService.getEntityType(context, item);
}
@Override
@@ -103,7 +99,12 @@ public List getAllRelationshipTypes(Context context, Entity en
@Override
public List getAllRelationshipTypes(Context context, Entity entity, Integer limit, Integer offset)
throws SQLException {
- return relationshipTypeService.findByEntityType(context, this.getType(context, entity), limit, offset);
+ EntityType entityType = this.getType(context, entity);
+ if (entityType != null) {
+ return relationshipTypeService.findByEntityType(context, entityType, limit, offset);
+ } else {
+ return Collections.emptyList();
+ }
}
@Override
@@ -115,7 +116,12 @@ public List getLeftRelationshipTypes(Context context, Entity e
@Override
public List getLeftRelationshipTypes(Context context, Entity entity, boolean isLeft,
Integer limit, Integer offset) throws SQLException {
- return relationshipTypeService.findByEntityType(context, this.getType(context, entity), isLeft, limit, offset);
+ EntityType entityType = this.getType(context, entity);
+ if (entityType != null) {
+ return relationshipTypeService.findByEntityType(context, entityType, isLeft, limit, offset);
+ } else {
+ return Collections.emptyList();
+ }
}
@Override
@@ -128,7 +134,12 @@ public List getRightRelationshipTypes(Context context, Entity
public List getRightRelationshipTypes(Context context, Entity entity, boolean isLeft,
Integer limit, Integer offset) throws SQLException {
- return relationshipTypeService.findByEntityType(context, this.getType(context, entity), isLeft, limit, offset);
+ EntityType entityType = this.getType(context, entity);
+ if (entityType != null) {
+ return relationshipTypeService.findByEntityType(context, entityType, isLeft, limit, offset);
+ } else {
+ return Collections.emptyList();
+ }
}
@Override
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/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
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/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java
index cd18b3a0bd19..d954cc3b26ca 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;
}
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);
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..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
@@ -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,11 +51,21 @@ 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 {
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);
@@ -75,6 +88,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 +106,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 +139,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));
}
diff --git a/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java b/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java
index 305ebff69710..11fbbb9f9119 100644
--- a/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java
+++ b/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java
@@ -145,6 +145,7 @@ public void testGetAllRelationshipTypes() throws Exception {
// Declare objects utilized for this test
Item item = mock(Item.class);
Entity entity = mock(Entity.class);
+ EntityType entityType = mock(EntityType.class);
RelationshipType relationshipType = mock(RelationshipType.class);
relationshipType.setLeftType(leftType);
relationshipType.setLeftType(rightType);
@@ -156,7 +157,8 @@ public void testGetAllRelationshipTypes() throws Exception {
// Mock the state of objects utilized in getAllRelationshipTypes()
// to meet the success criteria of the invocation
when(entity.getItem()).thenReturn(item);
- when(relationshipTypeService.findByEntityType(context, entityService.getType(context, entity), -1, -1))
+ when(entityService.getType(context, entity)).thenReturn(entityType);
+ when(relationshipTypeService.findByEntityType(context, entityType, -1, -1))
.thenReturn(relationshipTypeList);
// The relation(s) reported from our mocked Entity should match our relationshipList
@@ -181,10 +183,9 @@ public void testGetLeftRelationshipTypes() throws Exception {
// Mock the state of objects utilized in getLeftRelationshipTypes()
// to meet the success criteria of the invocation
- when(itemService.getMetadata(item, "dspace", "entity", "type", Item.ANY, false)).thenReturn(metsList);
when(entity.getItem()).thenReturn(item);
when(entityService.getType(context, entity)).thenReturn(entityType);
- when(relationshipTypeService.findByEntityType(context, entityService.getType(context, entity), true, -1, -1))
+ when(relationshipTypeService.findByEntityType(context, entityType, true, -1, -1))
.thenReturn(relationshipTypeList);
// The left relationshipType(s) reported from our mocked Entity should match our relationshipList
@@ -209,10 +210,9 @@ public void testGetRightRelationshipTypes() throws Exception {
// Mock the state of objects utilized in getRightRelationshipTypes()
// to meet the success criteria of the invocation
- when(itemService.getMetadata(item, "dspace", "entity", "type", Item.ANY, false)).thenReturn(metsList);
when(entity.getItem()).thenReturn(item);
when(entityService.getType(context, entity)).thenReturn(entityType);
- when(relationshipTypeService.findByEntityType(context, entityService.getType(context, entity), false, -1, -1))
+ when(relationshipTypeService.findByEntityType(context, entityType, false, -1, -1))
.thenReturn(relationshipTypeList);
// The right relationshipType(s) reported from our mocked Entity should match our relationshipList
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/DSpaceCsrfTokenRequestHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/DSpaceCsrfTokenRequestHandler.java
deleted file mode 100644
index f8af92fa5868..000000000000
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/DSpaceCsrfTokenRequestHandler.java
+++ /dev/null
@@ -1,66 +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.security;
-
-import java.util.function.Supplier;
-
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import org.springframework.security.web.csrf.CsrfToken;
-import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
-import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
-import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
-import org.springframework.util.StringUtils;
-
-/**
- * A custom Spring Security CsrfTokenRequestAttributeHandler which uses the Spring Security BREACH protection
- * (provided by XorCsrfTokenRequestAttributeHandler) *only* when the CSRF token is sent as a "_csrf" request parameter.
- * In all other scenarios, the CsrfTokenRequestAttributeHandler is used instead.
- *
- * NOTE: The DSpace UI always sends the CSRF Token as a request header. It does NOT send it as a "_csrf" request
- * paramter. So, this BREACH protection would ONLY be triggered for custom clients (not the DSpace UI).
- * Therefore, if using this custom class becomes problematic, we could revert to using the default
- * CsrfTokenRequestAttributeHandler without any negative impact on the DSpace UI.
- *
- * This code is copied from the example "SpaCsrfTokenRequestHandler" (for single page applications) from the Spring
- * Security docs at
- * https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#csrf-integration-javascript-spa-configuration
- */
-public final class DSpaceCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
- private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
-
- @Override
- public void handle(HttpServletRequest request, HttpServletResponse response, Supplier csrfToken) {
- /*
- * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
- * the CsrfToken when it is rendered in the response body.
- * NOTE: This should never occur from the DSpace UI, so it is only applicable for custom clients.
- */
- this.delegate.handle(request, response, csrfToken);
- }
-
- @Override
- public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
- /*
- * If the request contains a request header, use CsrfTokenRequestAttributeHandler
- * to resolve the CsrfToken. This applies to the DSpace UI which always includes
- * the raw CsrfToken in an HTTP Header.
- */
- if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
- return super.resolveCsrfTokenValue(request, csrfToken);
- }
- /*
- * In all other cases (e.g. if the request contains a request parameter), use
- * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
- * when a server-side rendered form includes the _csrf request parameter as a
- * hidden input.
- * NOTE: This should never occur from the DSpace UI, so it is only applicable for custom clients.
- */
- return this.delegate.resolveCsrfTokenValue(request, csrfToken);
- }
-}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
index 6fb8f285d1a4..af7116a2bea5 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
@@ -29,6 +29,7 @@
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.csrf.CsrfTokenRepository;
+import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/**
@@ -112,7 +113,12 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.csrf((csrf) -> csrf
.csrfTokenRepository(this.csrfTokenRepository())
.sessionAuthenticationStrategy(this.dSpaceCsrfAuthenticationStrategy())
- .csrfTokenRequestHandler(new DSpaceCsrfTokenRequestHandler()))
+ // Disable SpringSecurity BREACH protection, as this is not working well with Cookie-based storage.
+ // When enabled, BREACH protection causes the CSRF token to grow in size until UI errors occur.
+ // See https://github.com/DSpace/DSpace/issues/9450
+ // NOTE: DSpace doesn't need BREACH protection as it's only necessary when sending the token via a
+ // request attribute (e.g. "_csrf") which the DSpace UI never does.
+ .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()))
.exceptionHandling((exceptionHandling) -> exceptionHandling
// Return 401 on authorization failures with a correct WWWW-Authenticate header
.authenticationEntryPoint(new DSpace401AuthenticationEntryPoint(restAuthenticationService))
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/NotifyServiceRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/NotifyServiceRestRepositoryIT.java
index 653850331a61..5c6d394ffda0 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/NotifyServiceRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/NotifyServiceRestRepositoryIT.java
@@ -1266,6 +1266,8 @@ public void NotifyServiceInboundPatternConstraintReplaceOperationBadRequestTest(
.andExpect(status().isBadRequest());
}
+ // Temporarily disable. See: https://github.com/DSpace/DSpace/issues/9457
+ @Ignore
@Test
public void NotifyServiceInboundPatternConstraintRemoveOperationTest() throws Exception {
@@ -1330,6 +1332,8 @@ public void NotifyServiceInboundPatternConstraintRemoveOperationTest() throws Ex
)));
}
+ // Temporarily disable. See: https://github.com/DSpace/DSpace/issues/9457
+ @Ignore
@Test
public void NotifyServiceInboundPatternConstraintRemoveOperationBadRequestTest() throws Exception {
diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_announce_endorsement.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_announce_endorsement.json
index 828757c4c1b9..e266565d2c8b 100644
--- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_announce_endorsement.json
+++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_announce_endorsement.json
@@ -12,7 +12,7 @@
"id": "<
- com.sun.mail
+ jakarta.mail
+ jakarta.mail-api
+ provided
+
+
+ org.eclipse.angusjakarta.mail
diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml
index 1e799449898d..c5a2c02956d0 100644
--- a/dspace-swordv2/pom.xml
+++ b/dspace-swordv2/pom.xml
@@ -45,6 +45,20 @@
org.glassfish.jersey.corejersey-common
+
+
+ org.apache.geronimo.specs
+ geronimo-activation_1.1_spec
+
+
+ org.apache.geronimo.specs
+ geronimo-javamail_1.4_spec
+
+
+ org.apache.geronimo.specs
+ geronimo-stax-api_1.0_spec
+
diff --git a/dspace/config/ldn/request-endorsement b/dspace/config/ldn/request-endorsement
index e885bf88efab..443a7029159f 100644
--- a/dspace/config/ldn/request-endorsement
+++ b/dspace/config/ldn/request-endorsement
@@ -26,7 +26,7 @@
"id": "${params[5]}",
"ietf:cite-as": "${params[6]}",
"type": "sorg:AboutPage",
- "url": {
+ "ietf:item": {
"id": "${params[7]}",
"mediaType": "${params[8]}",
"type": [
diff --git a/dspace/config/ldn/request-ingest b/dspace/config/ldn/request-ingest
index 82bd9a85d90c..701f3929c826 100644
--- a/dspace/config/ldn/request-ingest
+++ b/dspace/config/ldn/request-ingest
@@ -26,7 +26,7 @@
"id": "${params[5]}",
"ietf:cite-as": "${params[6]}",
"type": "sorg:AboutPage",
- "url": {
+ "ietf:item": {
"id": "${params[7]}",
"mediaType": "${params[8]}",
"type": [
diff --git a/dspace/config/ldn/request-review b/dspace/config/ldn/request-review
index 24c1ac831968..01f177b6cc18 100644
--- a/dspace/config/ldn/request-review
+++ b/dspace/config/ldn/request-review
@@ -26,7 +26,7 @@
"id": "${params[5]}",
"ietf:cite-as": "${params[6]}",
"type": "sorg:AboutPage",
- "url": {
+ "ietf:item": {
"id": "${params[7]}",
"mediaType": "${params[8]}",
"type": [
diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml
index ed6aacdbd74a..659b7796dd97 100644
--- a/dspace/config/submission-forms.xml
+++ b/dspace/config/submission-forms.xml
@@ -163,7 +163,7 @@
dropdownSelect the type of content of the item.
-
+ You must choose at least one type.
@@ -366,7 +366,7 @@
Select the type(s) of content of the item. To select more than one value in the list, you may
have to hold down the "CTRL" or "Shift" key.
-
+ You must choose at least one type.
@@ -1041,7 +1041,7 @@
Select the type(s) of content of the item. To select more than one value in the list, you may
have to hold down the "CTRL" or "Shift" key.
-
+ You must choose at least one type.
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
diff --git a/pom.xml b/pom.xml
index 3fd890f6c305..f57bd812f96d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,8 +34,8 @@
2.16.02.16.02.1.1
- 4.0.0
- 4.0.1
+ 4.0.2
+ 4.0.51.1.19.4.54.v20240208
@@ -374,7 +374,7 @@
false
-
+
https://docs.spring.io/spring-framework/docs/current/javadoc-api/
https://docs.spring.io/spring-security/site/docs/current/api/
@@ -1494,7 +1494,7 @@
org.apache.commonscommons-compress
- 1.23.0
+ 1.26.0org.apache.commons
@@ -1522,9 +1522,22 @@
2.12.5
- com.sun.mail
+ jakarta.activation
+ jakarta.activation-api
+ 2.1.3
+
+
+
+ jakarta.mail
+ jakarta.mail-api
+ 2.1.3
+ provided
+
+
+
+ org.eclipse.angusjakarta.mail
- 2.0.1
+ 2.0.3jakarta.servlet