diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b5068a31792..f7239f819325 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,24 +45,15 @@ 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 }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 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' #- name: Install Grobid (only IT) # run: ./dspace-api/src/test/data/dspaceFolder/bin/install_grobid.sh @@ -83,14 +74,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' @@ -105,11 +96,11 @@ 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 - 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. @@ -117,10 +108,14 @@ 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 + 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 attempt_delay: 30000 diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml index 9e6dcc0b23af..1e3d835e2713 100644 --- a/.github/workflows/codescan.yml +++ b/.github/workflows/codescan.yml @@ -35,11 +35,11 @@ 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 - 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/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..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@v1.6.2 + uses: toshimaru/auto-author-assign@v2.1.0 diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml new file mode 100644 index 000000000000..12aa0cfe2864 --- /dev/null +++ b/.github/workflows/reusable-docker-build.yml @@ -0,0 +1,235 @@ +# +# 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 + 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 + 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-8_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: + # 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 + + # 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: + 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), + # 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@v4 + with: + name: digests-${{ inputs.build_id }}-${{ env.ARCH_NAME }} + 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 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 + + # 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@v4 + with: + 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 + + - 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 }} diff --git a/Dockerfile b/Dockerfile index 9c32ecb50cd4..ee48dec5083c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,12 +20,15 @@ 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 # 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..53040a2fad89 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 @@ -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' @@ -45,10 +45,15 @@ 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 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/* diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index a55b323339dc..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 @@ -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 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 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/docker-compose-cli.yml b/docker-compose-cli.yml index 9c66fed6835b..d6a194617e02 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -1,5 +1,9 @@ -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 +30,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..23fce37db245 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.7' networks: dspacenet: ipam: @@ -36,7 +35,7 @@ services: depends_on: - dspacedb networks: - dspacenet: + - dspacenet ports: - published: 8080 target: 8080 @@ -89,8 +88,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-api/pom.xml b/dspace-api/pom.xml index db1b4d2ac2aa..ee31a039f4aa 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -495,12 +495,6 @@ jaxen jaxen - - - xom - xom - - org.jdom @@ -534,7 +528,7 @@ org.hamcrest - hamcrest-all + hamcrest test @@ -592,6 +586,12 @@ solr-core test ${solr.client.version} + + + com.google.j2objc + j2objc-annotations + + org.apache.lucene @@ -626,7 +626,7 @@ com.maxmind.geoip2 geoip2 - 2.11.0 + 2.17.0 org.apache.ant @@ -635,7 +635,7 @@ dnsjava dnsjava - 2.1.7 + 2.1.9 @@ -671,7 +671,7 @@ org.flywaydb flyway-core - 8.4.4 + 8.5.13 @@ -809,7 +809,7 @@ com.opencsv opencsv - 5.6 + 5.9 @@ -827,7 +827,7 @@ org.apache.bcel bcel - 6.6.0 + 6.7.0 test @@ -884,6 +884,13 @@ eu.openaire funders-model 2.0.0 + + + + org.javassist + javassist + + @@ -927,32 +934,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 @@ -962,7 +969,7 @@ org.xmlunit xmlunit-core - 2.8.0 + 2.10.0 test @@ -988,7 +995,7 @@ org.scala-lang scala-library - 2.13.9 + 2.13.11 test 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(); } 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 503fb90df1d7..50572457dffb 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 @@ -827,8 +827,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); } } @@ -1123,8 +1125,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/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 255f4bdcbb15..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 @@ -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,24 +1802,25 @@ 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 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); - rp.setGroup(g); + rp.setRpType(rpType); resourcePolicyService.update(c, rp); } else { @@ -1957,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 { @@ -2233,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); @@ -2249,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-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); + } } } } diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java index 974dc784bd4f..a6ba9fde88d9 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java @@ -180,6 +180,7 @@ public void applyFiltersItem(Context c, Item item) throws Exception { } // clear item objects from context cache and internal cache c.uncacheEntity(currentItem); + // commit after each item to release DB resources c.commit(); currentItem = null; } 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 44fc07694cd3..958013263462 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,36 +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()); - context.uncacheEntity(indexableObject.get().getIndexedObject()); - } 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); } } } @@ -142,6 +123,41 @@ 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); + + 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()); + context.uncacheEntity(indexableObject.get().getIndexedObject()); + } 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/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 6c7ee651d930..3fd7089de705 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 @@ -22,7 +22,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.servlet.ServletException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; @@ -160,10 +159,9 @@ 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 collection collection - * @return DC input set + * @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 getInputsByCollection(Collection collection) throws DCInputsReaderException { diff --git a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java index 514143c93ea0..bff741b5ca42 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java @@ -155,7 +155,7 @@ protected SyndicationFeed getResults(Context context, String format, String quer format = "atom_1.0"; } - SyndicationFeed feed = new SyndicationFeed(labels.get(SyndicationFeed.MSG_UITYPE)); + SyndicationFeed feed = new SyndicationFeed(); feed.populate(null, context, scope, results, labels); feed.setType(format); feed.addModule(openSearchMarkup(query, totalResults, start, pageSize)); 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 402059493976..0d48f7c1d480 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,9 +11,11 @@ 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; +import java.util.Optional; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; @@ -28,7 +30,6 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.core.Context; -import org.dspace.discovery.SearchServiceException; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.services.RequestService; import org.dspace.services.factory.DSpaceServicesFactory; @@ -106,6 +107,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 @@ -147,6 +155,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); @@ -165,7 +174,8 @@ public void reload() throws SubmissionConfigReaderException { */ private void buildInputs(String fileName) throws SubmissionConfigReaderException { collectionToSubmissionConfig = new HashMap(); - submitDefns = new HashMap>>(); + entityTypeToSubmissionConfig = new HashMap(); + submitDefns = new LinkedHashMap>>(); String uri = "file:" + new File(fileName).getAbsolutePath(); @@ -182,9 +192,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); @@ -311,10 +318,11 @@ public SubmissionConfig getSubmissionConfigByCollection(Collection collection) { } } - submitName = collService.getMetadataFirstValue(collection, "dspace", "entity", "type", null); + // get entity-type based configuration + submitName = getEntityTypeSubmission(collection, collService); if (submitName != null) { try { - SubmissionConfig subConfig = getSubmissionConfigByName(submitName.toLowerCase()); + SubmissionConfig subConfig = getSubmissionConfigByName(submitName); if (subConfig != null) { return subConfig; } @@ -336,6 +344,27 @@ public SubmissionConfig getSubmissionConfigByCollection(Collection collection) { + "in 'submission-map' section of 'item-submission.xml'."); } + /** + * Returns the submission name for the given collection based on the entity type. + * It checks for the {@code collection-entity-type} configuration (standard DSpace), otherwise defaults to the + * {@code entityType} in lowercase (standard CRIS). + * + * @param collection Collection of which check the submission configuration + * @param collService CollectionService + * @return Submission Definition found. + */ + private String getEntityTypeSubmission(Collection collection, CollectionService collService) { + String submitName = null; + String entityType = collService.getMetadataFirstValue(collection, "dspace", "entity", "type", Item.ANY); + if (entityType != null) { + // collection-entity-type configuration (DSpace configuration) + submitName = Optional.ofNullable(entityTypeToSubmissionConfig.get(entityType)) + // entity-type lowercase configuration (CRIS configuration) + .orElseGet(entityType::toLowerCase); + } + return submitName; + } + /** * Returns the Item Submission process config * @@ -343,8 +372,7 @@ public SubmissionConfig getSubmissionConfigByCollection(Collection collection) { * @return the SubmissionConfig representing the item submission config */ public SubmissionConfig getSubmissionConfigByName(String submitName) { - log.debug("Loading submission process config named '" + submitName - + "'"); + log.debug("Loading submission process config named '" + submitName + "'"); // check mini-cache, and return if match if (lastSubmissionConfig != null @@ -367,8 +395,8 @@ public SubmissionConfig getSubmissionConfigByName(String submitName) { log.debug("Submission process config '" + submitName + "' not in cache. Reloading from scratch."); - lastSubmissionConfig = new SubmissionConfig(StringUtils.equals(getDefaultSubmissionConfigName(), submitName), - submitName, steps); + lastSubmissionConfig = + new SubmissionConfig(StringUtils.equals(getDefaultSubmissionConfigName(), submitName), submitName, steps); log.debug("Submission process config has " + lastSubmissionConfig.getNumberOfSteps() + " steps listed."); @@ -406,7 +434,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; } @@ -456,7 +484,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 { + private void processMap(Node e) throws SAXException { NodeList nl = e.getChildNodes(); int len = nl.getLength(); for (int i = 0; i < len; i++) { @@ -475,14 +503,14 @@ 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."); } if (id != null) { collectionToSubmissionConfig.put(id, value); } else { - collectionToSubmissionConfig.putIfAbsent(entityType, value); + entityTypeToSubmissionConfig.putIfAbsent(entityType, value); } } // ignore any child node that isn't a "name-map" } diff --git a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java index c1402499c444..d37e6d802fe1 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java @@ -84,11 +84,6 @@ public class SyndicationFeed { public static final String MSG_FEED_TITLE = "feed.title"; public static final String MSG_FEED_DESCRIPTION = "general-feed.description"; public static final String MSG_METADATA = "metadata."; - public static final String MSG_UITYPE = "ui.type"; - - // UI keywords - public static final String UITYPE_XMLUI = "xmlui"; - public static final String UITYPE_JSPUI = "jspui"; // default DC fields for entry protected String defaultTitleField = "dc.title"; @@ -145,10 +140,6 @@ public class SyndicationFeed { // the feed object we are building protected SyndFeed feed = null; - // memory of UI that called us, "xmlui" or "jspui" - // affects Bitstream retrieval URL and I18N keys - protected String uiType = null; - protected HttpServletRequest request = null; protected CollectionService collectionService; @@ -157,12 +148,9 @@ public class SyndicationFeed { /** * Constructor. - * - * @param ui either "xmlui" or "jspui" */ - public SyndicationFeed(String ui) { + public SyndicationFeed() { feed = new SyndFeedImpl(); - uiType = ui; ContentServiceFactory contentServiceFactory = ContentServiceFactory.getInstance(); itemService = contentServiceFactory.getItemService(); collectionService = contentServiceFactory.getCollectionService(); @@ -518,8 +506,7 @@ protected static String getDefaultedConfiguration(String key, String dfl) { protected String urlOfBitstream(HttpServletRequest request, Bitstream logo) { String name = logo.getName(); return resolveURL(request, null) + - (uiType.equalsIgnoreCase(UITYPE_XMLUI) ? "/bitstream/id/" : "/retrieve/") + - logo.getID() + "/" + (name == null ? "" : name); + "/bitstreams/" + logo.getID() + "/download"; } protected String baseURL = null; // cache the result for null 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 b5b53963eab6..d375c5148b34 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -572,13 +572,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()); @@ -692,11 +690,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); @@ -918,7 +914,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); } 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 d633e303bf66..25875dbdb366 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; } @@ -210,9 +218,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 662b14b18b2e..f099f72d65ce 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 @@ -18,7 +18,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. @@ -27,7 +26,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/browse/ItemCountDAO.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAO.java index ab16f68d3569..6438b5745bdb 100644 --- a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAO.java @@ -13,26 +13,17 @@ /** * Interface for data access of cached community and collection item count * information - * - * @author Richard Jones */ public interface ItemCountDAO { - /** - * Set the DSpace Context to use during data access - * - * @param context DSpace Context - * @throws ItemCountException if count error - */ - public void setContext(Context context) throws ItemCountException; /** * Get the number of items in the given DSpaceObject container. This method will * only succeed if the DSpaceObject is an instance of either a Community or a * Collection. Otherwise it will throw an exception. * + * @param context DSpace context * @param dso Dspace Object * @return count - * @throws ItemCountException if count error */ - public int getCount(DSpaceObject dso) throws ItemCountException; + int getCount(Context context, DSpaceObject dso); } diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOFactory.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOFactory.java deleted file mode 100644 index 722be645fdaa..000000000000 --- a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOFactory.java +++ /dev/null @@ -1,67 +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.browse; - -import java.lang.reflect.InvocationTargetException; - -import org.dspace.core.Context; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; - -/** - * Factory class to allow us to load the correct DAO for registering - * item count information - * - * @author Richard Jones - * @author Ivan Masár - */ -public class ItemCountDAOFactory { - - /** - * Default constructor - */ - private ItemCountDAOFactory() { } - - /** - * Get an instance of ItemCountDAO which supports the correct storage backend - * for the specific DSpace instance. - * - * @param context DSpace Context - * @return DAO - * @throws ItemCountException if count error - */ - public static ItemCountDAO getInstance(Context context) - throws ItemCountException { - - /** Log4j logger */ - ItemCountDAO dao = null; - - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); - String className = configurationService.getProperty("ItemCountDAO.class"); - - // SOLR implementation is the default since DSpace 4.0 - if (className == null) { - dao = new ItemCountDAOSolr(); - } else { - try { - dao = (ItemCountDAO) Class.forName(className.trim()) - .getDeclaredConstructor() - .newInstance(); - } catch (ClassNotFoundException | IllegalAccessException - | InstantiationException | NoSuchMethodException - | SecurityException | IllegalArgumentException - | InvocationTargetException e) { - throw new ItemCountException("The configuration for ItemCountDAO is invalid: " + className, e); - } - } - - dao.setContext(context); - return dao; - } -} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java index d233270d813c..e4d0079fe20b 100644 --- a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java @@ -24,14 +24,12 @@ import org.dspace.discovery.SearchServiceException; import org.dspace.discovery.configuration.DiscoveryConfigurationParameters; import org.dspace.discovery.indexobject.IndexableItem; -import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.beans.factory.annotation.Autowired; /** * Discovery (Solr) driver implementing ItemCountDAO interface to look up item * count information in communities and collections. Caching operations are * intentionally not implemented because Solr already is our cache. - * - * @author Ivan Masár, Andrea Bollini */ public class ItemCountDAOSolr implements ItemCountDAO { /** @@ -39,11 +37,6 @@ public class ItemCountDAOSolr implements ItemCountDAO { */ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemCountDAOSolr.class); - /** - * DSpace context - */ - private Context context; - /** * Hold the communities item count obtained from SOLR after the first query. This only works * well if the ItemCountDAO lifecycle is bound to the request lifecycle as @@ -61,41 +54,28 @@ public class ItemCountDAOSolr implements ItemCountDAO { /** * Solr search service */ - SearchService searcher = DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(SearchService.class.getName(), SearchService.class); - - /** - * Set the dspace context to use - * - * @param context DSpace Context - * @throws ItemCountException if count error - */ - @Override - public void setContext(Context context) throws ItemCountException { - this.context = context; - } + @Autowired + protected SearchService searchService; /** * Get the count of the items in the given container. * - * @param dso Dspace Context + * @param context DSpace context + * @param dso DspaceObject * @return count - * @throws ItemCountException if count error */ @Override - public int getCount(DSpaceObject dso) throws ItemCountException { - loadCount(); - Integer val; + public int getCount(Context context, DSpaceObject dso) { + loadCount(context); + Integer val = null; if (dso instanceof Collection) { - val = collectionsCount.get(String.valueOf(((Collection) dso).getID())); + val = collectionsCount.get(dso.getID().toString()); } else if (dso instanceof Community) { - val = communitiesCount.get(String.valueOf(((Community) dso).getID())); - } else { - throw new ItemCountException("We can only count items in Communities or Collections"); + val = communitiesCount.get(dso.getID().toString()); } if (val != null) { - return val.intValue(); + return val; } else { return 0; } @@ -105,15 +85,15 @@ public int getCount(DSpaceObject dso) throws ItemCountException { * make sure that the counts are actually fetched from Solr (if haven't been * cached in a Map yet) * - * @throws ItemCountException if count error + * @param context DSpace Context */ - private void loadCount() throws ItemCountException { + private void loadCount(Context context) { if (communitiesCount != null || collectionsCount != null) { return; } - communitiesCount = new HashMap(); - collectionsCount = new HashMap(); + communitiesCount = new HashMap<>(); + collectionsCount = new HashMap<>(); DiscoverQuery query = new DiscoverQuery(); query.setFacetMinCount(1); @@ -125,11 +105,13 @@ private void loadCount() throws ItemCountException { DiscoveryConfigurationParameters.SORT.COUNT)); query.addFilterQueries("search.resourcetype:" + IndexableItem.TYPE); // count only items query.addFilterQueries("NOT(discoverable:false)"); // only discoverable + query.addFilterQueries("withdrawn:false"); // only not withdrawn + query.addFilterQueries("archived:true"); // only archived query.setMaxResults(0); - DiscoverResult sResponse = null; + DiscoverResult sResponse; try { - sResponse = searcher.search(context, query); + sResponse = searchService.search(context, query); List commCount = sResponse.getFacetResult("location.comm"); List collCount = sResponse.getFacetResult("location.coll"); for (FacetResult c : commCount) { @@ -139,8 +121,7 @@ private void loadCount() throws ItemCountException { collectionsCount.put(c.getAsFilterQuery(), (int) c.getCount()); } } catch (SearchServiceException e) { - log.error("caught exception: ", e); - throw new ItemCountException(e); + log.error("Could not initialize Community/Collection Item Counts from Solr: ", e); } } } diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountException.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountException.java deleted file mode 100644 index 533e6ecceb97..000000000000 --- a/dspace-api/src/main/java/org/dspace/browse/ItemCountException.java +++ /dev/null @@ -1,32 +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.browse; - -/** - * Exception type to handle item count specific problems - * - * @author Richard Jones - */ -public class ItemCountException extends Exception { - - public ItemCountException() { - } - - public ItemCountException(String message) { - super(message); - } - - public ItemCountException(Throwable cause) { - super(cause); - } - - public ItemCountException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java b/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java index 20c43fc37298..b5a695566976 100644 --- a/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java @@ -13,26 +13,18 @@ import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; -import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.core.Context; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.dspace.web.ContextUtil; +import org.springframework.beans.factory.annotation.Autowired; /** * This class provides a standard interface to all item counting - * operations for communities and collections. It can be run from the - * command line to prepare the cached data if desired, simply by - * running: + * operations for communities and collections. * - * java org.dspace.browse.ItemCounter - * - * It can also be invoked via its standard API. In the event that - * the data cache is not being used, this class will return direct + * In the event that the data cache is not being used, this class will return direct * real time counts of content. - * - * @author Richard Jones */ public class ItemCounter { /** @@ -40,57 +32,15 @@ public class ItemCounter { */ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemCounter.class); - /** - * DAO to use to store and retrieve data - */ - private ItemCountDAO dao; - - /** - * DSpace Context - */ - private Context context; - - /** - * This field is used to hold singular instance of a class. - * Singleton pattern is used but this class should be - * refactored to modern DSpace approach (injectible service). - */ - - private static ItemCounter instance; - + @Autowired protected ItemService itemService; + @Autowired protected ConfigurationService configurationService; - private boolean showStrengths; - private boolean useCache; - - /** - * Construct a new item counter which will use the given DSpace Context - * - * @param context current context - * @throws ItemCountException if count error - */ - public ItemCounter(Context context) throws ItemCountException { - this.context = context; - this.dao = ItemCountDAOFactory.getInstance(this.context); - this.itemService = ContentServiceFactory.getInstance().getItemService(); - this.configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - this.showStrengths = configurationService.getBooleanProperty("webui.strengths.show", false); - this.useCache = configurationService.getBooleanProperty("webui.strengths.cache", true); - } - /** - * Get the singular instance of a class. - * It creates a new instance at the first usage of this method. - * - * @return instance af a class - * @throws ItemCountException when error occurs + * Construct a new item counter */ - public static ItemCounter getInstance() throws ItemCountException { - if (instance == null) { - instance = new ItemCounter(ContextUtil.obtainCurrentRequestContext()); - } - return instance; + protected ItemCounter() { } /** @@ -103,17 +53,24 @@ public static ItemCounter getInstance() throws ItemCountException { * If it is equal to 'false' it will count the number of items * in the container in real time. * + * @param context DSpace Context * @param dso DSpaceObject - * @return count - * @throws ItemCountException when error occurs + * @return count (-1 is returned if count could not be determined or is disabled) */ - public int getCount(DSpaceObject dso) throws ItemCountException { + public int getCount(Context context, DSpaceObject dso) { + boolean showStrengths = configurationService.getBooleanProperty("webui.strengths.show", false); + boolean useCache = configurationService.getBooleanProperty("webui.strengths.cache", true); if (!showStrengths) { return -1; } if (useCache) { - return dao.getCount(dso); + // NOTE: This bean is NOT Autowired above because it's a "prototype" bean which we want to reload + // occasionally. Each time the bean reloads it will update the cached item counts. + ItemCountDAO dao = + DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName("itemCountDAO", + ItemCountDAO.class); + return dao.getCount(context, dso); } // if we make it this far, we need to manually count @@ -121,8 +78,8 @@ public int getCount(DSpaceObject dso) throws ItemCountException { try { return itemService.countItems(context, (Collection) dso); } catch (SQLException e) { - log.error("caught exception: ", e); - throw new ItemCountException(e); + log.error("Error counting number of Items in Collection {} :", dso.getID(), e); + return -1; } } @@ -130,8 +87,8 @@ public int getCount(DSpaceObject dso) throws ItemCountException { try { return itemService.countItems(context, ((Community) dso)); } catch (SQLException e) { - log.error("caught exception: ", e); - throw new ItemCountException(e); + log.error("Error counting number of Items in Community {} :", dso.getID(), e); + return -1; } } 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/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java index a993959b7843..07b4f4073634 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -500,10 +500,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 e70af09bb610..05097f2996c8 100644 --- a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java @@ -563,10 +563,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/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java index dbe2d35efe1e..eecf2ddb4633 100644 --- a/dspace-api/src/main/java/org/dspace/content/Collection.java +++ b/dspace-api/src/main/java/org/dspace/content/Collection.java @@ -29,7 +29,6 @@ import javax.persistence.Transient; import org.dspace.authorize.AuthorizeException; -import org.dspace.browse.ItemCountException; import org.dspace.content.comparator.NameAscendingComparator; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; @@ -140,6 +139,9 @@ public Bitstream getLogo() { protected void setLogo(Bitstream logo) { this.logo = logo; + if (logo != null) { + logo.setCollection(this); + } setModified(); } @@ -341,18 +343,4 @@ private CollectionService getCollectionService() { } return collectionService; } - - /** - * return count of the collection items - * - * @return int - */ - public int countArchivedItems() { - try { - return collectionService.countArchivedItems(this); - } catch (ItemCountException e) { - throw new RuntimeException(e); - } - } - } diff --git a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java index eaf26ca0501a..b4e52e8d5746 100644 --- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java @@ -35,7 +35,6 @@ import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; -import org.dspace.browse.ItemCountException; import org.dspace.browse.ItemCounter; import org.dspace.content.dao.CollectionDAO; import org.dspace.content.service.BitstreamService; @@ -136,6 +135,9 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl i @Autowired(required = true) protected WorkflowItemService workflowItemService; + @Autowired + protected ItemCounter itemCounter; + protected CollectionServiceImpl() { super(); } @@ -908,10 +910,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; } } @@ -1241,13 +1248,13 @@ public List findAllCollectionsByEntityType(Context context, String e /** * Returns total collection archived items * + * @param context DSpace Context * @param collection Collection * @return total collection archived items - * @throws ItemCountException */ @Override - public int countArchivedItems(Collection collection) throws ItemCountException { - return ItemCounter.getInstance().getCount(collection); + public int countArchivedItems(Context context, Collection collection) { + return itemCounter.getCount(context, collection); } @Override 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..4503b24cfa82 100644 --- a/dspace-api/src/main/java/org/dspace/content/Community.java +++ b/dspace-api/src/main/java/org/dspace/content/Community.java @@ -25,7 +25,6 @@ import javax.persistence.Transient; import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.dspace.browse.ItemCountException; import org.dspace.content.comparator.NameAscendingComparator; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CommunityService; @@ -123,6 +122,9 @@ public Bitstream getLogo() { void setLogo(Bitstream logo) { this.logo = logo; + if (logo != null) { + logo.setCommunity(this); + } setModified(); } @@ -264,17 +266,4 @@ private CommunityService getCommunityService() { } return communityService; } - - /** - * return count of the community items - * - * @return int - */ - public int countArchivedItems() { - try { - return communityService.countArchivedItems(this); - } catch (ItemCountException e) { - throw new RuntimeException(e); - } - } } diff --git a/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java index 61e28fae1475..4435189768a9 100644 --- a/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CommunityServiceImpl.java @@ -25,7 +25,6 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; -import org.dspace.browse.ItemCountException; import org.dspace.browse.ItemCounter; import org.dspace.content.dao.CommunityDAO; import org.dspace.content.service.BitstreamService; @@ -81,6 +80,8 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl imp protected SubscribeService subscribeService; @Autowired(required = true) protected CrisMetricsService crisMetricsService; + @Autowired + protected ItemCounter itemCounter; protected CommunityServiceImpl() { super(); @@ -696,10 +697,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; } } @@ -716,13 +722,13 @@ public int countTotal(Context context) throws SQLException { /** * Returns total community archived items * + * @param context DSpace context * @param community Community * @return total community archived items - * @throws ItemCountException */ @Override - public int countArchivedItems(Community community) throws ItemCountException { - return ItemCounter.getInstance().getCount(community); + public int countArchivedItems(Context context, Community community) { + return itemCounter.getCount(context, community); } @Override 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 f520bbfbfdce..04d6cc2b2d38 100644 --- a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java @@ -250,6 +250,20 @@ 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 { @@ -261,6 +275,13 @@ public List addMetadata(Context context, T dso, MetadataField met List values, List authorities, List confidences, Supplier placeSupplier, Integer securityLevel) 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)); + } + Collection collection = getCollection(context, dso); boolean authorityControlled = metadataAuthorityService.isAuthorityAllowed(metadataField, dso.getType(), collection); @@ -432,20 +453,26 @@ public void addAndShiftRightSecuredMetadata(Context context, T dso, String schem @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 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 6666ad6f802c..babbb9efc7f6 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java @@ -158,7 +158,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()) { 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 7cdd5e1bae51..4d3e40f56c5f 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -59,7 +59,9 @@ import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.WorkspaceItemService; +import org.dspace.content.template.TemplateItemValueService; import org.dspace.content.virtual.VirtualMetadataPopulator; +import org.dspace.content.vo.MetadataValueVO; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.LogHelper; @@ -160,6 +162,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It protected WorkspaceItemService workspaceItemService; @Autowired(required = true) protected WorkflowItemService workflowItemService; + @Autowired + private TemplateItemValueService templateItemValueService; @Autowired(required = true) protected RelationshipService relationshipService; @@ -396,9 +400,64 @@ public Item createTemplateItem(Context context, Collection collection) throws SQ + template.getID())); return template; - } else { - return collection.getTemplateItem(); } + return collection.getTemplateItem(); + } + + @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 (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 (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(); + List metadataValueFromTemplateList = templateItemValueService.value(context, item, + templateItem, aMd); + + for (MetadataValueVO metadataValueFromTemplate : metadataValueFromTemplateList) { + addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(), + metadataField.getQualifier(), aMd.getLanguage(), + metadataValueFromTemplate.getValue(), metadataValueFromTemplate.getAuthority(), + metadataValueFromTemplate.getConfidence()); + } + } + } + } + + 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 @@ -1582,8 +1641,8 @@ private void addCustomPoliciesNotInPlace(Context context, DSpaceObject dso, List } /** - * Check whether or not there is already an RP on the given dso, which has actionId={@link Constants.READ} and - * resourceTypeId={@link ResourcePolicy.TYPE_CUSTOM} + * Check whether or not there is already an RP on the given dso, which has actionId={@link Constants#READ} and + * resourceTypeId={@link ResourcePolicy#TYPE_CUSTOM} * * @param context DSpace context * @param dso DSpace object to check for custom read RP @@ -1697,6 +1756,45 @@ public Iterator findUnfilteredByMetadataField(Context context, String sche } } + /** + * Returns an iterator of Items possessing the passed metadata field, or only + * those matching the passed value, if value is not Item.ANY + * + * @param context DSpace context object + * @param schema metadata field schema + * @param element metadata field element + * @param qualifier metadata field qualifier + * @param value field value or Item.ANY to match any value + * @return an iterator over the items matching that authority value + * @throws SQLException if database error + * An exception that provides information on a database access error or other errors. + * @throws AuthorizeException if authorization error + * Exception indicating the current user of the context does not have permission + * to perform a particular action. + * @throws IOException if IO error + * A general class of exceptions produced by failed or interrupted I/O operations. + */ + @Override + public Iterator findByMetadataField(Context context, + String schema, String element, String qualifier, String value) + throws SQLException, AuthorizeException, IOException { + MetadataSchema mds = metadataSchemaService.find(context, schema); + if (mds == null) { + throw new IllegalArgumentException("No such metadata schema: " + schema); + } + MetadataField mdf = metadataFieldService.findByElement(context, mds, element, qualifier); + if (mdf == null) { + throw new IllegalArgumentException( + "No such metadata field: schema=" + schema + ", element=" + element + ", qualifier=" + qualifier); + } + + if (Item.ANY.equals(value)) { + return itemDAO.findByMetadataField(context, mdf, null, true); + } + return itemDAO.findByMetadataField(context, mdf, value, true); + } + + @Override public Iterator findByMetadataQuery(Context context, List> listFieldList, List query_op, List query_val, List collectionUuids, @@ -1854,13 +1952,13 @@ public Iterator findByIds(Context context, List ids) throws SQLExc @Override public int countItems(Context context, Collection collection) throws SQLException { - return itemDAO.countItems(context, collection, true, false); + return itemDAO.countItems(context, collection, true, false, true); } @Override public int countAllItems(Context context, Collection collection) throws SQLException { - return itemDAO.countItems(context, collection, true, false) + itemDAO.countItems(context, collection, - false, true); + return itemDAO.countItems(context, collection, true, false, true) + itemDAO.countItems(context, collection, + false, true, true); } @Override @@ -1869,7 +1967,7 @@ public int countItems(Context context, Community community) throws SQLException List collections = communityService.getAllCollections(context, community); // Now, lets count unique items across that list of collections - return itemDAO.countItems(context, collections, true, false); + return itemDAO.countItems(context, collections, true, false, true); } @Override @@ -1878,16 +1976,21 @@ public int countAllItems(Context context, Community community) throws SQLExcepti List collections = communityService.getAllCollections(context, community); // Now, lets count unique items across that list of collections - return itemDAO.countItems(context, collections, true, false) + itemDAO.countItems(context, collections, - false, true); + return itemDAO.countItems(context, collections, true, false, true) + itemDAO.countItems(context, collections, + false, false, true); } @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; } } @@ -1910,19 +2013,19 @@ public int countTotal(Context context) throws SQLException { @Override public int countNotArchivedItems(Context context) throws SQLException { // return count of items not in archive and also not withdrawn - return itemDAO.countItems(context, false, false); + return itemDAO.countItems(context, false, false, true); } @Override public int countArchivedItems(Context context) throws SQLException { // return count of items in archive and also not withdrawn - return itemDAO.countItems(context, true, false); + return itemDAO.countItems(context, true, false, true); } @Override public int countWithdrawnItems(Context context) throws SQLException { // return count of items that are not in archive and withdrawn - return itemDAO.countItems(context, false, true); + return itemDAO.countItems(context, false, true, true ); } @Override @@ -2046,12 +2149,14 @@ public void setEntityType(Context context, Item item, String entityType) { */ @Override protected void moveSingleMetadataValue(Context context, Item dso, int place, MetadataValue rr) { + // If this is a (virtual) metadata value representing a relationship, + // then we must also update the corresponding Relationship with the new place if (rr instanceof RelationshipMetadataValue) { try { //Retrieve the applicable relationship Relationship rs = relationshipService.find(context, ((RelationshipMetadataValue) rr).getRelationshipId()); - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().getID().equals(dso.getID())) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); 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 f1a8e546ebb1..67e15a61249f 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; @@ -32,7 +29,6 @@ import org.dspace.content.service.MetadataValueService; import org.dspace.content.service.WorkspaceItemService; import org.dspace.content.template.TemplateItemValueService; -import org.dspace.content.vo.MetadataValueVO; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.LogHelper; @@ -109,11 +105,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); @@ -142,57 +145,16 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b } // Copy template if appropriate - Item templateItem = collection.getTemplateItem(); - - Optional colEntityType = getDSpaceEntityType(collection); - Optional templateItemEntityType = getDSpaceEntityType(templateItem); - - if (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 (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 templateMetadataValues = itemService.getMetadata(templateItem, Item.ANY, Item.ANY, - Item.ANY, Item.ANY); - - for (MetadataValue templateMetadataValue : templateMetadataValues) { - - MetadataField metadataField = templateMetadataValue.getMetadataField(); - MetadataSchema metadataSchema = metadataField.getMetadataSchema(); - - List metadataValueFromTemplateList = templateItemValueService.value(context, item, - templateItem, templateMetadataValue); - - for (MetadataValueVO metadataValueFromTemplate : metadataValueFromTemplateList) { - itemService.addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(), - metadataField.getQualifier(), templateMetadataValue.getLanguage(), - metadataValueFromTemplate.getValue(), metadataValueFromTemplate.getAuthority(), - metadataValueFromTemplate.getConfidence()); - } - } - } + itemService.populateWithTemplateItemMetadata(context, collection, template, item); itemService.update(context, item); // 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); @@ -221,15 +183,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/authority/DCInputAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java index 058f550cc8c6..9947a61c5ada 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 @@ -163,8 +163,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) || - StringUtils.containsIgnoreCase(labelsLocale[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)) { 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 65d0f25c3062..24a8f2ec2375 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 @@ -113,8 +113,9 @@ public boolean accept(File dir, String name) { } } String vocabulariesPath = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty( - "dspace.dir") + "/config/controlled-vocabularies/"; + .getProperty("dspace.dir") + + File.separator + "config" + + File.separator + "controlled-vocabularies"; String[] xmlFiles = (new File(vocabulariesPath)).list(new xmlFilter()); List names = new ArrayList(); for (String filename : xmlFiles) { @@ -252,6 +253,7 @@ public Choice getChoice(String authKey, String locale) { @Override public boolean isHierarchical() { + init(); return true; } 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/crosswalk/METSRightsCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java index 7f6622841ba7..bd424598771c 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,52 @@ 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 and type on policy and add to list of policies + rp.setAction(parsePermissions(permsElement)); + rp.setRpType(ResourcePolicy.TYPE_CUSTOM); + policies.add(rp); + } } //end if "Context" element } //end for loop 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); diff --git a/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java index 0cae1c7e5192..73f15d1d0586 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java @@ -145,7 +145,8 @@ public Iterator findAllByCollection(Context context, Collection collection * @return item count * @throws SQLException if database error */ - public int countItems(Context context, Collection collection, boolean includeArchived, boolean includeWithdrawn) + int countItems(Context context, Collection collection, boolean includeArchived, boolean includeWithdrawn, + boolean discoverable) throws SQLException; /** @@ -161,8 +162,8 @@ public int countItems(Context context, Collection collection, boolean includeArc * @return item count * @throws SQLException if database error */ - public int countItems(Context context, List collections, boolean includeArchived, - boolean includeWithdrawn) throws SQLException; + int countItems(Context context, List collections, boolean includeArchived, + boolean includeWithdrawn, boolean discoverable) throws SQLException; /** * Get all Items installed or withdrawn, discoverable, and modified since a Date. @@ -197,7 +198,8 @@ public Iterator findAll(Context context, boolean archived, * @return count of items * @throws SQLException if database error */ - int countItems(Context context, boolean includeArchived, boolean includeWithdrawn) throws SQLException; + int countItems(Context context, boolean includeArchived, boolean includeWithdrawn, + boolean discoverable) throws SQLException; /** * Count number of items from the specified submitter based on specific status flags @@ -209,7 +211,8 @@ public Iterator findAll(Context context, boolean archived, * @return count of items * @throws SQLException if database error */ - public int countItems(Context context, EPerson submitter, boolean includeArchived, boolean includeWithdrawn) + int countItems(Context context, EPerson submitter, boolean includeArchived, boolean includeWithdrawn, + boolean discoverable) throws SQLException; /** 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..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,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 DISTINCT c FROM Collection c JOIN Subscription s ON c.id = " + + "s.dSpaceObject")); } @Override diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java index 3b12c68dcedd..8e1b9e9b6a8c 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java @@ -419,30 +419,35 @@ public Iterator findAllByCollection(Context context, Collection collection } @Override - public int countItems(Context context, Collection collection, boolean includeArchived, boolean includeWithdrawn) + public int countItems(Context context, Collection collection, boolean includeArchived, boolean includeWithdrawn, + boolean discoverable) throws SQLException { Query query = createQuery(context, "select count(i) from Item i join i.collections c " + - "WHERE :collection IN c AND i.inArchive=:in_archive AND i.withdrawn=:withdrawn"); + "WHERE :collection IN c AND i.inArchive=:in_archive AND i.withdrawn=:withdrawn " + + "AND discoverable=:discoverable"); query.setParameter("collection", collection); query.setParameter("in_archive", includeArchived); query.setParameter("withdrawn", includeWithdrawn); + query.setParameter("discoverable", discoverable); return count(query); } @Override public int countItems(Context context, List collections, boolean includeArchived, - boolean includeWithdrawn) throws SQLException { + boolean includeWithdrawn, boolean discoverable) throws SQLException { if (collections.size() == 0) { return 0; } Query query = createQuery(context, "select count(distinct i) from Item i " + "join i.collections collection " + - "WHERE collection IN (:collections) AND i.inArchive=:in_archive AND i.withdrawn=:withdrawn"); + "WHERE collection IN (:collections) AND i.inArchive=:in_archive AND i.withdrawn=:withdrawn AND " + + "discoverable=:discoverable"); query.setParameter("collections", collections); query.setParameter("in_archive", includeArchived); query.setParameter("withdrawn", includeWithdrawn); + query.setParameter("discoverable", discoverable); return count(query); } @@ -465,24 +470,29 @@ public int countRows(Context context) throws SQLException { } @Override - public int countItems(Context context, boolean includeArchived, boolean includeWithdrawn) throws SQLException { + public int countItems(Context context, boolean includeArchived, boolean includeWithdrawn, + boolean discoverable) throws SQLException { Query query = createQuery(context, "SELECT count(*) FROM Item i " + - "WHERE i.inArchive=:in_archive AND i.withdrawn=:withdrawn"); + "WHERE i.inArchive=:in_archive AND i.withdrawn=:withdrawn AND discoverable=:discoverable"); query.setParameter("in_archive", includeArchived); query.setParameter("withdrawn", includeWithdrawn); + query.setParameter("discoverable", discoverable); return count(query); } @Override - public int countItems(Context context, EPerson submitter, boolean includeArchived, boolean includeWithdrawn) + public int countItems(Context context, EPerson submitter, boolean includeArchived, boolean includeWithdrawn, + boolean discoverable) throws SQLException { Query query = createQuery(context, "SELECT count(*) FROM Item i join i.submitter submitter " + - "WHERE i.inArchive=:in_archive AND i.withdrawn=:withdrawn AND submitter = :submitter"); + "WHERE i.inArchive=:in_archive AND i.withdrawn=:withdrawn AND submitter = :submitter AND " + + "discoverable=:discoverable"); query.setParameter("submitter", submitter); query.setParameter("in_archive", includeArchived); query.setParameter("withdrawn", includeWithdrawn); + query.setParameter("discoverable", discoverable); return count(query); } diff --git a/dspace-api/src/main/java/org/dspace/content/edit/service/impl/EditItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/edit/service/impl/EditItemServiceImpl.java index 68cdc0e94aeb..995565f68847 100644 --- a/dspace-api/src/main/java/org/dspace/content/edit/service/impl/EditItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/edit/service/impl/EditItemServiceImpl.java @@ -132,7 +132,7 @@ public List findBySubmitter(Context context, EPerson ep, int pageSize, */ @Override public int countBySubmitter(Context context, EPerson ep) throws SQLException { - return itemDAO.countItems(context, ep, true, false); + return itemDAO.countItems(context, ep, true, false, true); } /* (non-Javadoc) 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/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/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/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(); 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 f06208e2d151..220831eed00c 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 @@ -15,7 +15,6 @@ import java.util.UUID; import org.dspace.authorize.AuthorizeException; -import org.dspace.browse.ItemCountException; import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -553,9 +552,9 @@ public List findAllCollectionsByEntityType(Context context, String e /** * Returns total collection archived items * + * @param context DSpace context * @param collection Collection * @return total collection archived items - * @throws ItemCountException */ - int countArchivedItems(Collection collection) throws ItemCountException; + int countArchivedItems(Context context, Collection collection); } diff --git a/dspace-api/src/main/java/org/dspace/content/service/CommunityService.java b/dspace-api/src/main/java/org/dspace/content/service/CommunityService.java index c089bcec8df1..163baedf5ff2 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/CommunityService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/CommunityService.java @@ -14,7 +14,6 @@ import java.util.UUID; import org.dspace.authorize.AuthorizeException; -import org.dspace.browse.ItemCountException; import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -297,9 +296,9 @@ public void removeSubcommunity(Context context, Community parentCommunity, Commu /** * Returns total community archived items * + * @param context DSpace context * @param community Community * @return total community archived items - * @throws ItemCountException */ - int countArchivedItems(Community community) throws ItemCountException; + int countArchivedItems(Context context, Community community); } 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 5883006a9180..1102d14dc639 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 @@ -84,6 +84,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. @@ -729,11 +741,30 @@ public Iterator findArchivedByMetadataField(Context context, String schema * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - public Iterator findArchivedByMetadataField(Context context, String metadataField, String value) + Iterator findArchivedByMetadataField(Context context, String metadataField, String value) throws SQLException, AuthorizeException; - public Iterator findUnfilteredByMetadataField(Context context, String schema, String element, - String qualifier, String value) throws SQLException, AuthorizeException; + Iterator findUnfilteredByMetadataField( + Context context, String schema, String element, String qualifier, String value + ) throws SQLException, AuthorizeException; + + /** + * Returns an iterator of Items possessing the passed metadata field, or only + * those matching the passed value, if value is not Item.ANY + * + * @param context DSpace context object + * @param schema metadata field schema + * @param element metadata field element + * @param qualifier metadata field qualifier + * @param value field value or Item.ANY to match any value + * @return an iterator over the items matching that authority value + * @throws SQLException if database error + * @throws AuthorizeException if authorization error + * @throws IOException if IO error + */ + Iterator findByMetadataField( + Context context, String schema, String element, String qualifier, String value + ) throws SQLException, AuthorizeException, IOException; public Iterator findByMetadataQuery(Context context, List> listFieldList, List query_op, List query_val, List collectionUuids, 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/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index 8cbfd6e1deac..96d40b8206cb 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -439,7 +439,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-api/src/main/java/org/dspace/core/I18nUtil.java b/dspace-api/src/main/java/org/dspace/core/I18nUtil.java index afec0b95f69b..038232a247cb 100644 --- a/dspace-api/src/main/java/org/dspace/core/I18nUtil.java +++ b/dspace-api/src/main/java/org/dspace/core/I18nUtil.java @@ -101,14 +101,14 @@ private static Locale makeLocale(String localeSpec) { */ public static Locale getEPersonLocale(EPerson ep) { if (ep == null) { - log.error("No EPerson specified, returning default locale"); + log.info("No EPerson specified, returning default locale"); return I18nUtil.getDefaultLocale(); } String lang = ep.getLanguage(); if (StringUtils.isBlank(lang)) { - log.error("No language specified for EPerson " + ep.getID()); + log.info("No language specified for EPerson " + ep.getID() + ", returning default locale"); return I18nUtil.getDefaultLocale(); } @@ -271,7 +271,7 @@ public static String getMessage(String key, Locale locale) { String message = messages.getString(key.trim()); return message; } catch (MissingResourceException e) { - log.error("'" + key + "' translation undefined in locale '" + log.warn("'" + key + "' translation undefined in locale '" + locale.toString() + "'"); return key; } diff --git a/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java b/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java index 7cd2616ff6e7..5d7c16cf7d1a 100644 --- a/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java +++ b/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java @@ -19,10 +19,11 @@ /** * Iterator implementation which allows to iterate over items and commit while - * iterating. Using a list of UUID the iterator doesn't get invalidated after a - * commit + * iterating. Using an iterator over previous retrieved UUIDs the iterator doesn't + * get invalidated after a commit that would instead close the database ResultSet * * @author stefano.maffei at 4science.com + * @author Andrea Bollini (andrea.bollini at 4science.com) * @param class type */ public class UUIDIterator extends AbstractIterator { 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 59f898627832..6dadcbfc61a7 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 @@ -67,7 +67,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; @@ -85,7 +92,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); } } @@ -105,8 +112,8 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea && !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(); @@ -117,6 +124,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 @@ -125,30 +144,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); + } } 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 2188dcb81579..c5e4c8054b1d 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonServiceImpl.java @@ -143,10 +143,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 a05c73a070fa..493ed5059f05 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -881,10 +881,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; } } 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 bed8c044dd8a..2fe3f7d93063 100644 --- a/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java +++ b/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java @@ -36,6 +36,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -868,12 +869,12 @@ private Optional getMetadataIdentifier(Element record) { * @param item a newly created, but not yet installed, DSpace Item * @return null or the handle to be used. */ - private String extractHandle(Item item) { - String[] acceptedHandleServers = configurationService.getArrayProperty("oai.harvester.acceptedHandleServer", - new String[] { "hdl.handle.net" }); + protected String extractHandle(Item item) { + String[] acceptedHandleServers = configurationService + .getArrayProperty("oai.harvester.acceptedHandleServer", new String[] {"hdl.handle.net"}); - String[] rejectedHandlePrefixes = configurationService.getArrayProperty("oai.harvester.rejectedHandlePrefix", - new String[] { "123456789" }); + String[] rejectedHandlePrefixes = configurationService + .getArrayProperty("oai.harvester.rejectedHandlePrefix", new String[] {"123456789"}); List values = itemService.getMetadata(item, "dc", "identifier", Item.ANY, Item.ANY); @@ -891,12 +892,9 @@ private 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]; } - } } } 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 37c2888e449e..0a37523c5436 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -1109,8 +1109,9 @@ protected void removeDOIFromObject(Context context, DSpaceObject dso, String doi } itemService.clearMetadata(context, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null); - itemService.addMetadata(context, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, - remainder); + if (!remainder.isEmpty()) { + itemService.addMetadata(context, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, remainder); + } itemService.update(context, item); } 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..1b6da9d37b16 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java @@ -0,0 +1,123 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.crossref; + +import java.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; +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; + +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; + } +} 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 5c4c49deaec9..05d3798af110 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); } @@ -165,7 +165,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; } @@ -187,14 +189,15 @@ private SearchByIdCallable(Query query) { private SearchByIdCallable(String id) { this.query = new Query(); - query.addParameter("id", id); + query.addParameter("id", StringUtils.trim(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); if (StringUtils.isEmpty(responseString)) { @@ -202,7 +205,9 @@ public List call() throws Exception { } JsonNode jsonNode = convertStringJsonToJsonNode(responseString); JsonNode messageNode = jsonNode.at("/message"); - results.add(transformSourceRecords(messageNode.toString())); + if (!messageNode.isMissingNode()) { + results.add(transformSourceRecords(messageNode.toString())); + } return results; } } @@ -259,7 +264,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; } @@ -280,7 +287,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) { @@ -314,7 +321,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; } @@ -325,7 +332,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; @@ -345,4 +353,4 @@ public void setUrl(String url) { this.url = url; } -} \ No newline at end of file +} 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..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 @@ -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); @@ -108,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; 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 b2d2ea22b0c6..ba8e11c030e9 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 @@ -93,7 +93,7 @@ public void setMetadataFieldMapping( } /** - * 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 @@ -208,4 +208,4 @@ public void setCreatorMetadataContributor( SimpleXpathMetadatumContributor creatorMetadataContributor) { this.creatorMetadataContributor = creatorMetadataContributor; } -} \ No newline at end of file +} 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 c384b8f8cc1c..e5d0279aba21 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; } @@ -395,10 +400,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(); } } @@ -434,4 +445,4 @@ public void setInstKey(String instKey) { this.instKey = instKey; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/service/DoiCheck.java b/dspace-api/src/main/java/org/dspace/importer/external/service/DoiCheck.java index 3aa760eff39e..b690c627c85e 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/service/DoiCheck.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/service/DoiCheck.java @@ -20,7 +20,16 @@ */ public class DoiCheck { - private static final List DOI_PREFIXES = Arrays.asList("http://dx.doi.org/", "https://dx.doi.org/"); + private static final List DOI_PREFIXES = Arrays.asList( + "http://dx.doi.org/", + "https://dx.doi.org/", + "http://www-dx.doi.org/", + "https://www-dx.doi.org/", + "http://doi.org/", + "https://doi.org/", + "www.dx.doi.org/", + "dx.doi.org/", + "doi:"); private static final Pattern PATTERN = Pattern.compile("10.\\d{4,9}/[-._;()/:A-Z0-9]+" + "|10.1002/[^\\s]+" + 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 049b7845da50..e6d751a4a9c3 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/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 204da303c770..93c234c2a456 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -85,6 +85,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; @@ -151,6 +152,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; @@ -231,6 +234,15 @@ public void postView(DSpaceObject dspaceObject, HttpServletRequest request, public void postView(DSpaceObject dspaceObject, HttpServletRequest request, EPerson currentUser, String referrer, Date time) { + 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; 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 02203db4e8ba..28d2cc9ce694 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; @@ -119,6 +120,11 @@ public class S3BitStoreService extends BaseBitStoreService { private Integer connectionTimeout; private String endpoint; + /** + * 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 */ @@ -406,20 +412,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()); } /** @@ -804,4 +797,84 @@ public String path(Bitstream bitstream) throws IOException { return tempFile.getAbsolutePath(); } + 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; + 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 { + // 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; + // do we get any data or are we at the end of the file? + if (byteRead != -1) { + currPos++; + } else { + currentChunkStream.close(); + } + return byteRead; + } + + /** + * 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 { + if (currentChunkStream != null) { + currentChunkStream.close(); + } + } + + } } 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/submit/service/SubmissionConfigService.java b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java index 66f778947ab5..780854b72c10 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,8 +34,6 @@ 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 b76725107d44..8efe5b6c3b6e 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 @@ -56,11 +56,6 @@ public int countSubmissionConfigs() { return submissionConfigReader.countSubmissionConfigs(); } - @Override - public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) { - return submissionConfigReader.getSubmissionConfigByCollection(collectionHandle); - } - @Override public SubmissionConfig getSubmissionConfigByCollection(Collection collection) { return submissionConfigReader.getSubmissionConfigByCollection(collection); 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 65f1ae9dcf9b..ea66827dba0c 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java @@ -96,6 +96,7 @@ private String generateBodyMail(String type, List subscription // .orElseGet(() -> entityType2Disseminator.get("Item")) // .disseminate(context, item, out); } + out.close(); return out.toString(); } catch (Exception e) { log.error(e.getMessage(), e); 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 0cd8cb5fc64c..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(), true); + 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 802c4b3c0da2..864a7d17c67f 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java @@ -1236,7 +1236,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/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..61f361625dd9 --- /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,12 @@ +-- +-- 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/ +-- + +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) ; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.03.07.01__set_eperson_process_nullable.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.03.07.01__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.01__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/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_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..61f361625dd9 --- /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,12 @@ +-- +-- 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/ +-- + +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) ; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.03.07.01__set_eperson_process_nullable.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.03.07.01__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.01__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.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; 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 8a951d26b9e2..2a2747da20b1 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -27,6 +27,8 @@ + + @@ -491,6 +493,14 @@ + + + + + + + + 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 d2051496cfd7..d3989e9154b0 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 @@ -294,8 +294,7 @@ - + @@ -351,8 +350,7 @@ - + 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/app/bulkedit/BulkImportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java index 601d77d8b91c..1aef4739ea41 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java @@ -2172,9 +2172,8 @@ public void testBitstreamUpdateWithAdditionalConditionSetToFalse() throws Except .withName("Original bitstream title") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(bitstream) - .withUser(eperson) .withName("test") .withAction(READ) .withPolicyType(TYPE_CUSTOM) 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 63af96adb7c2..252818ead064 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,6 +9,7 @@ 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; @@ -231,9 +232,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(); @@ -245,19 +247,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 { diff --git a/dspace-api/src/test/java/org/dspace/app/filetype/consumer/FileTypeMetadataEnhancerConsumerIT.java b/dspace-api/src/test/java/org/dspace/app/filetype/consumer/FileTypeMetadataEnhancerConsumerIT.java index 9bdc3f752065..28d050f52362 100644 --- a/dspace-api/src/test/java/org/dspace/app/filetype/consumer/FileTypeMetadataEnhancerConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/filetype/consumer/FileTypeMetadataEnhancerConsumerIT.java @@ -158,10 +158,9 @@ public void testWithEntityTypeDelete() .build(); ResourcePolicyBuilder - .createResourcePolicy(context) + .createResourcePolicy(context, admin, null) .withDspaceObject(bitstream) .withAction(Constants.READ) - .withUser(admin) .build(); context.restoreAuthSystemState(); 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/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-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-api/src/test/java/org/dspace/authorize/relationship/RelationshipItemWritePolicyAuthorizerIT.java b/dspace-api/src/test/java/org/dspace/authorize/relationship/RelationshipItemWritePolicyAuthorizerIT.java index e03292389243..6d3ede6158ea 100644 --- a/dspace-api/src/test/java/org/dspace/authorize/relationship/RelationshipItemWritePolicyAuthorizerIT.java +++ b/dspace-api/src/test/java/org/dspace/authorize/relationship/RelationshipItemWritePolicyAuthorizerIT.java @@ -85,9 +85,8 @@ public void testWithWritePolicy() throws Exception { .withTitle("Test item") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(Constants.WRITE) - .withUser(eperson) .withDspaceObject(item) .build(); 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 2fee83afa924..bdf56370d92f 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CollectionBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CollectionBuilder.java @@ -289,8 +289,7 @@ public CollectionBuilder withSharedWorkspace() { 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 4af64b81cb0c..4b40043fc494 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleTest.java @@ -633,9 +633,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 a9df8f22a423..beb8cbfbcd91 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; @@ -537,6 +539,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. */ @@ -613,8 +626,67 @@ 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 + 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"; + int 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()); + // Is authority controlled, thus the authority will be there! + assertEquals(newAuthority, dc.get(0).getAuthority()); + assertEquals(newConfidence, dc.get(0).getConfidence()); + } + /** - * 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 { @@ -622,24 +694,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)); } /** @@ -700,6 +766,42 @@ public void testAddMetadata_7args_2_noauthority() throws SQLException { assertThat("testAddMetadata_7args_2 8", dc.get(0).getConfidence(), equalTo(0)); } + @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. */ @@ -1255,7 +1357,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); @@ -1282,9 +1384,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); @@ -1314,9 +1416,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/integration/crosswalks/XlsCollectionCrosswalkIT.java b/dspace-api/src/test/java/org/dspace/content/integration/crosswalks/XlsCollectionCrosswalkIT.java index f0f2fc6c1ed3..e8a7a7542f37 100644 --- a/dspace-api/src/test/java/org/dspace/content/integration/crosswalks/XlsCollectionCrosswalkIT.java +++ b/dspace-api/src/test/java/org/dspace/content/integration/crosswalks/XlsCollectionCrosswalkIT.java @@ -67,6 +67,9 @@ import org.dspace.content.WorkspaceItem; import org.dspace.core.Constants; import org.dspace.core.CrisConstants; +import org.dspace.eperson.Group; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.GroupService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.utils.DSpace; @@ -93,6 +96,10 @@ public class XlsCollectionCrosswalkIT extends AbstractIntegrationTestWithDatabas private Community community; + private Group anonymousGroup; + + private Group adminGroup; + private static final String BITSTREAM_URL_FORMAT = "%s/api/core/bitstreams/%s/content"; @Before @@ -108,9 +115,12 @@ public void setup() throws SQLException, AuthorizeException { .getServicesByType(BulkImportWorkbookBuilderImpl.class).get(0); configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); context.turnOffAuthorisationSystem(); community = createCommunity(context).build(); + anonymousGroup = groupService.findByName(context, Group.ANONYMOUS); + adminGroup = groupService.findByName(context, Group.ADMIN); context.restoreAuthSystemState(); } @@ -759,7 +769,7 @@ public void testCollectionDisseminateWithBitstreamSheet() throws Exception { .withDescription("desc 2") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(secondBitstream) .withAction(Constants.READ) .withName("openaccess") @@ -840,7 +850,7 @@ public void testCollectionDisseminateWithBitstreamSheetOneBitstream() throws Exc .withMetadata("dc", "contributor", null, "Unknown author") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withDescription("Test policy") @@ -848,27 +858,27 @@ public void testCollectionDisseminateWithBitstreamSheetOneBitstream() throws Exc .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .withDspaceObject(bitstream) .withAction(Constants.WRITE) .withName("administrator") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withName("openaccess") .withPolicyType(ResourcePolicy.TYPE_INHERITED) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withName("embargo") @@ -876,7 +886,7 @@ public void testCollectionDisseminateWithBitstreamSheetOneBitstream() throws Exc .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withName("lease") @@ -1012,12 +1022,12 @@ public void testCollectionDisseminateWithBitstreamSheetWithMetadataLanguages() t // add metadata dc.description[en] Bitstream bitstream = createBitstream(context, bundle, getBitstreamSample("First bitstream sample")) .withName("test.txt") - .withMetadata("dc", "description", null, "test description 1", "en") + .withMetadata("dc", "description", null, "en", "test description 1") .withMetadata("dc", "date", null, "2023-02-23") .withMetadata("dc", "contributor", null, "Unknown author") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withDescription("Test policy") @@ -1025,27 +1035,27 @@ public void testCollectionDisseminateWithBitstreamSheetWithMetadataLanguages() t .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) .withDspaceObject(bitstream) .withAction(Constants.WRITE) .withName("administrator") .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withName("openaccess") .withPolicyType(ResourcePolicy.TYPE_INHERITED) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withName("embargo") @@ -1053,7 +1063,7 @@ public void testCollectionDisseminateWithBitstreamSheetWithMetadataLanguages() t .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(bitstream) .withAction(Constants.READ) .withName("lease") 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 15e707385366..b06efaf66b93 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 @@ -388,9 +388,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); @@ -602,9 +601,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); @@ -824,10 +822,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 7d0ddcb5f9e9..eb2301343456 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 @@ -696,8 +696,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(); @@ -710,8 +709,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(); @@ -730,8 +728,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(); @@ -750,8 +747,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-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java index d42b34aa61be..28fac33f1f0a 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java @@ -17,7 +17,6 @@ import static org.junit.Assume.assumeNotNull; import static org.mockito.Mockito.mock; -import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; @@ -53,7 +52,6 @@ import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.utils.DSpace; -import org.dspace.workflow.WorkflowException; import org.dspace.workflow.WorkflowItem; import org.dspace.workflow.factory.WorkflowServiceFactory; import org.junit.After; @@ -171,11 +169,9 @@ public void destroy() { * * @throws SQLException if database error * @throws AuthorizeException if authorization error - * @throws IOException if IO error + * @throws Exception if error */ - private Item newItem() - throws SQLException, AuthorizeException, IOException, IllegalAccessException, IdentifierException, - WorkflowException { + private Item newItem() throws Exception { context.turnOffAuthorisationSystem(); WorkspaceItem wsItem = workspaceItemService.create(context, collection, false); @@ -211,11 +207,13 @@ private Item newItem() provider.DOI_ELEMENT, provider.DOI_QUALIFIER, null); - itemService.addMetadata(context, item, provider.MD_SCHEMA, - provider.DOI_ELEMENT, - provider.DOI_QUALIFIER, - null, - remainder); + if (!remainder.isEmpty()) { + itemService.addMetadata(context, item, provider.MD_SCHEMA, + provider.DOI_ELEMENT, + provider.DOI_QUALIFIER, + null, + remainder); + } itemService.update(context, item); //we need to commit the changes so we don't block the table for testing @@ -224,8 +222,7 @@ private Item newItem() return item; } - public String createDOI(Item item, Integer status, boolean metadata) - throws SQLException, IdentifierException, AuthorizeException { + public String createDOI(Item item, Integer status, boolean metadata) throws Exception { return this.createDOI(item, status, metadata, null); } @@ -237,12 +234,11 @@ public String createDOI(Item item, Integer status, boolean metadata) * @param metadata Whether the DOI should be included in the metadata of the item. * @param doi The DOI or null if we should generate one. * @return the DOI - * @throws SQLException if database error + * @throws Exception if error * @throws org.dspace.identifier.IdentifierException passed through. * @throws org.dspace.authorize.AuthorizeException passed through. */ - public String createDOI(Item item, Integer status, boolean metadata, String doi) - throws SQLException, IdentifierException, AuthorizeException { + public String createDOI(Item item, Integer status, boolean metadata, String doi) throws Exception { context.turnOffAuthorisationSystem(); // we need some random data. UUIDs would be bloated here Random random = new Random(); @@ -312,9 +308,7 @@ public void testDoes_not_support_invalid_String() { } @Test - public void testStore_DOI_as_item_metadata() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testStore_DOI_as_item_metadata() throws Exception { Item item = newItem(); String doi = DOI.SCHEME + PREFIX + "/" + NAMESPACE_SEPARATOR + Long.toHexString(new Date().getTime()); @@ -336,9 +330,7 @@ public void testStore_DOI_as_item_metadata() } @Test - public void testGet_DOI_out_of_item_metadata() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testGet_DOI_out_of_item_metadata() throws Exception { Item item = newItem(); String doi = DOI.SCHEME + PREFIX + "/" + NAMESPACE_SEPARATOR + Long.toHexString(new Date().getTime()); @@ -516,9 +508,7 @@ public void testGet_DOI_Belongs_To_GenericCollection() throws Exception { } @Test - public void testRemove_DOI_from_item_metadata() - throws SQLException, AuthorizeException, IOException, IdentifierException, WorkflowException, - IllegalAccessException { + public void testRemove_DOI_from_item_metadata() throws Exception { Item item = newItem(); String doi = DOI.SCHEME + PREFIX + "/" + NAMESPACE_SEPARATOR + Long.toHexString(new Date().getTime()); @@ -548,9 +538,7 @@ public void testRemove_DOI_from_item_metadata() } @Test - public void testGet_DOI_by_DSpaceObject() - throws SQLException, AuthorizeException, IOException, - IllegalArgumentException, IdentifierException, WorkflowException, IllegalAccessException { + public void testGet_DOI_by_DSpaceObject() throws Exception { Item item = newItem(); String doi = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, false); @@ -561,9 +549,7 @@ public void testGet_DOI_by_DSpaceObject() } @Test - public void testGet_DOI_lookup() - throws SQLException, AuthorizeException, IOException, - IllegalArgumentException, IdentifierException, WorkflowException, IllegalAccessException { + public void testGet_DOI_lookup() throws Exception { Item item = newItem(); String doi = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, false); @@ -574,9 +560,7 @@ public void testGet_DOI_lookup() } @Test - public void testGet_DSpaceObject_by_DOI() - throws SQLException, AuthorizeException, IOException, - IllegalArgumentException, IdentifierException, WorkflowException, IllegalAccessException { + public void testGet_DSpaceObject_by_DOI() throws Exception { Item item = newItem(); String doi = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, false); @@ -589,9 +573,7 @@ public void testGet_DSpaceObject_by_DOI() } @Test - public void testResolve_DOI() - throws SQLException, AuthorizeException, IOException, - IllegalArgumentException, IdentifierException, WorkflowException, IllegalAccessException { + public void testResolve_DOI() throws Exception { Item item = newItem(); String doi = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, false); @@ -608,9 +590,7 @@ public void testResolve_DOI() * problems while deleting DOIs. */ @Test - public void testRemove_two_DOIs_from_item_metadata() - throws SQLException, AuthorizeException, IOException, IdentifierException, WorkflowException, - IllegalAccessException { + public void testRemove_two_DOIs_from_item_metadata() throws Exception { // add two DOIs. Item item = newItem(); String doi1 = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, true); @@ -666,9 +646,7 @@ public void testRemove_two_DOIs_from_item_metadata() } @Test - public void testMintDOI() - throws SQLException, AuthorizeException, IOException, IllegalAccessException, IdentifierException, - WorkflowException { + public void testMintDOI() throws Exception { Item item = newItem(); String doi = null; try { @@ -691,9 +669,7 @@ public void testMintDOI() } @Test - public void testMint_returns_existing_DOI() - throws SQLException, AuthorizeException, IOException, IdentifierException, WorkflowException, - IllegalAccessException { + public void testMint_returns_existing_DOI() throws Exception { Item item = newItem(); String doi = this.createDOI(item, null, true); @@ -707,9 +683,7 @@ public void testMint_returns_existing_DOI() * Test minting a DOI with a filter that always returns false and therefore never mints the DOI */ @Test - public void testMint_DOI_withNonMatchingFilter() - throws SQLException, AuthorizeException, IOException, IllegalAccessException, IdentifierException, - WorkflowException { + public void testMint_DOI_withNonMatchingFilter() throws Exception { Item item = newItem(); boolean wasFiltered = false; try { @@ -735,9 +709,7 @@ public void testMint_DOI_withNonMatchingFilter() * (this should have hte same results as base testMint_DOI, but here we use an explicit filter rather than null) */ @Test - public void testMint_DOI_withMatchingFilter() - throws SQLException, AuthorizeException, IOException, IllegalAccessException, IdentifierException, - WorkflowException { + public void testMint_DOI_withMatchingFilter() throws Exception { Item item = newItem(); String doi = null; boolean wasFiltered = false; @@ -772,9 +744,7 @@ public void testMint_DOI_withMatchingFilter() @Test - public void testReserve_DOI() - throws SQLException, SQLException, AuthorizeException, IOException, - IdentifierException, WorkflowException, IllegalAccessException { + public void testReserve_DOI() throws Exception { Item item = newItem(); String doi = this.createDOI(item, null, true); @@ -788,9 +758,7 @@ public void testReserve_DOI() } @Test - public void testRegister_unreserved_DOI() - throws SQLException, SQLException, AuthorizeException, IOException, - IdentifierException, WorkflowException, IllegalAccessException { + public void testRegister_unreserved_DOI() throws Exception { Item item = newItem(); String doi = this.createDOI(item, null, true); @@ -804,9 +772,7 @@ public void testRegister_unreserved_DOI() } @Test - public void testRegister_reserved_DOI() - throws SQLException, SQLException, AuthorizeException, IOException, - IdentifierException, WorkflowException, IllegalAccessException { + public void testRegister_reserved_DOI() throws Exception { Item item = newItem(); String doi = this.createDOI(item, DOIIdentifierProvider.IS_RESERVED, true); @@ -820,9 +786,7 @@ public void testRegister_reserved_DOI() } @Test - public void testCreate_and_Register_DOI() - throws SQLException, SQLException, AuthorizeException, IOException, - IdentifierException, WorkflowException, IllegalAccessException { + public void testCreate_and_Register_DOI() throws Exception { Item item = newItem(); // Register, skipping the filter @@ -844,9 +808,7 @@ public void testCreate_and_Register_DOI() } @Test - public void testDelete_specified_DOI() - throws SQLException, AuthorizeException, IOException, IdentifierException, WorkflowException, - IllegalAccessException { + public void testDelete_specified_DOI() throws Exception { Item item = newItem(); String doi1 = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, true); String doi2 = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, true); @@ -886,9 +848,7 @@ public void testDelete_specified_DOI() } @Test - public void testDelete_all_DOIs() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testDelete_all_DOIs() throws Exception { Item item = newItem(); String doi1 = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, true); String doi2 = this.createDOI(item, DOIIdentifierProvider.IS_REGISTERED, true); @@ -928,9 +888,7 @@ public void testDelete_all_DOIs() } @Test - public void testUpdateMetadataSkippedForPending() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testUpdateMetadataSkippedForPending() throws Exception { context.turnOffAuthorisationSystem(); Item item = newItem(); // Mint a new DOI with PENDING status @@ -949,9 +907,7 @@ public void testUpdateMetadataSkippedForPending() @Test - public void testMintDoiAfterOrphanedPendingDOI() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testMintDoiAfterOrphanedPendingDOI() throws Exception { context.turnOffAuthorisationSystem(); Item item1 = newItem(); // Mint a new DOI with PENDING status @@ -988,9 +944,7 @@ public void testMintDoiAfterOrphanedPendingDOI() } @Test - public void testUpdateMetadataSkippedForMinted() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testUpdateMetadataSkippedForMinted() throws Exception { context.turnOffAuthorisationSystem(); Item item = newItem(); // Mint a new DOI with MINTED status @@ -1008,9 +962,7 @@ public void testUpdateMetadataSkippedForMinted() } @Test - public void testLoadOrCreateDOIReturnsMintedStatus() - throws SQLException, AuthorizeException, IOException, IdentifierException, IllegalAccessException, - WorkflowException { + public void testLoadOrCreateDOIReturnsMintedStatus() throws Exception { Item item = newItem(); // Mint a DOI without an explicit reserve or register context String mintedDoi = provider.mint(context, item, DSpaceServicesFactory.getInstance() 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-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java index d6001da9de6c..dfa03a0f1dc0 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 @@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -41,6 +42,7 @@ import io.findify.s3mock.S3Mock; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.BooleanUtils; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.matcher.LambdaMatcher; import org.dspace.authorize.AuthorizeException; @@ -52,6 +54,8 @@ import org.dspace.content.Collection; import org.dspace.content.Item; import org.dspace.core.Utils; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.After; @@ -59,6 +63,7 @@ import org.junit.Test; + /** * @author Luca Giamminonni (luca.giamminonni at 4science.com) */ @@ -79,9 +84,13 @@ public class S3BitStoreServiceIT extends AbstractIntegrationTestWithDatabase { private File s3Directory; + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + @Before public void setup() throws Exception { + configurationService.setProperty("assetstore.s3.enabled", "true"); s3Directory = new File(System.getProperty("java.io.tmpdir"), "s3"); s3Mock = S3Mock.create(8001, s3Directory.getAbsolutePath()); @@ -90,8 +99,9 @@ public void setup() throws Exception { amazonS3Client = createAmazonS3Client(S3_ENDPOINT); s3BitStoreService = new S3BitStoreService(amazonS3Client); - s3BitStoreService.setEnabled(true); - + s3BitStoreService.setEnabled(BooleanUtils.toBoolean( + configurationService.getProperty("assetstore.s3.enabled"))); + s3BitStoreService.setBufferSize(22); context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) @@ -131,12 +141,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())); @@ -149,7 +172,6 @@ public void testBitstreamPutAndGetWithAlreadyPresentBucket() throws IOException String key = s3BitStoreService.getFullKey(bitstream.getInternalId()); ObjectMetadata objectMetadata = amazonS3Client.getObjectMetadata(bucketName, key); assertThat(objectMetadata.getContentMD5(), is(expectedChecksum)); - } @Test @@ -394,6 +416,17 @@ public void givenBitStreamIdentifierWithSlashesWhenSanitizedThenSlashesMustBeRem assertThat(computedPath, Matchers.not(Matchers.containsString(File.separator))); } + @Test + public void testDoNotInitializeConfigured() throws Exception { + String assetstores3enabledOldValue = configurationService.getProperty("assetstore.s3.enabled"); + configurationService.setProperty("assetstore.s3.enabled", "false"); + s3BitStoreService = new S3BitStoreService(amazonS3Client); + s3BitStoreService.init(); + assertFalse(s3BitStoreService.isInitialized()); + assertFalse(s3BitStoreService.isEnabled()); + configurationService.setProperty("assetstore.s3.enabled", assetstores3enabledOldValue); + } + private byte[] generateChecksum(String content) { try { MessageDigest m = MessageDigest.getInstance("MD5"); 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-iiif/pom.xml b/dspace-iiif/pom.xml index 3da2b44a15ba..a160e3e46f10 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 diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 5f403b656628..604fc1dc90f3 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -192,7 +192,7 @@ org.hamcrest - hamcrest-all + hamcrest compile 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 2aff8f2cf7cb..775d70748996 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(); diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 1df37e2b7856..3ab678a71f7e 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -385,7 +385,7 @@ - + addon-loginmiur @@ -403,7 +403,7 @@ - + addon-importplus @@ -479,7 +479,7 @@ com.flipkart.zjsonpatch zjsonpatch - 0.4.14 + 0.4.16 @@ -499,7 +499,7 @@ org.webjars.bowergithub.jquery jquery-dist - 3.7.0 + 3.7.1 @@ -557,7 +557,7 @@ org.webjars.bowergithub.twbs bootstrap - 4.6.1 + 4.6.2 @@ -655,12 +655,12 @@ com.nimbusds nimbus-jose-jwt ${nimbus-jose-jwt.version} - - - net.minidev - json-smart - - + + + + net.minidev + json-smart + 2.3 @@ -715,7 +715,7 @@ org.hamcrest - hamcrest-all + hamcrest test 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 38588ed1f4ba..8718ff302e59 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; @@ -141,6 +142,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); @@ -169,6 +171,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); } @@ -201,12 +209,39 @@ 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 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 opened 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; } - List formats = List.of((configurationService.getArrayProperty("webui.content_disposition_format"))); + + // 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/main/java/org/dspace/app/rest/CsrfRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CsrfRestController.java new file mode 100644 index 000000000000..1695365477be --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CsrfRestController.java @@ -0,0 +1,63 @@ +/** + * 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 javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.rest.webmvc.ControllerUtils; +import org.springframework.hateoas.RepresentationModel; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Define GET /api/security/csrf endpoint which may be used to obtain a CSRF token from Spring Security. + * This is useful to force a CSRF token to be generated prior to a POST/PUT/PATCH request that requires it. + *

+ * NOTE: This endpoint should be used sparingly to ensure clients are NOT performing two requests for every modifying + * request (e.g. a GET /csrf followed by a POST/PUT/PATCH to another endpoint). Ideally, calling this endpoint is only + * necessary BEFORE the first POST/PUT/PATCH (if a CSRF token has not yet been obtained), or in scenarios where the + * client must *force* the CSRF token to be reloaded. + */ +@RequestMapping(value = "/api/security") +@RestController +public class CsrfRestController { + + @Lazy + @Autowired + CsrfTokenRepository csrfTokenRepository; + + /** + * Return the current CSRF token as defined by Spring Security. + * Inspired by + * https://docs.spring.io/spring-security/reference/5.8/migration/servlet/exploits.html#_i_am_using_a_single_page_application_with_httpsessioncsrftokenrepository + * @param request HTTP Request + * @param response HTTP response + * @param csrfToken injected CsrfToken by Spring Security + * @return An empty response with CSRF in header & cookie + */ + @GetMapping("/csrf") + @PreAuthorize("permitAll()") + public ResponseEntity> getCsrf(HttpServletRequest request, + HttpServletResponse response, + CsrfToken csrfToken) { + // Save the CSRF token to our response using the currently enabled CsrfTokenRepository + csrfTokenRepository.saveToken(csrfToken, request, response); + + // Return a 204 No Content status + return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java index b7f94e379685..c0e8374f4240 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java @@ -285,7 +285,6 @@ private Map getLabels(HttpServletRequest request) { labelMap.put(SyndicationFeed.MSG_UNTITLED, "notitle"); labelMap.put(SyndicationFeed.MSG_LOGO_TITLE, "logo.title"); labelMap.put(SyndicationFeed.MSG_FEED_DESCRIPTION, "general-feed.description"); - labelMap.put(SyndicationFeed.MSG_UITYPE, SyndicationFeed.UITYPE_JSPUI); for (String selector : SyndicationFeed.getDescriptionSelectors()) { labelMap.put("metadata." + selector, selector); } 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 c936f9b622f9..1111274b09fd 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 @@ -27,7 +27,6 @@ import org.dspace.content.InProgressSubmission; import org.dspace.content.Item; import org.dspace.core.Context; -import org.dspace.eperson.EPerson; import org.dspace.services.RequestService; import org.dspace.services.model.Request; import org.dspace.submit.factory.SubmissionServiceFactory; @@ -83,21 +82,14 @@ public AInprogressItemConverter() throws SubmissionConfigReaderException { protected void fillFromModel(T obj, R witem, Projection projection) { Collection collection = obj.getCollection(); Item item = obj.getItem(); - EPerson submitter = null; - submitter = obj.getSubmitter(); witem.setId(obj.getID()); - witem.setCollection(collection != null ? converter.toRest(collection, projection) : null); - 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 // info if (collection != null) { - addValidationErrorsToItem(obj, witem); SubmissionDefinitionRest def = converter.toRest(getSubmissionConfig(item, collection), projection); @@ -140,8 +132,6 @@ protected void fillFromModel(T obj, R witem, Projection projection) { } } - // need to be after to have stored the submission-name in the request attribute - witem.setItem(converter.toRest(item, projection)); } private SubmissionConfig getSubmissionConfig(Item item, Collection collection) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CollectionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CollectionConverter.java index e9b6aa03b85a..8cae60eca39e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CollectionConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CollectionConverter.java @@ -9,8 +9,11 @@ import org.dspace.app.rest.model.CollectionRest; import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.utils.ContextUtil; import org.dspace.content.Collection; +import org.dspace.content.service.CollectionService; import org.dspace.discovery.IndexableObject; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** @@ -23,10 +26,14 @@ public class CollectionConverter extends DSpaceObjectConverter implements IndexableObjectConverter { + @Autowired + CollectionService collectionService; + @Override public CollectionRest convert(Collection collection, Projection projection) { CollectionRest resource = super.convert(collection, projection); - resource.setArchivedItemsCount(collection.countArchivedItems()); + resource.setArchivedItemsCount( + collectionService.countArchivedItems(ContextUtil.obtainCurrentRequestContext(), collection)); return resource; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CommunityConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CommunityConverter.java index a90ad3cfe644..62062e08139c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CommunityConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CommunityConverter.java @@ -9,8 +9,11 @@ import org.dspace.app.rest.model.CommunityRest; import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.utils.ContextUtil; import org.dspace.content.Community; +import org.dspace.content.service.CommunityService; import org.dspace.discovery.IndexableObject; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** @@ -24,9 +27,14 @@ public class CommunityConverter extends DSpaceObjectConverter implements IndexableObjectConverter { + @Autowired + CommunityService communityService; + public CommunityRest convert(Community community, Projection projection) { CommunityRest resource = super.convert(community, projection); - resource.setArchivedItemsCount(community.countArchivedItems()); + + resource.setArchivedItemsCount( + communityService.countArchivedItems(ContextUtil.obtainCurrentRequestContext(), community)); return resource; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java index 8a2135e032bb..4299f11df8d5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java @@ -7,7 +7,6 @@ */ package org.dspace.app.rest.converter; -import java.util.Objects; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.Logger; @@ -137,11 +136,6 @@ protected void fillFromModel(EditItem obj, EditItemRest rest, Projection project } } - rest.setCollection(collection != null ? converter.toRest(collection, projection) : null); - rest.setItem(converter.toRest(item, projection)); - if (Objects.nonNull(submitter)) { - rest.setSubmitter(converter.toRest(submitter, projection)); - } } private void addValidationErrorsToItem(EditItem obj, EditItemRest rest) { 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 1c8cd6a21452..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 @@ -7,7 +7,6 @@ */ package org.dspace.app.rest.converter; -import java.util.Objects; import java.util.stream.Collectors; import org.dspace.app.rest.model.ParameterValueRest; @@ -40,7 +39,7 @@ public ProcessRest convert(Process process, Projection projection) { processRest.setId(process.getID()); processRest.setScriptName(process.getName()); processRest.setProcessId(process.getID()); - if (Objects.nonNull(process.getEPerson())) { + if (process.getEPerson() != null) { processRest.setUserId(process.getEPerson().getID()); } processRest.setProcessStatus(process.getProcessStatus()); 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 f2e049ed89e6..3bf7a43e5a59 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/EditItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EditItemRest.java index ef5dd4525464..ee0e0ee92380 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EditItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EditItemRest.java @@ -14,19 +14,33 @@ * * @author Danilo Di Nuzzo (danilo.dinuzzo at 4science.it) */ -@LinksRest(links = { +@LinksRest(links = + { @LinkRest( - name = EditItemRest.MODE, - method = "getModes" + name = EditItemRest.MODE, + method = "getModes" + ), + @LinkRest( + name = EditItemRest.ITEM, + method = "getEditItemItem" + ), + @LinkRest( + name = EditItemRest.COLLECTION, + method = "getEditItemCollection" ) -}) + } +) public class EditItemRest extends AInprogressSubmissionRest { private static final long serialVersionUID = 964876735342568998L; public static final String NAME = "edititem"; + public static final String NAME_PLURAL = "edititems"; public static final String MODE = "modes"; public static final String CATEGORY = RestAddressableModel.CORE; + 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/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 f31dbfa9dcf8..d4702fc30356 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/AbstractEditItemLinkRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AbstractEditItemLinkRestRepository.java new file mode 100644 index 000000000000..6a8e23033bce --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AbstractEditItemLinkRestRepository.java @@ -0,0 +1,99 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; + +import org.dspace.app.rest.projection.Projection; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Item; +import org.dspace.content.edit.EditItem; +import org.dspace.content.edit.service.EditItemService; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.AccessDeniedException; + +/** + * Class that contains the basic implementation to retrieve linked {@link EditItem} resources. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class AbstractEditItemLinkRestRepository extends AbstractDSpaceRestRepository { + + private static final Logger log = LoggerFactory.getLogger(AbstractEditItemLinkRestRepository.class); + + @Autowired + private EditItemService editItemService; + @Autowired + private ItemService itemService; + + protected static Optional getEditItemRestRequest(String data) { + if (data == null || data.isEmpty()) { + return Optional.empty(); + } + + String[] split = data.split(":"); + + UUID uuid; + try { + uuid = UUID.fromString(split[0]); + } catch (Exception e) { + log.error("Cannot convert the following uuid: {}", split[0], e); + return Optional.empty(); + } + String mode = split[1]; + return Optional.of(new EditItemRestRequest(uuid, mode)); + } + + protected EditItem findEditItem(EditItemRestRequest requestDetails) { + try { + Context context = obtainContext(); + Item item = itemService.find(context, requestDetails.uuid); + EditItem editItem = editItemService.find(context, item, requestDetails.mode); + + if (editItem == null) { + throw new ResourceNotFoundException("No such edit item found: " + requestDetails.uuid); + } + return editItem; + } catch (SQLException e) { + throw new RuntimeException(e); + } catch (AuthorizeException e) { + throw new AccessDeniedException( + "The current user does not have rights to edit mode <" + requestDetails.mode + ">" + ); + } + } + + protected R getMappedResource( + Optional request, + Function mapper, + Projection projection + ) { + return request.map(this::findEditItem) + .map(mapper) + .map(obj -> converter.toRest(obj, projection)) + .orElse(null); + } + + protected static class EditItemRestRequest { + public final UUID uuid; + public final String mode; + + public EditItemRestRequest(UUID uuid, String mode) { + this.uuid = uuid; + this.mode = mode; + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EditItemCollectionLinkRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EditItemCollectionLinkRestRepository.java new file mode 100644 index 000000000000..05f8aab0bf01 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EditItemCollectionLinkRestRepository.java @@ -0,0 +1,50 @@ +/** + * 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 javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.CollectionRest; +import org.dspace.app.rest.model.EditItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.edit.EditItem; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +/** + * Link resource for {@link EditItemRest#COLLECTION} + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +@Component(EditItemRest.CATEGORY + "." + EditItemRest.NAME + "." + EditItemRest.COLLECTION) +public class EditItemCollectionLinkRestRepository extends AbstractEditItemLinkRestRepository + implements LinkRestRepository { + + /** + * Retrieve the collection for an edit item. + * + * @param request - The current request + * @param data - The data template that contains both item uuid and mode {uuid:mode}, joined by a column + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the item for the edit item + */ + public CollectionRest getEditItemCollection( + @Nullable HttpServletRequest request, String data, + @Nullable Pageable optionalPageable, Projection projection + ) { + return getMappedResource( + getEditItemRestRequest(data), + EditItem::getCollection, + projection + ); + + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EditItemItemLinkRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EditItemItemLinkRestRepository.java new file mode 100644 index 000000000000..bb83fffd3673 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EditItemItemLinkRestRepository.java @@ -0,0 +1,49 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.EditItemRest; +import org.dspace.app.rest.model.ItemRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.edit.EditItem; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +/** + * Link resource for {@link EditItemRest#ITEM} + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +@Component(EditItemRest.CATEGORY + "." + EditItemRest.NAME + "." + EditItemRest.ITEM) +public class EditItemItemLinkRestRepository extends AbstractEditItemLinkRestRepository implements LinkRestRepository { + + /** + * Retrieve the item for an edit item. + * + * @param request - The current request + * @param data - The data template that contains both item uuid and mode {uuid:mode}, joined by a column + * @param optionalPageable - optional pageable object + * @param projection - the current projection + * @return the item for the edit item + */ + public ItemRest getEditItemItem( + @Nullable HttpServletRequest request, String data, + @Nullable Pageable optionalPageable, Projection projection + ) { + return getMappedResource( + getEditItemRestRequest(data), + EditItem::getItem, + projection + ); + } + + +} 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..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 @@ -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.isNotBlank(pageUrl) && ! 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 +} 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 9be8a117c992..2a9dbad77070 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; @@ -59,6 +61,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(); @@ -377,14 +384,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) { @@ -402,7 +423,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/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) { 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 72ca3f254256..e2c258d615df 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 @@ -259,14 +259,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 { @@ -275,12 +267,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); @@ -288,13 +279,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/main/java/org/dspace/app/rest/repository/VocabularyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyRestRepository.java index e95cefb9f04d..92d5c9daf47d 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 @@ -101,6 +101,9 @@ public VocabularyRest findByMetadataAndCollection( } String authorityName = cas.getChoiceAuthorityName(tokens[0], tokens[1], tokens[2], Constants.ITEM, collection); + if (authorityName == null) { + return null; + } ChoiceAuthority source = cas.getChoiceAuthorityByAuthorityName(authorityName); return authorityUtils.convertAuthority(source, authorityName, utils.obtainProjection()); } 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/DSpaceCsrfTokenRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/DSpaceCsrfTokenRepository.java index 2079727ac889..7cadcce077e8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/DSpaceCsrfTokenRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/DSpaceCsrfTokenRepository.java @@ -53,11 +53,16 @@ public class DSpaceCsrfTokenRepository implements CsrfTokenRepository { // This cookie name is changed from the default "XSRF-TOKEN" to ensure it is uniquely named and doesn't conflict // with any other XSRF-TOKEN cookies (e.g. in Angular UI, the XSRF-TOKEN cookie is a *client-side* only cookie) - static final String DEFAULT_CSRF_COOKIE_NAME = "DSPACE-XSRF-COOKIE"; + public static final String DEFAULT_CSRF_COOKIE_NAME = "DSPACE-XSRF-COOKIE"; - static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf"; + // The HTTP header that is sent back to the client whenever a new CSRF token is created + // (NOTE: This is purposefully different from DEFAULT_CSRF_HEADER_NAME below!) + public static final String DSPACE_CSRF_HEADER_NAME = "DSPACE-XSRF-TOKEN"; - static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN"; + public static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf"; + + // The HTTP header that Spring Security expects to receive from the client in order to validate a CSRF token + public static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN"; private String parameterName = DEFAULT_CSRF_PARAMETER_NAME; @@ -132,7 +137,7 @@ public void saveToken(CsrfToken token, HttpServletRequest request, // We send our token via a custom header because client can be on a different domain. // Cookies cannot be reliably sent cross-domain. if (StringUtils.hasLength(tokenValue)) { - response.setHeader("DSPACE-XSRF-TOKEN", tokenValue); + response.setHeader(DSPACE_CSRF_HEADER_NAME, tokenValue); } } 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/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 8c819dddfd3d..55a088f59399 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 @@ -221,7 +221,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().getID().equals(dso.getID())) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); 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* 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..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; @@ -33,7 +34,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; @@ -144,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))); @@ -165,16 +168,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; 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 d604ff2066c2..b83c6f638c46 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 @@ -8,12 +8,18 @@ package org.dspace.app.rest.utils; import java.sql.SQLException; +import java.util.Optional; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; +import org.dspace.content.service.DSpaceObjectService; import org.dspace.content.service.ItemService; import org.dspace.core.Context; import org.dspace.discovery.IndexableObject; @@ -40,28 +46,80 @@ public class ScopeResolver { @Autowired ItemService itemService; + /** + * 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)) { - try { - UUID uuid = UUID.fromString(scope); - scopeObj = new IndexableCommunity(communityService.find(context, uuid)); - if (scopeObj.getIndexedObject() == null) { - scopeObj = new IndexableCollection(collectionService.find(context, uuid)); - } - if (scopeObj.getIndexedObject() == null) { - scopeObj = new IndexableItem(itemService.find(context, uuid)); - } - } catch (IllegalArgumentException ex) { - log.warn("The given scope string " + StringUtils.trimToEmpty(scope) + " is not a UUID", ex); - } catch (SQLException ex) { + Optional uuidOptional = + Optional.ofNullable(scope) + .filter(StringUtils::isNotBlank) + .map(this::asUUID); + return uuidOptional + .flatMap(uuid -> resolveWithIndexedObject(context, Optional.of(uuid), communityService)) + .or(() -> resolveWithIndexedObject(context, uuidOptional, collectionService)) + .orElseGet(() -> { log.warn( - "Unable to retrieve DSpace Object with ID " + StringUtils.trimToEmpty(scope) + " from the database", - ex); - } + "The given scope string " + + StringUtils.trimToEmpty(scope) + + " is not a collection or community UUID." + ); + return uuidOptional.map(uuid -> resolve(context, uuid, itemService)).orElse(null); + }); + } + + private UUID asUUID(String scope) { + try { + return UUID.fromString(scope); + } catch (IllegalArgumentException ex) { + log.warn("The given scope string " + StringUtils.trimToEmpty(scope) + " is not a UUID", ex); } + return null; + } + + private Optional resolveWithIndexedObject( + Context context, Optional uuidOptional, DSpaceObjectService service + ) { + return uuidOptional.map(uuid -> resolve(context, uuid, service)) + .filter(obj -> obj.getIndexedObject() != null); + } - return scopeObj; + public IndexableObject resolve( + Context context, UUID uuid, DSpaceObjectService service + ) { + if (uuid == null) { + return null; + } + T dspaceObject = null; + try { + dspaceObject = service.find(context, uuid); + } catch (IllegalArgumentException ex) { + log.warn("The given scope string " + StringUtils.trimToEmpty(uuid.toString()) + " is not a UUID", ex); + } catch (SQLException ex) { + log.warn( + "Unable to retrieve DSpace Object with ID " + StringUtils.trimToEmpty(uuid.toString()) + + " from the database", + ex); + } + if (dspaceObject == null) { + return null; + } + if (dspaceObject instanceof Community) { + return new IndexableCommunity((Community) dspaceObject); + } + if (dspaceObject instanceof Collection) { + return new IndexableCollection((Collection) dspaceObject); + } + return new IndexableItem((Item) dspaceObject); } } diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml index 49a1fc6e0605..b6c6cc364dc5 100644 --- a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml +++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml @@ -4554,6 +4554,24 @@ + + + + + crispj.coordinator + crispj.partnerou + crispj.organization + + + + + + + + + + @@ -4694,16 +4712,16 @@ - + - + - + 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..645d52df59f1 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 @@ -96,7 +96,12 @@ public void findResultSimpleTest() throws Exception { Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) .withName("Sub Community") .build(); - Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + // Add a fake logo to the Collection + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withLogo("logo_collection") + .build(); + String colLogoUuid = col1.getLogo().getID().toString(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Boars at Yellowstone") @@ -118,10 +123,24 @@ public void findResultSimpleTest() throws Exception { .andExpect(xpath("feed/Query/@searchTerms").string("Yellowstone")) .andExpect(xpath("feed/totalResults").string("2")) ; + + // When we search at the Collection level (scope = Collection UUID) + getClient().perform(get("/opensearch/search") + .param("scope", col1.getID().toString()) + .param("query", "Yellowstone")) + //The status has to be 200 OK + .andExpect(status().isOk()) + // We expect the content type to be "application/atom+xml;charset=UTF-8" + .andExpect(content().contentType("application/atom+xml;charset=UTF-8")) + .andExpect(xpath("feed/Query/@searchTerms").string("Yellowstone")) + .andExpect(xpath("feed/totalResults").string("2")) + // We expect feed will have the Collection logo + .andExpect(xpath("feed/logo") + .string("http://localhost:4000/bitstreams/" + colLogoUuid + "/download")) + ; } // This test does not find the record, so there are obviously issues with special chars - @Ignore @Test public void findResultWithSpecialCharsTest() throws Exception { //Turn off the authorization system, otherwise we can't make the objects @@ -144,12 +163,12 @@ public void findResultWithSpecialCharsTest() throws Exception { .build(); //When we call the root endpoint getClient().perform(get("/opensearch/search") - .param("query", "Bär")) + .param("query", "Bären")) //The status has to be 200 OK .andExpect(status().isOk()) //We expect the content type to be "application/atom+xml;charset=UTF-8" .andExpect(content().contentType("application/atom+xml;charset=UTF-8")) - .andExpect(xpath("feed/Query/@searchTerms").string("B%C3%A4r")) + .andExpect(xpath("feed/Query/@searchTerms").string("B%C3%A4ren")) .andExpect(xpath("feed/totalResults").string("1")) ; } @@ -269,4 +288,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")); + } } 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/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 { 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 92cff6db2192..b3964d3e8153 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 @@ -208,6 +208,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")) @@ -234,7 +246,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 @@ -1105,8 +1117,7 @@ public void updateBitstreamFormatEPerson() throws Exception { context.turnOffAuthorisationSystem(); - createResourcePolicy(context) - .withUser(eperson) + createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(bitstream) .build(); @@ -1359,7 +1370,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(); @@ -1367,8 +1378,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(); @@ -1377,9 +1388,89 @@ 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 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(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + Item item = ItemBuilder.createItem(context, collection).build(); + 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(); + // 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(); + + // 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(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") @@ -1388,11 +1479,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")); } } } 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 a0069a0a4b4f..91f94fadb479 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 @@ -711,7 +711,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(); @@ -774,9 +774,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(); @@ -899,7 +899,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(); @@ -1547,8 +1547,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/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/java/org/dspace/app/rest/CollectionLogoControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionLogoControllerIT.java index d97d0f2ce692..7d34aa771bdc 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 @@ -24,10 +24,13 @@ import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.Bitstream; import org.dspace.content.Collection; +import org.dspace.content.Community; import org.dspace.content.service.BitstreamService; import org.dspace.core.Constants; +import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.hamcrest.Matchers; @@ -116,6 +119,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( @@ -196,6 +227,42 @@ public void collectionLogoPoliciesTest() throws Exception { )); } + @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/CollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java index 1595fb1069ce..8c2d970735a3 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 @@ -2020,8 +2020,7 @@ public void testHiddenMetadataForUserWithWriteRights() throws Exception { - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(col1) .build(); @@ -3242,11 +3241,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(); @@ -3298,11 +3296,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(); @@ -3360,11 +3357,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); @@ -3413,11 +3409,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/CommunityLogoControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityLogoControllerIT.java index 61ebfc7dd7b6..bd6437467390 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 @@ -23,9 +23,12 @@ import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.Bitstream; +import org.dspace.content.Community; import org.dspace.content.service.BitstreamService; import org.dspace.core.Constants; +import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.hamcrest.Matchers; @@ -111,6 +114,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( @@ -191,6 +217,38 @@ public void communityLogoPoliciesTest() throws Exception { )); } + @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"; } 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/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index a8417e84f809..1ab5a3203c08 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(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CsrfRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CsrfRestControllerIT.java new file mode 100644 index 000000000000..1bfdc8feaff0 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CsrfRestControllerIT.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; + +import static org.junit.Assert.assertNotEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.dspace.app.rest.security.DSpaceCsrfTokenRepository; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.test.web.servlet.MockMvc; + +/** + * Integration test for the /api/security/csrf endpoint + *

+ * NOTE: This test will autoconfigure the MockMvc (@AutoConfigureMockMvc) in order to avoid using + * AbstractControllerIntegrationTest.getClient() because that method may use this /api/security/csrf endpoint to + * obtain a CSRF token. + **/ +@AutoConfigureMockMvc +public class CsrfRestControllerIT extends AbstractControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void getCsrf() throws Exception { + // NOTE: We avoid using getClient() here because that method may also call this "/api/security/csrf" endpoint. + String headerToken = mockMvc.perform(get("/api/security/csrf")) + .andExpect(status().isNoContent()) + // Expect this endpoint to send back the proper HTTP Header & Cookie + // as set by DSpaceCsrfTokenRepository + .andExpect(cookie().exists(DSpaceCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME)) + .andExpect(header().exists(DSpaceCsrfTokenRepository.DSPACE_CSRF_HEADER_NAME)) + .andReturn().getResponse() + .getHeader(DSpaceCsrfTokenRepository.DSPACE_CSRF_HEADER_NAME); + + // Call the endpoint again, and verify we get a new token again. Endpoint should ALWAYS change the CSRF token + String headerToken2 = mockMvc.perform(get("/api/security/csrf")) + .andExpect(status().isNoContent()) + // Expect this endpoint to send back the proper HTTP Header & Cookie + // as set by DSpaceCsrfTokenRepository + .andExpect(cookie().exists(DSpaceCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME)) + .andExpect(header().exists(DSpaceCsrfTokenRepository.DSPACE_CSRF_HEADER_NAME)) + .andReturn().getResponse() + .getHeader(DSpaceCsrfTokenRepository.DSPACE_CSRF_HEADER_NAME); + + assertNotEquals("CSRF Tokens should not be the same in separate requests", headerToken, headerToken2); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DSpaceRestRepositoryLanguageIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DSpaceRestRepositoryLanguageIT.java index 8a27cfeca803..37d6e09595b8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DSpaceRestRepositoryLanguageIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DSpaceRestRepositoryLanguageIT.java @@ -53,7 +53,9 @@ public void setup() { .build(); collection = CollectionBuilder.createCollection(context, parentCommunity) - .withName("Collection 1").build(); + .withEntityType("Publication") + .withName("Publications") + .build(); context.restoreAuthSystemState(); } @@ -235,56 +237,61 @@ public void testFindWorkspaceItem() throws Exception { context.turnOffAuthorisationSystem(); - WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection) - .withTitle("workspaceItem title") - .withTitleForLanguage("english title", "en") - .withTitleForLanguage("german title", "de") - .withSubjectForLanguage("german subject", "de") - .withSubjectForLanguage("spain subject", "es") - .withEntityType("Publication") - .build(); + WorkspaceItem workspaceItem = + WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("workspaceItem title") + .withTitleForLanguage("english title", "en") + .withTitleForLanguage("german title", "de") + .withSubjectForLanguage("german subject", "de") + .withSubjectForLanguage("spain subject", "es") + .withIssueDate("2024-09-26") + .build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); // When no projection is requested - getClient(token).perform(get("/api/submission/workspaceitems/" + workspaceItem.getID()) - .header("Accept-Language", Locale.ENGLISH.getLanguage())) + getClient(token).perform( + get("/api/submission/workspaceitems/" + workspaceItem.getID()) + .param("embed", "item") + .header("Accept-Language", Locale.ENGLISH.getLanguage()) + ) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.item", - ItemMatcher.matchItemProperties(workspaceItem.getItem()))) + ItemMatcher.matchItemProperties(workspaceItem.getItem()))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "english title", 0, "en"))) + matchMetadata("dc.title", "english title", 0, "en"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dspace.entity.type", "Publication", 0, null))) + matchMetadata("dspace.entity.type", "Publication", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "german subject", 0, "de"))) + matchMetadata("dc.subject", "german subject", 0, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.title", null))) + matchMetadataLanguageDoesNotExist("dc.title", null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.title", "de"))) + matchMetadataLanguageDoesNotExist("dc.title", "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.subject", "es"))); + matchMetadataLanguageDoesNotExist("dc.subject", "es"))); // When allLanguages projection is requested getClient(token).perform(get("/api/submission/workspaceitems/" + workspaceItem.getID()) - .param("projection", "allLanguages")) + .param("embed", "item") + .param("projection", "allLanguages")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.item", - ItemMatcher.matchItemProperties(workspaceItem.getItem()))) + ItemMatcher.matchItemProperties(workspaceItem.getItem()))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "workspaceItem title", 0, null))) + matchMetadata("dc.title", "workspaceItem title", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "english title", 1, "en"))) + matchMetadata("dc.title", "english title", 1, "en"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "german title", 2, "de"))) + matchMetadata("dc.title", "german title", 2, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dspace.entity.type", "Publication", 0, null))) + matchMetadata("dspace.entity.type", "Publication", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "german subject", 0, "de"))) + matchMetadata("dc.subject", "german subject", 0, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "spain subject", 1, "es"))); + matchMetadata("dc.subject", "spain subject", 1, "es"))); } @@ -299,6 +306,7 @@ public void testFindWorkflowItem() throws Exception { Collection collection = CollectionBuilder.createCollection(context, child) .withName("Collection 1") + .withEntityType("Publication") .withWorkflowGroup(1, admin).build(); XmlWorkflowItem workflowItem = WorkflowItemBuilder.createWorkflowItem(context, collection) @@ -307,7 +315,6 @@ public void testFindWorkflowItem() throws Exception { .withTitleForLanguage("german title", "de") .withSubjectForLanguage("german subject", "de") .withSubjectForLanguage("spain subject", "es") - .withEntityType("Publication") .build(); context.restoreAuthSystemState(); @@ -316,41 +323,43 @@ public void testFindWorkflowItem() throws Exception { // When no projection is requested getClient(token).perform(get("/api/workflow/workflowitems/" + workflowItem.getID()) - .header("Accept-Language", Locale.ENGLISH.getLanguage())) + .param("embed", "item") + .header("Accept-Language", Locale.ENGLISH.getLanguage())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.item", - ItemMatcher.matchItemProperties(workflowItem.getItem()))) + ItemMatcher.matchItemProperties(workflowItem.getItem()))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "english title", 0, "en"))) + matchMetadata("dc.title", "english title", 0, "en"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dspace.entity.type", "Publication", 0, null))) + matchMetadata("dspace.entity.type", "Publication", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "german subject", 0, "de"))) + matchMetadata("dc.subject", "german subject", 0, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.title", null))) + matchMetadataLanguageDoesNotExist("dc.title", null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.title", "de"))) + matchMetadataLanguageDoesNotExist("dc.title", "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.subject", "es"))); + matchMetadataLanguageDoesNotExist("dc.subject", "es"))); // When allLanguages projection is requested getClient(token).perform(get("/api/workflow/workflowitems/" + workflowItem.getID()) - .param("projection", "allLanguages")) + .param("embed", "item") + .param("projection", "allLanguages")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.item", - ItemMatcher.matchItemProperties(workflowItem.getItem()))) + ItemMatcher.matchItemProperties(workflowItem.getItem()))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "workflowItem title", 0, null))) + matchMetadata("dc.title", "workflowItem title", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "english title", 1, "en"))) + matchMetadata("dc.title", "english title", 1, "en"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "german title", 2, "de"))) + matchMetadata("dc.title", "german title", 2, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dspace.entity.type", "Publication", 0, null))) + matchMetadata("dspace.entity.type", "Publication", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "german subject", 0, "de"))) + matchMetadata("dc.subject", "german subject", 0, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "spain subject", 1, "es"))); + matchMetadata("dc.subject", "spain subject", 1, "es"))); } @@ -358,11 +367,6 @@ public void testFindWorkflowItem() throws Exception { public void testFindEditItem() throws Exception { context.turnOffAuthorisationSystem(); - Collection collection = CollectionBuilder.createCollection(context, parentCommunity) - .withEntityType("Publication") - .withName("Collection 1") - .build(); - Item item = ItemBuilder.createItem(context, collection) .withTitle("Item title") .withTitleForLanguage("english title", "en") @@ -379,35 +383,37 @@ public void testFindEditItem() throws Exception { // When no projection is requested getClient(tokenAdmin).perform(get("/api/core/edititems/" + editItem.getID() + ":none") - .header("Accept-Language", Locale.ENGLISH.getLanguage())) + .param("embed", "item") + .header("Accept-Language", Locale.ENGLISH.getLanguage())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.item", ItemMatcher.matchItemProperties(item))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "english title", 0, "en"))) + matchMetadata("dc.title", "english title", 0, "en"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "german subject", 0, "de"))) + matchMetadata("dc.subject", "german subject", 0, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.title", null))) + matchMetadataLanguageDoesNotExist("dc.title", null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.title", "de"))) + matchMetadataLanguageDoesNotExist("dc.title", "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadataLanguageDoesNotExist("dc.subject", "es"))); + matchMetadataLanguageDoesNotExist("dc.subject", "es"))); // When allLanguages projection is requested getClient(tokenAdmin).perform(get("/api/core/edititems/" + editItem.getID() + ":none") - .param("projection", "allLanguages", "full")) + .param("embed", "item") + .param("projection", "allLanguages", "full")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.item", ItemMatcher.matchItemProperties(item))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "Item title", 0, null))) + matchMetadata("dc.title", "Item title", 0, null))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "english title", 1, "en"))) + matchMetadata("dc.title", "english title", 1, "en"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.title", "german title", 2, "de"))) + matchMetadata("dc.title", "german title", 2, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "german subject", 0, "de"))) + matchMetadata("dc.subject", "german subject", 0, "de"))) .andExpect(jsonPath("$._embedded.item.metadata", - matchMetadata("dc.subject", "spain subject", 1, "es"))); + matchMetadata("dc.subject", "spain subject", 1, "es"))); } @Test @@ -472,7 +478,6 @@ public void testFindWorkspaceItemWithMultipleProjections() throws Exception { .withTitleForLanguage("german title", "de") .withSubjectForLanguage("german subject", "de") .withSubjectForLanguage("spain subject", "es") - .withEntityType("Publication") .build(); context.restoreAuthSystemState(); 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/java/org/dspace/app/rest/DiscoveryRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java index ae5982e9fff4..49ca6ae3f747 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 @@ -1193,11 +1193,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()), @@ -6291,10 +6291,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") @@ -6308,12 +6304,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))); } @@ -6378,14 +6368,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/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 473bdad73bfa..6ab18d4bd4f4 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 @@ -3460,10 +3460,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); @@ -3523,10 +3523,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 8e27e047e6b5..576965e2d663 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 @@ -329,7 +329,6 @@ public void findAllWithPaginationTest() throws Exception { .andExpect(jsonPath("$.page.totalPages", is(2))) .andExpect(jsonPath("$.page.number", is(0))) .andExpect(jsonPath("$.page.totalElements", is(3))); - ; getClient(token).perform(get("/api/core/items") .param("size", "2") @@ -681,7 +680,7 @@ public void withdrawPatchTest() throws Exception { // is used in the provenance note. String token = getAuthToken(admin.getEmail(), password); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -736,7 +735,7 @@ public void withdrawPatchUnauthorizedTest() throws Exception { .build(); context.restoreAuthSystemState(); - List ops = new ArrayList(); + List ops = new ArrayList<>(); ReplaceOperation replaceOperation = new ReplaceOperation("/withdrawn", true); ops.add(replaceOperation); String patchBody = getPatchContent(ops); @@ -3059,7 +3058,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()) @@ -3137,8 +3136,7 @@ public void testHiddenMetadataForUserWithWriteRights() throws Exception { context.restoreAuthSystemState(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(item) .build(); @@ -3170,8 +3168,7 @@ public void testHiddenMetadataForUserWithReadRights() throws Exception { context.restoreAuthSystemState(); - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(READ) .withDspaceObject(item) .build(); @@ -4748,8 +4745,7 @@ public void putItemMetadataWithUserNotPartOfGroupConfigured() throws Exception { Group group = GroupBuilder.createGroup(context).build(); configurationService.setProperty("edit.metadata.allowed-group", group.getID()); // add write rights to the user - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(itemService.find(context, UUID.fromString(itemUuidString))) .build(); @@ -4817,8 +4813,7 @@ public void putItemMetadataWithUserPartOfGroupConfigured() throws Exception { itemRest.setUuid(itemUuidString); itemRest.setHandle(itemHandleString); // add write rights to the user - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(itemService.find(context, UUID.fromString(itemUuidString))) .build(); @@ -5123,8 +5118,7 @@ public void patchItemMetadataWithUserPartOfGroupConfigured() throws Exception { .withSubject("ExtraEntry") .build(); // add write permission to the user admin - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(itemService.find(context, item.getID())) .build(); @@ -5170,8 +5164,7 @@ public void patchItemMetadataWithUserNotPartOfGroupConfigured() throws Exception .withSubject("ExtraEntry") .build(); // add write rights to the user admin - ResourcePolicyBuilder.createResourcePolicy(context) - .withUser(eperson) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withAction(WRITE) .withDspaceObject(itemService.find(context, item.getID())) .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/LayoutSecurityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java index aa5705beb861..51576730f44c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java @@ -819,10 +819,9 @@ public void patchAddMetadataContainedInAdministratorBoxTest() throws Exception { .withAuthor("Smith, Maria") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); MetadataField abs = mfss.findByElement(context, "dc", "description", "abstract"); @@ -909,10 +908,9 @@ public void adminTryToPatchAddMetadataToBoxesWithDifferentLayoutSecurityTest() t .withIssueDate("2015-06-25") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); MetadataField author = mfss.findByElement(context, "dc", "contributor", "author"); @@ -1023,10 +1021,9 @@ public void patchRemoveMetadataContainedInAdministratorAndPublicBoxesTest() thro .withAuthor("Smith, Maria") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "A secured abstract"); @@ -1123,10 +1120,9 @@ public void patchRemoveMetadataContainedInBoxesWithOnlyOwnerLayoutSecurityTest() .withDescriptionAbstract("A secured abstract") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); MetadataField abs = mfss.findByElement(context, "dc", "description", "abstract"); @@ -1221,13 +1217,16 @@ public void patchRemoveMetadataContainedInBoxesWithCustomDataLayoutSecurityTest( .withAuthor("Smith, Maria") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(userA) - .withGroup(groupA) .build(); + ResourcePolicyBuilder.createResourcePolicy(context, null, groupA) + .withDspaceObject(itemA) + .withAction(Constants.WRITE) + .build(); + itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "A secured abstract"); itemService.addMetadata(context, itemA, "cris", "policy", "eperson", null, userA.getFullName(), userA.getID().toString(), 600); @@ -1328,10 +1327,9 @@ public void patchReplaceMetadataContainedInAdministratorAndPublicBoxesTest() thr .withAuthor("Smith, Maria") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "A secured abstract"); @@ -1442,17 +1440,19 @@ public void patchReplaceMetadataContainedInBoxesWithCustomDataLayoutSecurityTest .withAuthor("Smith, Maria") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(userA) - .withGroup(groupA) .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, groupA) + .withDspaceObject(itemA) + .withAction(Constants.WRITE) + .build(); + + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "A secured abstract"); @@ -1589,15 +1589,15 @@ public void patchReplaceMetadataContainedInBoxesWithOnlyOwnerLayoutSecurityTest( .withDescriptionAbstract("A secured abstract") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson).build(); + .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(userA).build(); + .build(); MetadataField abs = mfss.findByElement(context, "dc", "description", "abstract"); MetadataField title = mfss.findByElement(context, "dc", "title", null); @@ -1698,10 +1698,9 @@ public void patchMoveMetadataContainedInAdministratorAndPublicBoxesTest() throws .withAuthor("Doe, John") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "First Abstract description"); @@ -1814,10 +1813,9 @@ public void patchMoveMetadataContainedInBoxesWithOnlyOwnerLayoutSecurityTest() t .withDescriptionAbstract("Second Abstract description") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson) .build(); MetadataField abs = mfss.findByElement(context, "dc", "description", "abstract"); @@ -1938,16 +1936,20 @@ public void patchMoveMetadataContainedInBoxesWithCustomDataLayoutSecurityTest() .withAuthor("Doe, John") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, userA, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(userA) - .withGroup(groupA).build(); + .build(); + + ResourcePolicyBuilder.createResourcePolicy(context, null, groupA) + .withDspaceObject(itemA) + .withAction(Constants.WRITE) + .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(eperson).build(); + .build(); itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "First Abstract description"); itemService.addMetadata(context, itemA, "dc", "description", "abstract", null, "Second Abstract description"); @@ -2112,7 +2114,10 @@ public void findOneWorkspaceItemUsingLayoutSecurityCustomDataTest() throws Excep context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); - getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID())) + getClient(token).perform( + get("/api/submission/workspaceitems/" + witem.getID()) + .param("embed", "item") + ) .andExpect(status().isOk()) .andExpect(jsonPath("$.sections.publication", Matchers.allOf( hasJsonPath("$['dc.contributor.author'][0].value", is("Smith, Donald")), @@ -2184,7 +2189,10 @@ public void findOneWorkflowItemUsingLayoutSecurityCustomDataTest() throws Except String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())) + getClient(authToken).perform( + get("/api/workflow/workflowitems/" + witem.getID()) + .param("embed", "item") + ) .andExpect(status().isOk()) .andExpect(jsonPath("$.sections.publication", Matchers.allOf( hasJsonPath("$['dc.contributor.author'][0].value", is("Smith, Donald")), @@ -2257,7 +2265,10 @@ public void findOneEditItemUsingLayoutSecurityCustomDataTest() throws Exception String tokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(tokenAdmin).perform(get("/api/core/edititem/" + itemA.getID() + ":MODE1")) + getClient(tokenAdmin).perform( + get("/api/core/edititems/" + itemA.getID() + ":MODE1") + .param("embed", "item") + ) .andExpect(status().isOk()) .andExpect(jsonPath("$.sections.traditionalpageone-cris", Matchers.allOf( hasJsonPath("$['dc.contributor.author'][0].value", is("Smith, Maria")), 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 d98041d0830d..c628922afbf6 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 @@ -239,6 +239,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)) @@ -252,7 +253,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/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index c16d81db4f15..d03db7823b59 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 @@ -42,9 +42,11 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.RelationshipTypeBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; +import org.dspace.content.EntityType; import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.Relationship; @@ -129,6 +131,19 @@ public void setUp() throws Exception { .withSubmissionDefinition("traditional") .build(); + EntityType publicationType = + entityTypeService.findByEntityType(context, "Publication"); + + RelationshipTypeBuilder.createRelationshipTypeBuilder( + context, + publicationType, + publicationType, + "isCorrectionOfItem", + "isCorrectedByItem", + 0, 1, + 0, 1 + ); + context.restoreAuthSystemState(); } @@ -1617,18 +1632,19 @@ private void removeTraditionalPageOneAuthorTest(int path, List expectedO */ private void patchAddEntireArray(List metadataValues) throws Exception { List ops = new ArrayList(); - List value = new ArrayList(); - // generates the MetadataValueRest list - metadataValues.stream().forEach(mv -> { - MetadataValueRest mrv = new MetadataValueRest(); - value.add(mrv); - mrv.setValue(mv.getValue()); - if (mv.getAuthority() != null && mv.getAuthority().startsWith("virtual::")) { - mrv.setAuthority(mv.getAuthority()); - mrv.setConfidence(mv.getConfidence()); - } - }); + List value = + metadataValues.stream() + .map(mv -> { + MetadataValueRest mrv = new MetadataValueRest(); + mrv.setValue(mv.getValue()); + if (mv.getAuthority() != null && mv.getAuthority().startsWith("virtual::")) { + mrv.setAuthority(mv.getAuthority()); + mrv.setConfidence(mv.getConfidence()); + } + return mrv; + }) + .collect(Collectors.toList()); AddOperation add = new AddOperation("/sections/traditionalpageone/dc.contributor.author", value); ops.add(add); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java index 2c6b14aa7e33..63808f8f1c62 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java @@ -7,7 +7,6 @@ */ package org.dspace.app.rest; -import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.dspace.content.Item.ANY; import static org.hamcrest.CoreMatchers.equalTo; @@ -236,7 +235,7 @@ private void initPersonProjectPublication() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = - itemService.getMetadata(projectItem, "dc", "contributor", "author", Item.ANY); + itemService.getMetadata(projectItem, "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertThat(projectAuthorList.get(0).getAuthority(), startsWith("virtual::")); @@ -522,31 +521,49 @@ public void testDeleteJournalRelationshipCopyToBothItems() throws Exception { public void deleteItemCopyVirtualMetadataAll() throws Exception { initPersonProjectPublication(); - for (Item item : asList(publicationItem, projectItem)) { - - // Verify the dc.contributor.author virtual metadata - assertEquals( - 1, - itemService.getMetadata(item, "dc", "contributor", "author", ANY).size() - ); - - // Verify there's no dc.contributor.author actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) - .collect(toList()).size() - ); - - // Verify there's no relation.isAuthorOfPublication actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "isAuthorOfPublication" - .equals(metadataValue.getMetadataField().getElement())) - .collect(toList()).size() - ); - } + // -- Initial checks for PublicationItem -- + // Verify the dc.contributor.author virtual metadata + assertEquals( + 1, + itemService.getMetadata(publicationItem, "dc", "contributor", "author", ANY).size() + ); + // Verify there's no dc.contributor.author actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .collect(toList()).size() + ); + // Verify there's no relation.isAuthorOfPublication actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "isAuthorOfPublication" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + + // -- Initial checks For ProjectItem -- + // Verify the project.investigator virtual metadata + assertEquals( + 1, + itemService.getMetadata(projectItem, "project", "investigator", ANY, ANY).size() + ); + // Verify there's no project.investigator actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + // Verify there's no relation.isPersonOfProject actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "isPersonOfProject" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); getClient(adminAuthToken).perform( delete("/api/core/items/" + personItem.getID() + "?copyVirtualMetadata=all")) @@ -583,7 +600,7 @@ public void deleteItemCopyVirtualMetadataAll() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = itemService.getMetadata(projectItem, - "dc", "contributor", "author", Item.ANY); + "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertNull(projectAuthorList.get(0).getAuthority()); @@ -591,11 +608,11 @@ public void deleteItemCopyVirtualMetadataAll() throws Exception { "relation", "isPersonOfProject", Item.ANY, Item.ANY); assertThat(projectRelationships.size(), equalTo(1)); - // Verify there's dc.contributor.author actual metadata on the project item + // Verify there's project.investigator actual metadata on the project item assertEquals( 1, projectItem.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) .collect(toList()) .size() ); @@ -615,31 +632,49 @@ public void deleteItemCopyVirtualMetadataAll() throws Exception { public void deleteItemCopyVirtualMetadataOneType() throws Exception { initPersonProjectPublication(); - for (Item item : asList(publicationItem, projectItem)) { - - // Verify the dc.contributor.author virtual metadata - assertEquals( - 1, - itemService.getMetadata(item, "dc", "contributor", "author", ANY).size() - ); - - // Verify there's no dc.contributor.author actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) - .collect(toList()).size() - ); - - // Verify there's no relation.isAuthorOfPublication actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "isAuthorOfPublication" - .equals(metadataValue.getMetadataField().getElement())) - .collect(toList()).size() - ); - } + // -- Initial checks for PublicationItem -- + // Verify the dc.contributor.author virtual metadata + assertEquals( + 1, + itemService.getMetadata(publicationItem, "dc", "contributor", "author", ANY).size() + ); + // Verify there's no dc.contributor.author actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .collect(toList()).size() + ); + // Verify there's no relation.isAuthorOfPublication actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "isAuthorOfPublication" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + + // -- Initial checks For ProjectItem -- + // Verify the project.investigator virtual metadata + assertEquals( + 1, + itemService.getMetadata(projectItem, "project", "investigator", ANY, ANY).size() + ); + // Verify there's no project.investigator actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + // Verify there's no relation.isPersonOfProject actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "isPersonOfProject" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); getClient(adminAuthToken).perform( delete("/api/core/items/" + personItem.getID() + "?copyVirtualMetadata=" @@ -677,7 +712,7 @@ public void deleteItemCopyVirtualMetadataOneType() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = itemService.getMetadata(projectItem, - "dc", "contributor", "author", Item.ANY); + "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(0)); List projectRelationships = itemService.getMetadata(projectItem, "relation", "isPersonOfProject", Item.ANY, Item.ANY); @@ -688,31 +723,49 @@ public void deleteItemCopyVirtualMetadataOneType() throws Exception { public void deleteItemCopyVirtualMetadataTwoTypes() throws Exception { initPersonProjectPublication(); - for (Item item : asList(publicationItem, projectItem)) { - - // Verify the dc.contributor.author virtual metadata - assertEquals( - 1, - itemService.getMetadata(item, "dc", "contributor", "author", ANY).size() - ); - - // Verify there's no dc.contributor.author actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) - .collect(toList()).size() - ); - - // Verify there's no relation.isAuthorOfPublication actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "isAuthorOfPublication" - .equals(metadataValue.getMetadataField().getElement())) - .collect(toList()).size() - ); - } + // -- Initial checks for PublicationItem -- + // Verify the dc.contributor.author virtual metadata + assertEquals( + 1, + itemService.getMetadata(publicationItem, "dc", "contributor", "author", ANY).size() + ); + // Verify there's no dc.contributor.author actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .collect(toList()).size() + ); + // Verify there's no relation.isAuthorOfPublication actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "isAuthorOfPublication" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + + // -- Initial checks For ProjectItem -- + // Verify the project.investigator virtual metadata + assertEquals( + 1, + itemService.getMetadata(projectItem, "project", "investigator", ANY, ANY).size() + ); + // Verify there's no project.investigator actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + // Verify there's no relation.isPersonOfProject actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "isPersonOfProject" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); getClient(adminAuthToken).perform( delete("/api/core/items/" + personItem.getID() @@ -751,7 +804,7 @@ public void deleteItemCopyVirtualMetadataTwoTypes() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = itemService.getMetadata(projectItem, - "dc", "contributor", "author", Item.ANY); + "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertNull(projectAuthorList.get(0).getAuthority()); @@ -759,11 +812,11 @@ public void deleteItemCopyVirtualMetadataTwoTypes() throws Exception { "relation", "isPersonOfProject", Item.ANY, Item.ANY); assertThat(projectRelationships.size(), equalTo(1)); - // Verify there's dc.contributor.author actual metadata on the project item + // Verify there's project.investigator actual metadata on the project item assertEquals( 1, projectItem.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) .collect(toList()) .size() ); @@ -827,7 +880,7 @@ public void deleteItemCopyVirtualMetadataInvalid() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = - itemService.getMetadata(projectItem, "dc", "contributor", "author", Item.ANY); + itemService.getMetadata(projectItem, "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertThat(projectAuthorList.get(0).getAuthority(), startsWith("virtual::")); @@ -840,31 +893,49 @@ public void deleteItemCopyVirtualMetadataInvalid() throws Exception { public void deleteItemCopyVirtualMetadataAllNoPermissions() throws Exception { initPersonProjectPublication(); - for (Item item : asList(publicationItem, projectItem)) { - - // Verify the dc.contributor.author virtual metadata - assertEquals( - 1, - itemService.getMetadata(item, "dc", "contributor", "author", ANY).size() - ); - - // Verify there's no dc.contributor.author actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) - .collect(toList()).size() - ); - - // Verify there's no relation.isAuthorOfPublication actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "isAuthorOfPublication" - .equals(metadataValue.getMetadataField().getElement())) - .collect(toList()).size() - ); - } + // -- Initial checks for PublicationItem -- + // Verify the dc.contributor.author virtual metadata + assertEquals( + 1, + itemService.getMetadata(publicationItem, "dc", "contributor", "author", ANY).size() + ); + // Verify there's no dc.contributor.author actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .collect(toList()).size() + ); + // Verify there's no relation.isAuthorOfPublication actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "isAuthorOfPublication" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + + // -- Initial checks For ProjectItem -- + // Verify the project.investigator virtual metadata + assertEquals( + 1, + itemService.getMetadata(projectItem, "project", "investigator", ANY, ANY).size() + ); + // Verify there's no project.investigator actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + // Verify there's no relation.isPersonOfProject actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "isPersonOfProject" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); getClient(getAuthToken(eperson.getEmail(), password)).perform( delete("/api/core/items/" + personItem.getID())) @@ -882,7 +953,7 @@ public void deleteItemCopyVirtualMetadataAllNoPermissions() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = - itemService.getMetadata(projectItem, "dc", "contributor", "author", Item.ANY); + itemService.getMetadata(projectItem, "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertThat(projectAuthorList.get(0).getAuthority(), startsWith("virtual::")); @@ -995,7 +1066,7 @@ public void deleteItemCopyVirtualMetadataAllInsufficientPermissions() throws Exc projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = - itemService.getMetadata(projectItem, "dc", "contributor", "author", Item.ANY); + itemService.getMetadata(projectItem, "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertThat(projectAuthorList.get(0).getAuthority(), startsWith("virtual::")); @@ -1027,7 +1098,7 @@ public void deleteItemCopyVirtualMetadataTypeInsufficientPermissions() throws Ex projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = - itemService.getMetadata(projectItem, "dc", "contributor", "author", Item.ANY); + itemService.getMetadata(projectItem, "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertThat(projectAuthorList.get(0).getAuthority(), startsWith("virtual::")); @@ -1040,31 +1111,49 @@ public void deleteItemCopyVirtualMetadataTypeInsufficientPermissions() throws Ex public void deleteItemCopyVirtualMetadataConfigured() throws Exception { initPersonProjectPublication(); - for (Item item : asList(publicationItem, projectItem)) { - - // Verify the dc.contributor.author virtual metadata - assertEquals( - 1, - itemService.getMetadata(item, "dc", "contributor", "author", ANY).size() - ); - - // Verify there's no dc.contributor.author actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) - .collect(toList()).size() - ); - - // Verify there's no relation.isAuthorOfPublication actual metadata on the item - assertEquals( - 0, - item.getMetadata().stream() - .filter(metadataValue -> "isAuthorOfPublication" - .equals(metadataValue.getMetadataField().getElement())) - .collect(toList()).size() - ); - } + // -- Initial checks for PublicationItem -- + // Verify the dc.contributor.author virtual metadata + assertEquals( + 1, + itemService.getMetadata(publicationItem, "dc", "contributor", "author", ANY).size() + ); + // Verify there's no dc.contributor.author actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .collect(toList()).size() + ); + // Verify there's no relation.isAuthorOfPublication actual metadata on the item + assertEquals( + 0, + publicationItem.getMetadata().stream() + .filter(metadataValue -> "isAuthorOfPublication" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + + // -- Initial checks For ProjectItem -- + // Verify the project.investigator virtual metadata + assertEquals( + 1, + itemService.getMetadata(projectItem, "project", "investigator", ANY, ANY).size() + ); + // Verify there's no project.investigator actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); + // Verify there's no relation.isPersonOfProject actual metadata on the item + assertEquals( + 0, + projectItem.getMetadata().stream() + .filter(metadataValue -> "isPersonOfProject" + .equals(metadataValue.getMetadataField().getElement())) + .collect(toList()).size() + ); getClient(adminAuthToken).perform( delete("/api/core/items/" + personItem.getID() + "?copyVirtualMetadata=configured")) @@ -1081,7 +1170,7 @@ public void deleteItemCopyVirtualMetadataConfigured() throws Exception { projectItem = itemService.find(context, projectItem.getID()); List projectAuthorList = itemService.getMetadata(projectItem, - "dc", "contributor", "author", Item.ANY); + "project", "investigator", Item.ANY, Item.ANY); assertThat(projectAuthorList.size(), equalTo(1)); assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); assertNull(projectAuthorList.get(0).getAuthority()); @@ -1089,11 +1178,11 @@ public void deleteItemCopyVirtualMetadataConfigured() throws Exception { "relation", "isPersonOfProject", Item.ANY, Item.ANY); assertThat(projectRelationships.size(), equalTo(1)); - // Verify there's dc.contributor.author actual metadata on the project item + // Verify there's project.investigator actual metadata on the project item assertEquals( 1, projectItem.getMetadata().stream() - .filter(metadataValue -> "author".equals(metadataValue.getMetadataField().getQualifier())) + .filter(metadataValue -> "investigator".equals(metadataValue.getMetadataField().getElement())) .collect(toList()) .size() ); 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 9996c8dbc82c..476eb5382d9d 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 @@ -2330,6 +2330,77 @@ 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) + ))) + ; + + // 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 public void putRelationshipWithNonexistentID() throws Exception { context.turnOffAuthorisationSystem(); 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..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 @@ -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(); @@ -1005,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) @@ -1052,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 { @@ -1143,9 +1228,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 +1254,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 +1283,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 +1335,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 +1405,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 +1467,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 +1528,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 +1594,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 +1655,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 +1706,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 +1768,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 +1874,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 +1931,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 +1983,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 +2034,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 +2076,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 +2122,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 +2178,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 +2226,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 +2277,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 +2325,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 +2370,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 +2413,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 +2452,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 +2491,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 +2541,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 +2576,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 +2620,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 +2664,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 +2705,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 +2745,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 +2794,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 +2834,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 +2882,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 +2931,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 +2994,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 +3084,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 +3163,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 +3280,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 +3326,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 +3373,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 +3422,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 +3471,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 +3521,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 +3562,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 +3594,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 +3656,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 +3680,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 +3699,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 +3727,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/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index 90567de6725c..bb174dffa6cd 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 @@ -113,8 +113,7 @@ public void scopusImportMetadataGetRecordsEmptyResponseTest() 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); @@ -307,4 +306,4 @@ private String getResponseWithCreatorAndWithoutAuthorsElement() { ""; } -} \ No newline at end of file +} 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(); 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 adf2ea830613..8a45e5d3e9a6 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 @@ -311,10 +311,9 @@ public void usagereport_loggedInUserReadRights() throws Exception { // ** WHEN ** context.turnOffAuthorisationSystem(); authorizeService.removeAllPolicies(context, itemNotVisitedWithBitstreams); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(itemNotVisitedWithBitstreams) - .withAction(Constants.READ) - .withUser(eperson).build(); + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withDspaceObject(itemNotVisitedWithBitstreams) + .withAction(Constants.READ).build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) .withEmail("eperson1@mail.com") @@ -347,10 +346,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") @@ -1337,10 +1335,9 @@ public void usagereportSearch_loggedInUserReadRights() throws Exception { // ** WHEN ** context.turnOffAuthorisationSystem(); authorizeService.removeAllPolicies(context, itemNotVisitedWithBitstreams); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(itemNotVisitedWithBitstreams) - .withAction(Constants.READ) - .withUser(eperson).build(); + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) + .withDspaceObject(itemNotVisitedWithBitstreams) + .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/StatisticsRestSearchByCategoryRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestSearchByCategoryRepositoryIT.java index c9afc668ea7b..a5a015b9b9e7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestSearchByCategoryRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestSearchByCategoryRepositoryIT.java @@ -162,10 +162,10 @@ public void setUp() throws Exception { project2Item = ItemBuilder.createItem(context, collectionProjects).withTitle("Project 2") .withProjectCoinvestigators("Person#1", personItem.getID().toString()).build(); authorizeService.removeAllPolicies(context, project2Item); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(project2Item) .withAction(Constants.READ) - .withUser(eperson).build(); + .build(); project3Item = ItemBuilder.createItem(context, collectionProjects).withTitle("Project 3") .withProjectInvestigator("Person#2", person2Item.getID().toString()).build(); @@ -196,10 +196,10 @@ public void setUp() throws Exception { bitstream2Visited2 = BitstreamBuilder.createBitstream(context, publication2Item, toInputStream("test", UTF_8)).withName("Bitstream2Visited2Name").build(); authorizeService.removeAllPolicies(context, bitstream2Visited2); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, eperson, null) .withDspaceObject(bitstream2Visited2) .withAction(Constants.READ) - .withUser(eperson).build(); + .build(); bitstream3NotVisited = BitstreamBuilder.createBitstream(context, publication3Item, toInputStream("test", UTF_8)).withName("Bitstream3NotVisitedName").build(); bitstreamProjVisited = BitstreamBuilder.createBitstream(context, 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 743e7def3404..d21c54fba821 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 @@ -44,6 +44,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 = 22; + @Test public void findAll() throws Exception { //When we call the root endpoint as anonymous user @@ -307,51 +312,231 @@ 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("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.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=19"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(20))) - .andExpect(jsonPath("$.page.totalPages", is(20))) - .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("funding"))) - .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=19"), Matchers.containsString("size=1")))) - .andExpect(jsonPath("$.page.size", is(1))) - .andExpect(jsonPath("$.page.totalElements", is(20))) - .andExpect(jsonPath("$.page.totalPages", is(20))) - .andExpect(jsonPath("$.page.number", is(1))); + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("traditional-with-custom-url"))) + .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("traditional-with-correction"))) + .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("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=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")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("traditional-cris"))) + .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=3"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.next.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=5"), Matchers.containsString("size=1")))) + .andExpect(jsonPath("$._links.self.href", Matchers.allOf( + Matchers.containsString("/api/config/submissiondefinitions?"), + Matchers.containsString("page=4"), 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(4))); + + 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("publication"))) + .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(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("patent"))) + .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("person"))) + .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("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=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))); + } @Test @@ -376,9 +561,9 @@ public void testFindAllSortedAlphabetically() throws Exception { //The array of browse index should have a size greater or equals to 1 .andExpect(jsonPath("$._embedded.submissiondefinitions", hasSize(greaterThanOrEqualTo(1)))) .andDo(result -> - jsonArrayRef.set( - read(result.getResponse().getContentAsString(), "$._embedded.submissiondefinitions") - )); + jsonArrayRef.set( + read(result.getResponse().getContentAsString(), "$._embedded.submissiondefinitions") + )); List submissionDefinitionRests = jsonArrayRef.get().stream().collect(Collectors.toList()) @@ -398,12 +583,12 @@ public void testFindAllSortedAlphabetically() throws Exception { assertTrue( isSorted(submissionDefinitionRests, - Comparator.comparing(SubmissionDefinitionRest::getName)) + Comparator.comparing(SubmissionDefinitionRest::getName)) ); assertTrue( isSorted(submissionDefinitionRests, - Comparator.comparing(SubmissionDefinitionRest::getId)) + Comparator.comparing(SubmissionDefinitionRest::getId)) ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java index 16d4580594ff..22f33293b620 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java @@ -404,6 +404,7 @@ public void acceptSuggestionTest() throws Exception { ObjectMapper mapper = new ObjectMapper(); MvcResult mvcResult = getClient(adminToken).perform( post("/api/submission/workspaceitems?owningCollection=" + colPublications.getID().toString()) + .param("embed", "item") .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) .content("http://localhost/api/integration/externalsources/" + MockSuggestionExternalDataSource.NAME + "/entryValues/" + suggestionId)) @@ -413,7 +414,8 @@ public void acceptSuggestionTest() throws Exception { workspaceItemId = (Integer) map.get("id"); String itemUuidString = String.valueOf(((Map) ((Map) map.get("_embedded")).get("item")).get("uuid")); - getClient(adminToken).perform(get("/api/submission/workspaceitems/" + workspaceItemId)) + getClient(adminToken).perform( + get("/api/submission/workspaceitems/" + workspaceItemId).param("embed", "item")) .andExpect(status().isOk()) .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.id", is(workspaceItemId)), 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 59fddc0e5fbf..ca034840b2d9 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 @@ -110,6 +110,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(); @@ -445,6 +450,20 @@ public void findByLabelAndValueTest() throws Exception { .andExpect(jsonPath("$.page.size", Matchers.is(10))); } + @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(); 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/WorkspaceItemFromTemplateIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemFromTemplateIT.java index 148da3cc0050..bc7b725a52d2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemFromTemplateIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemFromTemplateIT.java @@ -55,11 +55,14 @@ public void createSingleWorkspaceItemWithTemplate() throws Exception { String authToken = getAuthToken(eperson.getEmail(), password); + context.commit(); context.restoreAuthSystemState(); final String today = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now()); getClient(authToken).perform(post("/api/submission/workspaceitems") + .param("embed", "item") + .param("embed", "collection") .param("owningCollection", col1.getID().toString()) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) 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()))); + + } + + +} 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 d2381ca43818..248915a85449 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 @@ -181,6 +181,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration private Group embargoedGroups; private Group embargoedGroup1; private Group embargoedGroup2; + private Group adminGroup; private Group anonymousGroup; private EntityType publicationType; private EntityType journalType; @@ -220,6 +221,8 @@ public void setUp() throws Exception { if (orgUnitType == null) { orgUnitType = EntityTypeBuilder.createEntityTypeBuilder(context, "OrgUnit").build(); } + adminGroup = EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ADMIN); + context.restoreAuthSystemState(); } @@ -954,6 +957,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()))) @@ -962,6 +966,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()))) @@ -970,10 +975,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"))); @@ -1025,7 +1030,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", @@ -1054,6 +1060,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", @@ -1119,7 +1126,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]" + @@ -1153,6 +1161,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]" + @@ -1218,7 +1227,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." + @@ -1249,6 +1259,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." + @@ -1317,7 +1328,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]" + @@ -1357,6 +1369,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]" + @@ -1426,7 +1439,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", @@ -1466,6 +1480,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" @@ -1547,7 +1562,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", @@ -1629,7 +1645,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", @@ -1709,7 +1726,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", @@ -1790,7 +1808,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", @@ -1873,7 +1892,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", @@ -1958,7 +1978,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", @@ -1993,6 +2015,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", @@ -2070,7 +2093,8 @@ public void createSingleWorkspaceItemsFromSingleFileWithMultipleEntriesTest() th // create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1) getClient(authToken) .perform( - multipart("/api/submission/workspaceitems").file(bibtexFile) + multipart("/api/submission/workspaceitems") + .file(bibtexFile) ) // bulk create should return 200, 201 (created) is better for single resource .andExpect(status().isOk()) @@ -2080,36 +2104,18 @@ public void createSingleWorkspaceItemsFromSingleFileWithMultipleEntriesTest() th is("My Article") ) ) - .andExpect( - jsonPath( - "$._embedded.workspaceitems[0]._embedded.collection.id", - is(col1.getID().toString()) - ) - ) .andExpect( jsonPath( "$._embedded.workspaceitems[1].sections.traditionalpageone['dc.title'][0].value", is("My Article 2") ) ) - .andExpect( - jsonPath( - "$._embedded.workspaceitems[1]._embedded.collection.id", - is(col1.getID().toString()) - ) - ) .andExpect( jsonPath( "$._embedded.workspaceitems[2].sections.traditionalpageone['dc.title'][0].value", is("My Article 3") ) ) - .andExpect( - jsonPath( - "$._embedded.workspaceitems[2]._embedded.collection.id", - is(col1.getID().toString()) - ) - ) .andExpect( jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist()); getClient(authToken) @@ -2125,24 +2131,12 @@ public void createSingleWorkspaceItemsFromSingleFileWithMultipleEntriesTest() th is("My Article") ) ) - .andExpect( - jsonPath( - "$._embedded.workspaceitems[0]._embedded.collection.id", - is(col2.getID().toString()) - ) - ) .andExpect( jsonPath( "$._embedded.workspaceitems[1].sections.traditionalpageone['dc.title'][0].value", is("My Article 2") ) ) - .andExpect( - jsonPath( - "$._embedded.workspaceitems[1]._embedded.collection.id", - is(col2.getID().toString()) - ) - ) .andExpect( jsonPath( "$._embedded.workspaceitems[2].sections.traditionalpageone['dc.title'][0].value", @@ -2254,13 +2248,13 @@ public void createPubmedWorkspaceItemFromFileTest() throws Exception { } @Test + @Ignore /** * Test the creation of a workspaceitem POSTing to the resource collection endpoint a PDF file. As a single item * will be created we expect to have the pdf file stored as a bitstream * * @throws Exception */ - @Ignore public void createWorkspaceItemFromPDFFileTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -2366,6 +2360,8 @@ public void createSingleWorkspaceItemWithTemplate() throws Exception { getClient(authToken).perform(post("/api/submission/workspaceitems") .param("owningCollection", col1.getID().toString()) + .param("embed", "item") + .param("embed", "collection") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.item.metadata['dc.title'][0].value", is("SimpleTitle"))) @@ -5256,6 +5252,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")) @@ -5266,7 +5263,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)), @@ -5461,6 +5459,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/" + @@ -5470,7 +5469,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)), @@ -6647,6 +6648,7 @@ public void createEmptyWorkspaceItemWithEntityTypeTest() throws Exception { getClient(authToken).perform(post("/api/submission/workspaceitems") .param("entityType", "Publication") .param("projection", "full") + .param("embed", "collection") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.collection.metadata.['dspace.entity.type'][0].value", @@ -6657,6 +6659,7 @@ public void createEmptyWorkspaceItemWithEntityTypeTest() throws Exception { // create a workspaceitem explicitly with entityType Journal getClient(authToken).perform(post("/api/submission/workspaceitems") .param("entityType", "Journal") + .param("embed", "collection") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.collection.metadata.['dspace.entity.type'][0].value", @@ -6773,6 +6776,7 @@ public void testWorkspaceItemPoliciesWithSharedWorkspace() throws Exception { getClient(getAuthToken(submitter1.getEmail(), password)).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()))) @@ -6852,6 +6856,7 @@ public void testWorkspaceItemPoliciesWithoutSharedWorkspace() throws Exception { getClient(getAuthToken(submitter1.getEmail(), password)).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()))) @@ -8170,13 +8175,12 @@ public void patchAccesConditionDiscoverableTest() throws Exception { .build(); witem.getItem().setDiscoverable(false); - ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(witem.getItem()) + ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup).withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("administrator") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -8230,7 +8234,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") @@ -8277,7 +8281,7 @@ public void patcDiscoverableWithAccesConditionConfigurationDiscoverableDisabledT .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -8330,7 +8334,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") @@ -8388,7 +8392,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") @@ -8503,13 +8507,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") @@ -8563,17 +8567,17 @@ 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") - .build(); +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); Calendar calendar = Calendar.getInstance(); @@ -8583,7 +8587,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") @@ -8645,17 +8649,17 @@ 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") - .build(); +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -8708,17 +8712,17 @@ 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") - .build(); +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -8773,17 +8777,17 @@ 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") - .build(); +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -8838,17 +8842,17 @@ 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") - .build(); +ResourcePolicyBuilder.createResourcePolicy(context, null, adminGroup) + .withDspaceObject(witem.getItem()) + .withPolicyType(TYPE_CUSTOM) + .withName("administrator") + .build(); context.restoreAuthSystemState(); @@ -8908,7 +8912,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") @@ -8922,7 +8926,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") @@ -8988,7 +8992,7 @@ public void patchReplaceAccesConditionsFromOpenaccessToAdministratorTest() throw .withSubject("ExtraEntry") .build(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, null, anonymousGroup) .withDspaceObject(witem.getItem()) .withPolicyType(TYPE_CUSTOM) .withName("openaccess") @@ -9458,6 +9462,7 @@ public void testIgnoredMetadataFieldsWithCorrectionSubmissionDefinition() throws .param("owningCollection", collection.getID().toString()) .param("item", item.getID().toString()) .param("relationship", "isCorrectionOfItem") + .param("embed", "item") .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.item.metadata['dc.title'].[0].value", is ("Test item"))) 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 abb52f374ba1..3acbc34a1376 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 @@ -289,10 +289,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()); @@ -309,4 +308,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/EditMetadataFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditMetadataFeatureIT.java index 0c24f7830769..596a58baeabf 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditMetadataFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EditMetadataFeatureIT.java @@ -90,10 +90,9 @@ public void checkCanEditMetadataFeatureTest() throws Exception { context.turnOffAuthorisationSystem(); configurationService.setProperty("edit.metadata.allowed-group", groupA.getID()); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, user, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(user) .build(); context.restoreAuthSystemState(); @@ -130,10 +129,9 @@ public void checkCanEditMetadataFeatureTest() throws Exception { public void checkCanEditMetadataFeatureWithDefaulGroupUnsetTest() throws Exception { context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, user, null) .withDspaceObject(itemA) .withAction(Constants.WRITE) - .withUser(user) .build(); context.restoreAuthSystemState(); 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 0ac7eea4250d..6f5af480f755 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 @@ -150,25 +150,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) @@ -670,10 +666,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(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1Writer, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(item1Writer) .build(); context.restoreAuthSystemState(); @@ -690,10 +685,9 @@ public void testCanMoveWriter() throws Exception { // grant item 1 write REMOVE permissions on the item’s owning collection context.turnOffAuthorisationSystem(); - ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicyBuilder.createResourcePolicy(context, item1Writer, null) .withDspaceObject(collectionX) .withAction(Constants.REMOVE) - .withUser(item1Writer) .build(); context.restoreAuthSystemState(); @@ -765,10 +759,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); @@ -923,10 +916,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); @@ -938,10 +930,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 @@ -952,10 +943,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 @@ -981,10 +971,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); @@ -1007,10 +996,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); @@ -1037,10 +1025,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); @@ -1067,10 +1054,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); @@ -1087,10 +1073,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); @@ -1107,15 +1092,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); @@ -1142,10 +1125,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); @@ -1172,10 +1154,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); @@ -1193,15 +1174,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); @@ -1353,10 +1332,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); @@ -1373,10 +1351,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); @@ -1394,25 +1371,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); @@ -1460,15 +1433,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); @@ -1491,4 +1462,4 @@ private ResultActions getAuthorizationFeatures(String adminToken, String uri, in ) ); } -} \ 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(); 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 be0a27b4ebd1..54339aa1d6f0 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..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 @@ -9,19 +9,40 @@ 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.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.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 +62,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 +94,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 +113,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 +134,333 @@ 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(); + + // 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 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 + // 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 - 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)); + 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 - @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 +468,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("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/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"}} 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"}} 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 + diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/sword2/example.zip b/dspace-server-webapp/src/test/resources/org/dspace/app/sword2/example.zip new file mode 100644 index 000000000000..3426c6730edd Binary files /dev/null and b/dspace-server-webapp/src/test/resources/org/dspace/app/sword2/example.zip differ 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-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..d3394399301f --- /dev/null +++ b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java @@ -0,0 +1,64 @@ +/* + * 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 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 { + + 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) { + 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 null; + } + } + + @Override + public String[] getPropertyNames() { + final List keys = new ArrayList<>(); + source.getKeys().forEachRemaining(keys::add); + return keys.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } +} diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 7ddb846cfc89..d4383cc0b35a 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/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; 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/ContainerManagerDSpace.java b/dspace-swordv2/src/main/java/org/dspace/sword2/ContainerManagerDSpace.java index 454afd80dc1c..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,7 +755,7 @@ 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); 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())); diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index ffa6a4a84fdc..9d854d602672 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -3,24 +3,28 @@ - + 10.5072/dspace- @@ -47,9 +51,9 @@ properties are in the metadata of the item to export. The classe named above respects this. --> - + - + - + + + + + + + + Other + Other + + + - + - + + + @@ -247,7 +270,7 @@ - @@ -256,6 +279,13 @@ + + - + + @@ -296,9 +334,14 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - + - + + + + + + @@ -330,16 +373,20 @@ + + AlternativeTitle + Subtitle + TranslatedTitle @@ -358,6 +405,7 @@ --> + @@ -453,7 +501,7 @@ dissertations. DataCite uses submitted for the "date the creator submits the resource to the publisher". --> - Issued + Submitted Updated @@ -490,9 +538,9 @@ Audiovisual - Text - Text - Text + JournalArticle + Book + BookChapter Dataset InteractiveResource Image @@ -500,14 +548,14 @@ Model Other Model - Text - Text + Preprint + Other Sound Sound Sound Software - Text - Text + Report + Dissertation Audiovisual Text Other @@ -529,7 +577,7 @@ resolveUrlToHandle(context, altId) until one is recognized or all have been tested. --> - + @@ -578,20 +626,9 @@ Adds Rights information --> - - - - - - - - - - - - - - + + + + Abstract diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index d89e793e01e6..fbdeb2bc863f 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -627,8 +627,8 @@ 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 @@ -894,6 +894,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 @@ -1133,7 +1138,7 @@ cc.license.classfilter = recombo, mark, publicdomain # 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 @@ -1203,16 +1208,6 @@ webui.preview.brand.fontpoint = 12 # webui.strengths.cache = true -###### ItemCounter Configuration ###### -# -# Define the DAO class to use. This must correspond to your choice of -# storage for the browse system (Solr is only option at this time). -# By default, the Solr implementation is used. -# -# Solr: -# ItemCountDAO.class = org.dspace.browse.ItemCountDAOSolr - - ###### Browse Configuration ###### # # Define the DAO class to use this must meet your storage choice for @@ -1433,7 +1428,7 @@ webui.browse.link.1 = author:dc.contributor.* #ance.webservice.addjournal.endpoint = https://webservice.cineca.it/pubblicazioni ### DLExporter URL -loginmiur.dlexporter.accesstoken = +loginmiur.dlexporter.accesstoken = loginmiur.dlexporter.url = /dlexporter?accessToken=${loginmiur.dlexporter.accesstoken} @@ -1458,8 +1453,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) @@ -1564,9 +1557,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 +# 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 ##### diff --git a/dspace/config/emails/batch_import_error b/dspace/config/emails/batch_import_error new file mode 100644 index 000000000000..2c62d72bf9c9 --- /dev/null +++ b/dspace/config/emails/batch_import_error @@ -0,0 +1,19 @@ +## Email sent to DSpace users when their 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 + diff --git a/dspace/config/modules/identifiers.cfg b/dspace/config/modules/identifiers.cfg index 63a9cda30f17..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 @@ -41,9 +36,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') diff --git a/dspace/config/modules/swordv2-server.cfg b/dspace/config/modules/swordv2-server.cfg index 2e60a7d2c251..ccb7760e6d02 100644 --- a/dspace/config/modules/swordv2-server.cfg +++ b/dspace/config/modules/swordv2-server.cfg @@ -297,6 +297,7 @@ plugin.named.org.dspace.sword2.SwordContentDisseminator = \ plugin.named.org.dspace.sword2.SwordStatementDisseminator = \ org.dspace.sword2.AtomStatementDisseminator = atom, \ org.dspace.sword2.OreStatementDisseminator = rdf, \ + org.dspace.sword2.AtomStatementDisseminator = application/atom+xml, \ org.dspace.sword2.AtomStatementDisseminator = application/atom+xml_type_feed, \ org.dspace.sword2.OreStatementDisseminator = application/rdf+xml 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 + + diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index 58e7be388ad8..c18ed1d3f08f 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -31,6 +31,9 @@ + + + diff --git a/dspace/config/spring/api/crossref-integration.xml b/dspace/config/spring/api/crossref-integration.xml index f1363fae322e..2c481e0f01f2 100644 --- a/dspace/config/spring/api/crossref-integration.xml +++ b/dspace/config/spring/api/crossref-integration.xml @@ -132,8 +132,11 @@ - + + + + diff --git a/dspace/config/spring/api/discovery.xml b/dspace/config/spring/api/discovery.xml index fa865b7aa3de..e8a2e54412f0 100644 --- a/dspace/config/spring/api/discovery.xml +++ b/dspace/config/spring/api/discovery.xml @@ -3552,18 +3552,18 @@ - + - + - + diff --git a/dspace/config/spring/api/identifier-service.xml b/dspace/config/spring/api/identifier-service.xml index 958a0b5d5ee3..56a7b2657932 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"> - + + + - + + on the filters defined in item-filters.xml, eg. + Of course, you can use a filter on the VersionedDOIIdentifierProvider as well. + --> - 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 @@ - + addon-importplus @@ -353,7 +353,7 @@ - + ${basedir}/../../.. diff --git a/dspace/solr/search/conf/schema.xml b/dspace/solr/search/conf/schema.xml index 1a987843d79c..e32c496e0c6a 100644 --- a/dspace/solr/search/conf/schema.xml +++ b/dspace/solr/search/conf/schema.xml @@ -163,6 +163,7 @@ + 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: + + + + + + + + + + + + + + + + + /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 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 00dde2e83187..c9b87c904f17 100644 --- a/dspace/src/main/docker-compose/docker-compose-angular.yml +++ b/dspace/src/main/docker-compose/docker-compose-angular.yml @@ -6,9 +6,12 @@ # http://www.dspace.org/license/ # -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 +27,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..6f5470e5d157 100644 --- a/dspace/src/main/docker-compose/docker-compose-iiif.yml +++ b/dspace/src/main/docker-compose/docker-compose-iiif.yml @@ -10,9 +10,12 @@ # Test environment for DSpace + Cantaloupe for IIIF support. See README for instructions. # This should NEVER be used in production scenarios. # -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 +24,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..f7fb2dcbd1ae 100644 --- a/dspace/src/main/docker-compose/docker-compose-shibboleth.yml +++ b/dspace/src/main/docker-compose/docker-compose-shibboleth.yml @@ -10,9 +10,12 @@ # 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: - 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 +25,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 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..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,15 +33,14 @@ then psql -U $POSTGRES_USER < ${LOCALSQL} touch $CHECKFILE - exit 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; 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/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 diff --git a/pom.xml b/pom.xml index 5fc18c075c70..5fcaccf21abb 100644 --- a/pom.xml +++ b/pom.xml @@ -19,36 +19,36 @@ 11 - 5.3.27 - 2.7.12 - 5.7.8 + 5.3.34 + 2.7.18 + 5.7.11 5.6.15.Final 6.2.5.Final - 42.6.0 - 8.11.2 + 42.7.3 + 8.11.3 - 3.4.0 + 3.10.8 2.10.0 - 2.13.4 - 2.13.4.2 + 2.16.0 + 2.16.0 1.3.2 2.3.1 - 2.3.1 - 1.1.0 + 2.3.9 + 1.1.1 - 9.4.53.v20231009 - 2.20.0 - 2.0.28 + 9.4.54.v20240208 + 2.23.1 + 2.0.31 1.19.0 1.7.36 - 2.5.0 + 2.9.2 - 1.70 + 1.78.1 - 2.6.0 + 2.9.0 7.9 @@ -58,7 +58,7 @@ https://jena.apache.org/documentation/migrate_jena2_jena3.html --> 2.13.0 - 2.35 + 2.39.1 [cris-2023.02.06-SNAPSHOT,cris-2023.02.07-SNAPSHOT) @@ -71,7 +71,7 @@ ${addons.version} ${addons.version} ${addons.version} - + UTF-8 ${project.build.sourceEncoding} @@ -269,7 +269,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 + 3.3.1 verify-style @@ -299,14 +299,14 @@ com.puppycrawl.tools checkstyle - 8.30 + 8.38 com.github.spotbugs spotbugs-maven-plugin - 4.0.4 + 4.8.2.0 Max Low @@ -316,7 +316,7 @@ com.github.spotbugs spotbugs - 4.1.2 + 4.8.2 @@ -346,7 +346,7 @@ maven-assembly-plugin - 3.2.0 + 3.6.0 org.apache.maven.plugins @@ -444,6 +444,8 @@ + + **/src/main/java/org/dspace/servicemanager/config/DSpaceConfigurationPropertySource.java **/src/test/resources/** **/src/test/data/** **/src/main/license/** @@ -718,7 +720,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 @@ -739,7 +741,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 @@ -752,7 +754,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 @@ -947,7 +949,7 @@ - + @@ -1385,6 +1387,12 @@ ${spring.version} + + org.springframework + spring-context-support + ${spring.version} + + spring-tx org.springframework @@ -1480,20 +1488,26 @@ org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on + ${bouncycastle.version} + + + + org.bouncycastle + bcprov-jdk18on ${bouncycastle.version} org.bouncycastle - bcprov-jdk15on + bcutil-jdk18on ${bouncycastle.version} org.apache.james apache-mime4j-core - 0.8.4 + 0.8.10 @@ -1527,7 +1541,7 @@ org.apache.ant ant - 1.10.11 + 1.10.14 org.apache.jena @@ -1549,7 +1563,7 @@ net.handle handle - 9.3.0 + 9.3.1 @@ -1642,17 +1656,17 @@ commons-cli commons-cli - 1.4 + 1.6.0 commons-codec commons-codec - 1.10 + 1.16.0 org.apache.commons commons-collections4 - 4.1 + 4.4 + commons-logging commons-logging - 1.2 + 1.3.0 + + + org.apache.commons + commons-compress + 1.26.0 org.apache.commons commons-pool2 - 2.11.1 + 2.12.0 org.apache.commons @@ -1707,12 +1726,12 @@ commons-validator commons-validator - 1.5.0 + 1.7 joda-time joda-time - 2.9.2 + 2.12.5 com.sun.mail @@ -1729,13 +1748,7 @@ jaxen jaxen - 1.1.6 - - - xom - xom - - + 2.0.0 org.jdom @@ -1817,17 +1830,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 @@ -1858,21 +1871,21 @@ - junit - junit - 4.13.1 + 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 @@ -1885,7 +1898,7 @@ com.h2database h2 - 2.2.220 + 2.2.224 test @@ -1929,7 +1942,7 @@ com.google.code.findbugs jsr305 - 3.0.1 + 3.0.2 provided @@ -1943,7 +1956,7 @@ com.fasterxml classmate - 1.3.0 + 1.6.0 com.fasterxml.jackson.core @@ -1975,7 +1988,7 @@ xom xom - 1.2.5 + 1.3.9 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