diff --git a/.github/actions/project-management-action/Dockerfile b/.github/actions/project-management-action/Dockerfile deleted file mode 100644 index 1d3301259e4e..000000000000 --- a/.github/actions/project-management-action/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -# Container image that runs your code -FROM alpine:3.10 - -RUN apk add --no-cache --no-progress curl jq - -# Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh -RUN chmod 777 /entrypoint.sh -# Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/project-management-action/LICENSE b/.github/actions/project-management-action/LICENSE deleted file mode 100644 index c4f50f8a29e8..000000000000 --- a/.github/actions/project-management-action/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Sergio Pintaldi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.github/actions/project-management-action/README.md b/.github/actions/project-management-action/README.md deleted file mode 100644 index 1b2fa18c17e2..000000000000 --- a/.github/actions/project-management-action/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# GitHub Action for Assign to One Project - -[![Docker Cloud Automated build](https://img.shields.io/docker/cloud/automated/srggrs/assign-one-project-github-action)][docker] -[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/srggrs/assign-one-project-github-action)][docker] -[![Docker Pulls](https://img.shields.io/docker/pulls/srggrs/assign-one-project-github-action)][docker] -[![GitHub license](https://img.shields.io/github/license/srggrs/assign-one-project-github-action.svg)][license] -![Latest Version](https://img.shields.io/github/v/release/srggrs/assign-one-project-github-action?color=orange&label=latest%20release) - -[docker]: https://hub.docker.com/r/srggrs/assign-one-project-github-action -[license]: https://github.com/srggrs/assign-one-project-github-action/blob/master/LICENSE - -Automatically add an issue or pull request to specific [GitHub Project](https://help.github.com/articles/about-project-boards/) when you __create__ and/or __label__ them. By default, the issues are assigned to the __`To do`__ column and the pull requests to the __`In progress`__ one, so make sure you have those columns in your project dashboard. But the workflow __allowed you to specify the column name as input__, so you can assign the issues/PRs based on a set of conditions to a specific column of a specific project. - -## Latest features: - -* included `issue_comment` as trigger for this action. -* added project pagination for searching 100+ GitHub projects. - -## Acknowledgment & Motivations - -This action has been modified from the original action from [masutaka](https://github.com/masutaka/github-actions-all-in-one-project). I needed to fix it as the original docker container would not build. Also I think the GitHub Action syntax changed a bit. - -I would like to thank @SunRunAway for adding the labelling functionality and custom column input. - -## Inputs - -### `project` - -**Required** The url of the project to be assigned to. - -### `column_name` - -The column name of the project, defaults to `'To do'` for issues and `'In progress'` for pull requests. - -## Example usage - -Examples of action: - -### Repository project - -```yaml -name: Auto Assign to Project(s) - -on: - issues: - types: [opened, labeled] - pull_request: - types: [opened, labeled] - issue_comment: - types: [created] -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -jobs: - assign_one_project: - runs-on: ubuntu-latest - name: Assign to One Project - steps: - - name: Assign NEW issues and NEW pull requests to project 2 - uses: srggrs/assign-one-project-github-action@1.2.1 - if: github.event.action == 'opened' - with: - project: 'https://github.com/srggrs/assign-one-project-github-action/projects/2' - - - name: Assign issues and pull requests with `bug` label to project 3 - uses: srggrs/assign-one-project-github-action@1.2.1 - if: | - contains(github.event.issue.labels.*.name, 'bug') || - contains(github.event.pull_request.labels.*.name, 'bug') - with: - project: 'https://github.com/srggrs/assign-one-project-github-action/projects/3' - column_name: 'Labeled' -``` - -#### __Notes__ -Be careful of using the conditions above (opened and labeled issues/PRs) because in such workflow, if the issue/PR is opened and labeled at the same time, it will be assigned to __both__ projects! - - -You can use any combination of conditions. For example, to assign new issues or issues labeled with 'mylabel' to a project column, use: -```yaml -... - -if: | - github.event_name == 'issues' && - ( - github.event.action == 'opened' || - contains(github.event.issue.labels.*.name, 'mylabel') - ) -... -``` - -### Organisation or User project - -Generate a token from the Organisation settings or User Settings and add it as a secret in the repository secrets as `MY_GITHUB_TOKEN` - -```yaml -name: Auto Assign to Project(s) - -on: - issues: - types: [opened, labeled] - pull_request_target: - types: [opened, labeled] - issue_comment: - types: [created] -env: - MY_GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} - -jobs: - assign_one_project: - runs-on: ubuntu-latest - name: Assign to One Project - steps: - - name: Assign NEW issues and NEW pull requests to project 2 - uses: srggrs/assign-one-project-github-action@1.2.1 - if: github.event.action == 'opened' - with: - project: 'https://github.com/srggrs/assign-one-project-github-action/projects/2' - - - name: Assign issues and pull requests with `bug` label to project 3 - uses: srggrs/assign-one-project-github-action@1.2.1 - if: | - contains(github.event.issue.labels.*.name, 'bug') || - contains(github.event.pull_request.labels.*.name, 'bug') - with: - project: 'https://github.com/srggrs/assign-one-project-github-action/projects/3' - column_name: 'Labeled' -``` - -## [Change Log](./CHANGELOG.md) - -Please refer to the list of changes [here](./CHANGELOG.md) diff --git a/.github/actions/project-management-action/action.yml b/.github/actions/project-management-action/action.yml deleted file mode 100644 index 40f7a1208834..000000000000 --- a/.github/actions/project-management-action/action.yml +++ /dev/null @@ -1,22 +0,0 @@ -# action.yml -name: 'Assign to One Project' -description: 'Assign new/labeled Issue or Pull Request to a specific project dashboard column' -author: srggrs -inputs: - project: - description: 'The url of the project to be assigned to.' - required: true - column_name: - description: 'The column name of the project, defaults to "To do" for issues and "In progress" for pull requests.' - required: false - -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.project }} - - ${{ inputs.column_name }} - -branding: - icon: 'box' - color: 'red' diff --git a/.github/actions/project-management-action/entrypoint.sh b/.github/actions/project-management-action/entrypoint.sh deleted file mode 100644 index 05b81c7d2d0c..000000000000 --- a/.github/actions/project-management-action/entrypoint.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/bin/sh -l - -PROJECT_URL="$INPUT_PROJECT" -if [ -z "$PROJECT_URL" ]; then - echo "Project input variable is not defined." >&2 - exit 1 -fi - -get_project_type() { - _PROJECT_URL="$1" - - case "$_PROJECT_URL" in - https://github.com/orgs/*) - echo "org" - ;; - https://github.com/users/*) - echo "user" - ;; - https://github.com/*/projects/*) - echo "repo" - ;; - *) - echo "Invalid Project URL: '$_PROJECT_URL' . Please pass a valid Project URL in the project input variable" >&2 - exit 1 - ;; - esac - - unset _PROJECT_URL -} - -get_next_url_from_headers() { - _HEADERS_FILE=$1 - grep -i '^link' "$_HEADERS_FILE" | tr ',' '\n'| grep \"next\" | sed 's/.*<\(.*\)>.*/\1/' -} - -find_project_id() { - _PROJECT_TYPE="$1" - _PROJECT_URL="$2" - - case "$_PROJECT_TYPE" in - org) - _ORG_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/orgs/\([^/]\+\)/projects/[0-9]\+@\1@') - _ENDPOINT="https://api.github.com/orgs/$_ORG_NAME/projects?per_page=100" - ;; - user) - _USER_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/users/\([^/]\+\)/projects/[0-9]\+@\1@') - _ENDPOINT="https://api.github.com/users/$_USER_NAME/projects?per_page=100" - ;; - repo) - _ENDPOINT="https://api.github.com/repos/$GITHUB_REPOSITORY/projects?per_page=100" - ;; - esac - - _NEXT_URL="$_ENDPOINT" - - while : ; do - - _PROJECTS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - -D /tmp/headers \ - "$_NEXT_URL") - - _PROJECTID=$(echo "$_PROJECTS" | jq -r ".[] | select(.html_url == \"$_PROJECT_URL\").id") - _NEXT_URL=$(get_next_url_from_headers '/tmp/headers') - - if [ "$_PROJECTID" != "" ]; then - echo "$_PROJECTID" - elif [ "$_NEXT_URL" == "" ]; then - echo "No project was found." >&2 - exit 1 - fi - done - - unset _PROJECT_TYPE _PROJECT_URL _ORG_NAME _USER_NAME _ENDPOINT _PROJECTS _PROJECTID _NEXT_URL -} - -find_column_id() { - _PROJECT_ID="$1" - _INITIAL_COLUMN_NAME="$2" - - _COLUMNS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - "https://api.github.com/projects/$_PROJECT_ID/columns") - - - echo "$_COLUMNS" | jq -r ".[] | select(.name == \"$_INITIAL_COLUMN_NAME\").id" - unset _PROJECT_ID _INITIAL_COLUMN_NAME _COLUMNS -} - -PROJECT_TYPE=$(get_project_type "${PROJECT_URL:? required this environment variable}") - -if [ "$PROJECT_TYPE" = org ] || [ "$PROJECT_TYPE" = user ]; then - if [ -z "$MY_GITHUB_TOKEN" ]; then - echo "MY_GITHUB_TOKEN not defined" >&2 - exit 1 - fi - - TOKEN="$MY_GITHUB_TOKEN" # It's User's personal access token. It should be secret. -else - if [ -z "$GITHUB_TOKEN" ]; then - echo "GITHUB_TOKEN not defined" >&2 - exit 1 - fi - - TOKEN="$GITHUB_TOKEN" # GitHub sets. The scope in only the repository containing the workflow file. -fi - -INITIAL_COLUMN_NAME="$INPUT_COLUMN_NAME" -if [ -z "$INITIAL_COLUMN_NAME" ]; then - # assing the column name by default - INITIAL_COLUMN_NAME='To do' - if [ "$GITHUB_EVENT_NAME" == "pull_request" ] || [ "$GITHUB_EVENT_NAME" == "pull_request_target" ]; then - echo "changing column name for PR event" - INITIAL_COLUMN_NAME='In progress' - fi -fi - - -PROJECT_ID=$(find_project_id "$PROJECT_TYPE" "$PROJECT_URL") -INITIAL_COLUMN_ID=$(find_column_id "$PROJECT_ID" "${INITIAL_COLUMN_NAME:? required this environment variable}") - -if [ -z "$INITIAL_COLUMN_ID" ]; then - echo "Column name '$INITIAL_COLUMN_ID' is not found." >&2 - exit 1 -fi - -case "$GITHUB_EVENT_NAME" in - issues|issue_comment) - ISSUE_ID=$(jq -r '.issue.id' < "$GITHUB_EVENT_PATH") - - # Add this issue to the project column - curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - -d "{\"content_type\": \"Issue\", \"content_id\": $ISSUE_ID}" \ - "https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards" - ;; - pull_request|pull_request_target) - PULL_REQUEST_ID=$(jq -r '.pull_request.id' < "$GITHUB_EVENT_PATH") - - # Add this pull_request to the project column - curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - -d "{\"content_type\": \"PullRequest\", \"content_id\": $PULL_REQUEST_ID}" \ - "https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards" - ;; - *) - echo "Nothing to be done on this action: '$GITHUB_EVENT_NAME'" >&2 - exit 1 - ;; -esac diff --git a/.github/disabled-workflows/issue_opened.yml b/.github/disabled-workflows/issue_opened.yml deleted file mode 100644 index b4436dca3aad..000000000000 --- a/.github/disabled-workflows/issue_opened.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This workflow runs whenever a new issue is created -name: Issue opened - -on: - issues: - types: [opened] - -permissions: {} -jobs: - automation: - runs-on: ubuntu-latest - steps: - # Add the new issue to a project board, if it needs triage - # See https://github.com/actions/add-to-project - - name: Add issue to triage board - # 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 - # 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 - # This is necessary because the "DSpace Backlog" project is an org level project (i.e. not repo specific) - with: - github-token: ${{ secrets.TRIAGE_PROJECT_TOKEN }} - project-url: https://github.com/orgs/DSpace/projects/24 diff --git a/.github/disabled-workflows/label_merge_conflicts.yml b/.github/disabled-workflows/label_merge_conflicts.yml deleted file mode 100644 index d71d244c2b02..000000000000 --- a/.github/disabled-workflows/label_merge_conflicts.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow checks open PRs for merge conflicts and labels them when conflicts are found -name: Check for merge conflicts - -# Run whenever the "main" branch is updated -# NOTE: This means merge conflicts are only checked for when a PR is merged to main. -on: - push: - branches: [ main ] - # So that the `conflict_label_name` is removed if conflicts are resolved, - # we allow this to run for `pull_request_target` so that github secrets are available. - pull_request_target: - types: [ synchronize ] - -permissions: {} - -jobs: - triage: - # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' - if: github.repository == 'dspace/dspace' - runs-on: ubuntu-latest - permissions: - pull-requests: write - steps: - # See: https://github.com/prince-chrismc/label-merge-conflicts-action - - name: Auto-label PRs with merge conflicts - uses: prince-chrismc/label-merge-conflicts-action@v2 - # Add "merge conflict" label if a merge conflict is detected. Remove it when resolved. - # Note, the authentication token is created automatically - # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token - with: - conflict_label_name: 'merge conflict' - github_token: ${{ secrets.GITHUB_TOKEN }} - conflict_comment: | - Hi @${author}, - Conflicts have been detected against the base branch. - Please [resolve these conflicts](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/about-merge-conflicts) as soon as you can. Thanks! diff --git a/README.md b/README.md index 4bd5d04b704a..9d402806f4ff 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # clarin-dspace This repository contains the sources for the backend part of clarin-dspace. This version is built on top of DSpace v7. To get more details start at [v7 wiki](https://github.com/ufal/clarin-dspace/wiki/NewHome) or jump directly to the [installation instructions](https://github.com/ufal/clarin-dspace/wiki/NewInstallation) + diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/clarin/ClarinMatomoBitstreamTracker.java b/dspace-api/src/main/java/org/dspace/app/statistics/clarin/ClarinMatomoBitstreamTracker.java index 08287caa6ce8..838a2650586a 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/clarin/ClarinMatomoBitstreamTracker.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/clarin/ClarinMatomoBitstreamTracker.java @@ -122,8 +122,6 @@ public void trackBitstreamDownload(Context context, HttpServletRequest request, List items = clarinItemService.findByBitstreamUUID(context, bit.getID()); if (CollectionUtils.isEmpty(items)) { - log.error("Cannot find the Item for the bitstream with ID: " + bit.getID() + - " - the statistics cannot be logged."); return; } diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index 9c7f4396c96c..f9d43a9abc98 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -25,6 +25,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.xml.sax.SAXException; @@ -163,6 +164,11 @@ public class DCInput { */ private ComplexDefinition complexDefinition = null; + /** + * the dropdown input type could have defined a default value + */ + private String defaultValue = ""; + private boolean isRelationshipField = false; private boolean isMetadataField = false; private String relationshipType = null; @@ -244,12 +250,11 @@ public DCInput(Map fieldMap, Map> listMap, // parsing of the element (using the colon as split separator) typeBind = new ArrayList(); String typeBindDef = fieldMap.get("type-bind"); - if (typeBindDef != null && typeBindDef.trim().length() > 0) { - String[] types = typeBindDef.split(","); - for (String type : types) { - typeBind.add(type.trim()); - } - } + this.insertToTypeBind(typeBindDef); + String typeBindField = fieldMap.get(DCInputsReader.TYPE_BIND_FIELD_ATTRIBUTE); + this.insertToTypeBind(typeBindField); + + style = fieldMap.get("style"); isRelationshipField = fieldMap.containsKey("relationship-type"); isMetadataField = fieldMap.containsKey("dc-schema"); @@ -264,9 +269,19 @@ public DCInput(Map fieldMap, Map> listMap, externalSources.add(StringUtils.trim(source)); } } + defaultValue = fieldMap.get("default-value"); } + private void insertToTypeBind(String typeBindDef) { + if (StringUtils.isNotEmpty(typeBindDef)) { + String[] types = typeBindDef.split(","); + for (String type : types) { + typeBind.add(type.trim()); + } + } + } + protected void initRegex(String regex) { this.regex = null; this.pattern = null; @@ -553,6 +568,21 @@ public boolean isAllowedFor(String typeName) { return typeBind.contains(typeName); } + /** + * Decides if this field is valid for the document type + * Check if one of the typeName is in the typeBind list + * + * @param typeNames List of document type names e.g. ["VIDEO"] + * @return true when there is no type restriction or typeName is allowed + */ + public boolean isAllowedFor(List typeNames) { + if (typeBind.isEmpty()) { + return true; + } + + return CollectionUtils.containsAny(typeBind, typeNames); + } + public String getScope() { return visibility; } @@ -607,6 +637,24 @@ public ComplexDefinition getComplexDefinition() { return this.complexDefinition; } + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public boolean hasDefaultValue() { + return StringUtils.isNotEmpty(this.getDefaultValue()); + } + + public boolean isDropdownValue() { + return "dropdown".equals(getInputType()); + } + + + /** * Convert complex definition HashMap to the ordered JSON string * @return complex definition in the JSON string which will be parsed in the FE 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 d013a7d3fe7b..f79de9bd8df7 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 @@ -74,6 +74,7 @@ public class DCInputsReader { * Keyname for storing the name of the complex input type */ static final String COMPLEX_DEFINITION_REF = "complex-definition-ref"; + public static final String TYPE_BIND_FIELD_ATTRIBUTE = "field"; /** diff --git a/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java index 822543d08c80..98c030be80c7 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java @@ -11,6 +11,7 @@ import java.net.URLEncoder; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -19,13 +20,13 @@ import java.util.Objects; import java.util.UUID; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.app.util.Util; import org.dspace.authenticate.AuthenticationMethod; import org.dspace.authenticate.factory.AuthenticateServiceFactory; import org.dspace.authorize.AuthorizeException; @@ -247,15 +248,16 @@ public int authenticate(Context context, String username, String password, // Should we auto register new users. boolean autoRegister = configurationService.getBooleanProperty("authentication-shibboleth.autoregister", true); + String[] netidHeaders = configurationService.getArrayProperty("authentication-shibboleth.netid-header"); // Four steps to authenticate a user try { // Step 1: Identify User - EPerson eperson = findEPerson(context, request); + EPerson eperson = findEPerson(context, request, netidHeaders); // Step 2: Register New User, if necessary if (eperson == null && autoRegister && !isDuplicateUser) { - eperson = registerNewEPerson(context, request); + eperson = registerNewEPerson(context, request, netidHeaders); } if (eperson == null) { @@ -263,7 +265,7 @@ public int authenticate(Context context, String username, String password, } // Step 3: Update User's Metadata - updateEPerson(context, request, eperson); + updateEPerson(context, request, eperson, netidHeaders); // Step 4: Log the user in. context.setCurrentUser(eperson); @@ -540,11 +542,11 @@ public static boolean isEnabled() { * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - protected EPerson findEPerson(Context context, HttpServletRequest request) throws SQLException, AuthorizeException { + protected EPerson findEPerson(Context context, HttpServletRequest request, String[] netidHeaders) + throws SQLException { boolean isUsingTomcatUser = configurationService .getBooleanProperty("authentication-shibboleth.email-use-tomcat-remote-user"); - String netidHeader = configurationService.getProperty("authentication-shibboleth.netid-header"); String emailHeader = configurationService.getProperty("authentication-shibboleth.email-header"); EPerson eperson = null; @@ -554,26 +556,10 @@ protected EPerson findEPerson(Context context, HttpServletRequest request) throw // 1) First, look for a netid header. - if (netidHeader != null) { - String org = shibheaders.get_idp(); - String netid = Util.formatNetId(findSingleAttribute(request, netidHeader), org); - if (StringUtils.isEmpty(netid)) { - netid = shibheaders.get_single(netidHeader); - } - - if (netid != null) { + if (netidHeaders != null) { + eperson = findEpersonByNetId(netidHeaders, shibheaders, eperson, ePersonService, context, true); + if (eperson != null) { foundNetID = true; - eperson = ePersonService.findByNetid(context, netid); - - if (eperson == null) { - log.info( - "Unable to identify EPerson based upon Shibboleth netid header: '" + netidHeader + "'='" + - netid + "'."); - } else { - log.debug( - "Identified EPerson based upon Shibboleth netid header: '" + netidHeader + "'='" + - netid + "'" + "."); - } } } @@ -656,7 +642,6 @@ protected EPerson findEPerson(Context context, HttpServletRequest request) throw return eperson; } - /** * Register a new eperson object. This method is called when no existing user was * found for the NetID or Email and autoregister is enabled. When these conditions @@ -677,11 +662,10 @@ protected EPerson findEPerson(Context context, HttpServletRequest request) throw * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - protected EPerson registerNewEPerson(Context context, HttpServletRequest request) + protected EPerson registerNewEPerson(Context context, HttpServletRequest request, String[] netidHeaders) throws SQLException, AuthorizeException { // Header names - String netidHeader = configurationService.getProperty("authentication-shibboleth.netid-header"); String emailHeader = configurationService.getProperty("authentication-shibboleth.email-header"); String fnameHeader = configurationService.getProperty("authentication-shibboleth.firstname-header"); String lnameHeader = configurationService.getProperty("authentication-shibboleth.lastname-header"); @@ -694,15 +678,12 @@ protected EPerson registerNewEPerson(Context context, HttpServletRequest request // CLARIN // Header values - String netid = Util.formatNetId(findSingleAttribute(request, netidHeader), org); + String netid = getFirstNetId(netidHeaders); String email = getEmailAcceptedOrNull(findSingleAttribute(request, emailHeader)); String fname = Headers.updateValueByCharset(findSingleAttribute(request, fnameHeader)); String lname = Headers.updateValueByCharset(findSingleAttribute(request, lnameHeader)); // If the values are not in the request headers try to retrieve it from `shibheaders`. - if (StringUtils.isEmpty(netid)) { - netid = shibheaders.get_single(netidHeader); - } if (StringUtils.isEmpty(email) && Objects.nonNull(clarinVerificationToken)) { email = clarinVerificationToken.getEmail(); } @@ -718,7 +699,7 @@ protected EPerson registerNewEPerson(Context context, HttpServletRequest request // don't have at least these three pieces of information then we fail. String message = "Unable to register new eperson because we are unable to find an email address along " + "with first and last name for the user.\n"; - message += " NetId Header: '" + netidHeader + "'='" + netid + "' (Optional) \n"; + message += " NetId Header: '" + Arrays.toString(netidHeaders) + "'='" + netid + "' (Optional) \n"; message += " Email Header: '" + emailHeader + "'='" + email + "' \n"; message += " First Name Header: '" + fnameHeader + "'='" + fname + "' \n"; message += " Last Name Header: '" + lnameHeader + "'='" + lname + "'"; @@ -807,24 +788,20 @@ protected EPerson registerNewEPerson(Context context, HttpServletRequest request * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - protected void updateEPerson(Context context, HttpServletRequest request, EPerson eperson) + protected void updateEPerson(Context context, HttpServletRequest request, EPerson eperson, String[] netidHeaders) throws SQLException, AuthorizeException { // Header names & values - String netidHeader = configurationService.getProperty("authentication-shibboleth.netid-header"); String emailHeader = configurationService.getProperty("authentication-shibboleth.email-header"); String fnameHeader = configurationService.getProperty("authentication-shibboleth.firstname-header"); String lnameHeader = configurationService.getProperty("authentication-shibboleth.lastname-header"); - String netid = Util.formatNetId(findSingleAttribute(request, netidHeader), shibheaders.get_idp()); + String netid = getFirstNetId(netidHeaders); String email = getEmailAcceptedOrNull(findSingleAttribute(request, emailHeader)); String fname = Headers.updateValueByCharset(findSingleAttribute(request, fnameHeader)); String lname = Headers.updateValueByCharset(findSingleAttribute(request, lnameHeader)); // If the values are not in the request headers try to retrieve it from `shibheaders`. - if (StringUtils.isEmpty(netid)) { - netid = shibheaders.get_single(netidHeader); - } if (StringUtils.isEmpty(email) && Objects.nonNull(clarinVerificationToken)) { email = clarinVerificationToken.getEmail(); } @@ -858,7 +835,16 @@ protected void updateEPerson(Context context, HttpServletRequest request, EPerso } // The email could have changed if using netid based lookup. if (email != null) { - eperson.setEmail(email.toLowerCase()); + String lowerCaseEmail = email.toLowerCase(); + // Check the email is unique + EPerson epersonByEmail = ePersonService.findByEmail(context, lowerCaseEmail); + if (epersonByEmail != null && !epersonByEmail.getID().equals(eperson.getID())) { + log.error("Unable to update the eperson's email metadata because the email '{}' is already in use.", + lowerCaseEmail); + throw new AuthorizeException("The email address is already in use."); + } else { + eperson.setEmail(email.toLowerCase()); + } } if (fname != null) { eperson.setFirstName(context, fname); @@ -1207,29 +1193,11 @@ public String findSingleAttribute(HttpServletRequest request, String name) { if (name == null) { return null; } - String value = findAttribute(request, name); - if (value != null) { - // If there are multiple values encoded in the shibboleth attribute - // they are separated by a semicolon, and any semicolons in the - // attribute are escaped with a backslash. For this case we are just - // looking for the first attribute so we scan the value until we find - // the first unescaped semicolon and chop off everything else. - int idx = 0; - do { - idx = value.indexOf(';', idx); - if (idx != -1 && value.charAt(idx - 1) != '\\') { - value = value.substring(0, idx); - break; - } - } while (idx >= 0); - - // Unescape the semicolon after splitting - value = value.replaceAll("\\;", ";"); + value = sortEmailsAndGetFirst(value); } - return value; } @@ -1338,5 +1306,70 @@ public String getEmailAcceptedOrNull(String email) { } return email; } + + /** + * Find an EPerson by a NetID header. The method will go through all the netid headers and try to find a user. + */ + public static EPerson findEpersonByNetId(String[] netidHeaders, ShibHeaders shibheaders, EPerson eperson, + EPersonService ePersonService, Context context, boolean logAllowed) + throws SQLException { + // Go through all the netid headers and try to find a user. It could be e.g., `eppn`, `persistent-id`,.. + for (String netidHeader : netidHeaders) { + netidHeader = netidHeader.trim(); + String netid = shibheaders.get_single(netidHeader); + if (netid == null) { + continue; + } + + eperson = ePersonService.findByNetid(context, netid); + + if (eperson == null && logAllowed) { + log.info( + "Unable to identify EPerson based upon Shibboleth netid header: '" + netidHeader + + "'='" + netid + "'."); + } else { + log.debug( + "Identified EPerson based upon Shibboleth netid header: '" + netidHeader + "'='" + + netid + "'" + "."); + } + } + return eperson; + } + + /** + * Sort the email addresses and return the first one. + * @param value The email addresses separated by semicolons. + */ + public static String sortEmailsAndGetFirst(String value) { + // If there are multiple values encoded in the shibboleth attribute + // they are separated by a semicolon, and any semicolons in the + // attribute are escaped with a backslash. + // Step 1: Split the input string into email addresses + List emails = Arrays.stream(value.split("(? email.replaceAll("\\\\;", ";")) // Unescape semicolons + .collect(Collectors.toList()); + + // Step 2: Sort the email list alphabetically + emails.sort(String::compareToIgnoreCase); + + // Step 3: Get the first sorted email + return emails.get(0); + } + + /** + * Get the first netid from the list of netid headers. E.g., eppn, persistent-id,... + * @param netidHeaders list of netid headers loaded from the configuration `authentication-shibboleth.netid-header` + */ + public String getFirstNetId(String[] netidHeaders) { + for (String netidHeader : netidHeaders) { + netidHeader = netidHeader.trim(); + String netid = shibheaders.get_single(netidHeader); + if (netid != null) { + //When creating use first match (eppn before targeted-id) + return netid; + } + } + return null; + } } diff --git a/dspace-api/src/main/java/org/dspace/authenticate/clarin/ShibHeaders.java b/dspace-api/src/main/java/org/dspace/authenticate/clarin/ShibHeaders.java index 65897087302e..a4b85e53be2e 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/clarin/ShibHeaders.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/clarin/ShibHeaders.java @@ -14,6 +14,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -32,7 +33,7 @@ public class ShibHeaders { // constants // private static final String header_separator_ = ";"; - private String netIdHeader = ""; + private String[] netIdHeaders = null; ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); // variables @@ -105,7 +106,7 @@ public String get_single(String name) { List values = get(name); if (values != null && !values.isEmpty()) { // Format netId - if (StringUtils.equals(name, this.netIdHeader)) { + if (ArrayUtils.contains(this.netIdHeaders, name)) { return Util.formatNetId(values.get(0), this.get_idp()); } return values.get(0); @@ -150,6 +151,10 @@ public void log_headers() { } private void initializeNetIdHeader() { - this.netIdHeader = configurationService.getProperty("authentication-shibboleth.netid-header"); + this.netIdHeaders = configurationService.getArrayProperty("authentication-shibboleth.netid-header"); + } + + public String[] getNetIdHeaders() { + return this.netIdHeaders; } } diff --git a/dspace-api/src/main/java/org/dspace/content/PreviewContent.java b/dspace-api/src/main/java/org/dspace/content/PreviewContent.java new file mode 100644 index 000000000000..125f7309bd62 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/PreviewContent.java @@ -0,0 +1,155 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content; + +import java.util.Hashtable; +import java.util.Map; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.dspace.core.Context; +import org.dspace.core.ReloadableEntity; + +/** + * Database entity representation of the previewcontent table. + * It is not possible to create entity from FileInfo class (without modifications) + * so we created PreviewContent (which serves as an entity for FileInfo) + * with corresponding database table previewcontent. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +@Entity +@Table(name = "previewcontent") +public class PreviewContent implements ReloadableEntity { + + @Id + @Column(name = "previewcontent_id") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "previewcontent_previewcontent_id_seq") + @SequenceGenerator(name = "previewcontent_previewcontent_id_seq", + sequenceName = "previewcontent_previewcontent_id_seq", allocationSize = 1) + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST}) + @JoinColumn(name = "bitstream_id") + private Bitstream bitstream; + + @Column(name = "name") + public String name; + + @Column(name = "content") + public String content; + + @Column(name = "isDirectory") + public boolean isDirectory; + + @Column(name = "size") + public String size; + + @OneToMany(cascade = CascadeType.ALL) + @JoinTable( + name = "preview2preview", + joinColumns = @JoinColumn(name = "parent_id"), + inverseJoinColumns = @JoinColumn(name = "child_id") + ) + @MapKeyColumn(name = "name") + public Map sub = new Hashtable<>(); + + /** + * Protected constructor. + */ + protected PreviewContent() {} + + /** + * Protected constructor, create object using: + * {@link org.dspace.content.service.PreviewContentService#create(Context, PreviewContent)} + */ + protected PreviewContent(PreviewContent previewContent) { + this.bitstream = previewContent.getBitstream(); + this.name = previewContent.getName(); + this.content = previewContent.getContent(); + this.isDirectory = previewContent.isDirectory(); + this.size = previewContent.getSize(); + this.sub = previewContent.getSubPreviewContents(); + } + + /** + * Protected constructor, create object using: + * {@link org.dspace.content.service.PreviewContentService#create(Context, Bitstream, String, String, boolean, + * String, Map)} + */ + protected PreviewContent(Bitstream bitstream, String name, String content, boolean isDirectory, String size, + Map subPreviewContents) { + this.bitstream = bitstream; + this.name = name; + this.content = content; + this.isDirectory = isDirectory; + this.size = size; + this.sub = subPreviewContents; + } + + @Override + public Integer getID() { + return id; + } + + public Bitstream getBitstream() { + return bitstream; + } + + public void setBitstream(Bitstream bitstream) { + this.bitstream = bitstream; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isDirectory() { + return isDirectory; + } + + public void setDirectory(boolean directory) { + isDirectory = directory; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + public Map getSubPreviewContents() { + return sub; + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java new file mode 100644 index 000000000000..2d528ca0a884 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java @@ -0,0 +1,90 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.dao.PreviewContentDAO; +import org.dspace.content.service.PreviewContentService; +import org.dspace.core.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Service implementation for the PreviewContent object. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public class PreviewContentServiceImpl implements PreviewContentService { + + /** + * logger + */ + private static final Logger log = LoggerFactory.getLogger(PreviewContentServiceImpl.class); + + + @Autowired + PreviewContentDAO previewContentDAO; + @Autowired(required = true) + AuthorizeService authorizeService; + + @Override + public PreviewContent create(Context context, Bitstream bitstream, String name, String content, + boolean isDirectory, String size, Map subPreviewContents) + throws SQLException { + //no authorization required! + // Create a table row + PreviewContent previewContent = previewContentDAO.create(context, new PreviewContent(bitstream, name, content, + isDirectory, size, subPreviewContents)); + log.info("Created new preview content of ID = {}", previewContent.getID()); + return previewContent; + } + + @Override + public PreviewContent create(Context context, PreviewContent previewContent) throws SQLException { + //no authorization required! + PreviewContent newPreviewContent = previewContentDAO.create(context, new PreviewContent(previewContent)); + log.info("Created new preview content of ID = {}", newPreviewContent.getID()); + return newPreviewContent; + } + + @Override + public void delete(Context context, PreviewContent previewContent) throws SQLException, AuthorizeException { + if (!authorizeService.isAdmin(context)) { + throw new AuthorizeException( + "You must be an admin to delete an CLARIN Content Preview"); + } + previewContentDAO.delete(context, previewContent); + } + + @Override + public PreviewContent find(Context context, int valueId) throws SQLException { + return previewContentDAO.findByID(context, PreviewContent.class, valueId); + } + + @Override + public List findByBitstream(Context context, UUID bitstreamId) throws SQLException { + return previewContentDAO.findByBitstream(context, bitstreamId); + } + + @Override + public List findRootByBitstream(Context context, UUID bitstreamId) throws SQLException { + return previewContentDAO.findRootByBitstream(context, bitstreamId); + } + + @Override + public List findAll(Context context) throws SQLException { + return previewContentDAO.findAll(context, PreviewContent.class); + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java index 2c23eba24d04..71500d00ecad 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java @@ -53,6 +53,7 @@ public class ClarinLicense implements ReloadableEntity { * Required info key word. */ public static final String SEND_TOKEN = "SEND_TOKEN"; + public static final String EXTRA_EMAIL = "EXTRA_EMAIL"; @Id @Column(name = "license_id") diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseServiceImpl.java index 64aa376b744a..10f209df7d04 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseServiceImpl.java @@ -193,7 +193,7 @@ public List findAll(Context context) throws SQLException, Authori public void delete(Context context, ClarinLicense clarinLicense) throws SQLException, AuthorizeException { if (!authorizeService.isAdmin(context)) { throw new AuthorizeException( - "You must be an admin to create an CLARIN License"); + "You must be an admin to delete an CLARIN License"); } clarinLicenseDAO.delete(context, clarinLicense); @@ -203,7 +203,7 @@ public void delete(Context context, ClarinLicense clarinLicense) throws SQLExcep public void update(Context context, ClarinLicense newClarinLicense) throws SQLException, AuthorizeException { if (!authorizeService.isAdmin(context)) { throw new AuthorizeException( - "You must be an admin to create an CLARIN License"); + "You must be an admin to update an CLARIN License"); } if (Objects.isNull(newClarinLicense)) { diff --git a/dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java new file mode 100644 index 000000000000..9abc1e732b74 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java @@ -0,0 +1,44 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.dao; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; + +import org.dspace.content.PreviewContent; +import org.dspace.core.Context; +import org.dspace.core.GenericDAO; + +/** + * Database Access Object interface class for the PreviewContent object. + * This class should only be accessed from a single service and should never be exposed outside of the API + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public interface PreviewContentDAO extends GenericDAO { + /** + * Find all preview content based on ID of bitstream the preview content is added to. + * + * @param context DSpace context + * @param bitstreamId The bitstream ID + * @return List of found preview content + * @throws SQLException If a database error occurs + */ + List findByBitstream(Context context, UUID bitstreamId) throws SQLException; + + /** + * Find all preview content based on bitstream that are the root directory. + * + * @param context DSpace context + * @param bitstreamId The bitstream ID + * @return List of found preview content + * @throws SQLException If a database error occurs + */ + List findRootByBitstream(Context context, UUID bitstreamId) throws SQLException; +} diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java new file mode 100644 index 000000000000..9e1cc3dc0593 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java @@ -0,0 +1,55 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.dao.impl; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; +import javax.persistence.Query; + +import org.dspace.content.PreviewContent; +import org.dspace.content.dao.PreviewContentDAO; +import org.dspace.core.AbstractHibernateDAO; +import org.dspace.core.Context; + +/** + * Hibernate implementation of the Database Access Object interface class for the PreviewContent object. + * This class should never be accessed directly. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public class PreviewContentDAOImpl extends AbstractHibernateDAO implements PreviewContentDAO { + + protected PreviewContentDAOImpl() { + super(); + } + + @Override + public List findByBitstream(Context context, UUID bitstreamId) throws SQLException { + Query query = createQuery(context, "SELECT pc FROM " + PreviewContent.class.getSimpleName() + + " as pc join pc.bitstream as b WHERE b.id = :bitstream_id"); + query.setParameter("bitstream_id", bitstreamId); + query.setHint("org.hibernate.cacheable", Boolean.TRUE); + return findMany(context, query); + } + + @Override + public List findRootByBitstream(Context context, UUID bitstreamId) throws SQLException { + // select only data from the previewcontent table whose ID is not a child in the preview2preview table + Query query = createQuery(context, + "SELECT pc FROM " + PreviewContent.class.getSimpleName() + " pc " + + "JOIN pc.bitstream b " + + "WHERE b.id = :bitstream_id " + + "AND pc.id NOT IN (SELECT child.id FROM " + PreviewContent.class.getSimpleName() + " parent " + + "JOIN parent.sub child)" + ); + query.setParameter("bitstream_id", bitstreamId); + query.setHint("org.hibernate.cacheable", Boolean.TRUE); + return findMany(context, query); + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java index 53749de86352..dbe842a4194f 100644 --- a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java +++ b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java @@ -29,6 +29,7 @@ import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataValueService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.SiteService; @@ -76,6 +77,7 @@ public abstract class ContentServiceFactory { public abstract SiteService getSiteService(); public abstract SubscribeService getSubscribeService(); + public abstract PreviewContentService getPreviewContentService(); /** * Return the implementation of the RelationshipTypeService interface diff --git a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java index e4cd23988954..a38dec0c0a9d 100644 --- a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java @@ -26,6 +26,7 @@ import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataValueService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.SiteService; @@ -83,6 +84,8 @@ public class ContentServiceFactoryImpl extends ContentServiceFactory { private EntityTypeService entityTypeService; @Autowired(required = true) private EntityService entityService; + @Autowired(required = true) + private PreviewContentService previewContentService; @Autowired(required = true) private DspaceObjectClarinService dspaceObjectClarinService; @@ -165,6 +168,11 @@ public SubscribeService getSubscribeService() { return subscribeService ; } + @Override + public PreviewContentService getPreviewContentService() { + return previewContentService; + } + @Override public RelationshipTypeService getRelationshipTypeService() { return relationshipTypeService; diff --git a/dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java b/dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java new file mode 100644 index 000000000000..8ecf7066e2b4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java @@ -0,0 +1,98 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.service; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Bitstream; +import org.dspace.content.PreviewContent; +import org.dspace.core.Context; + +/** + * Service interface class for the PreviewContent object. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public interface PreviewContentService { + + /** + * Create a new preview content in the database. + * + * @param context DSpace context + * @param bitstream The bitstream to create a preview content for + * @param name The name of preview content + * @param content The content of preview content + * @param isDirectory True if preview content is directory else false + * @param size The size of preview content + * @param subPreviewContents The sub preview contents of preview content + * @return The newly created preview content + * @throws SQLException If a database error occurs + */ + PreviewContent create(Context context, Bitstream bitstream, String name, String content, + boolean isDirectory, String size, Map subPreviewContents) + throws SQLException; + + /** + * Create a new preview content in the database. + * + * @param context DSpace context + * @param previewContent The preview content + * @return The newly created preview content + * @throws SQLException If a database error occurs + */ + PreviewContent create(Context context, PreviewContent previewContent) throws SQLException; + + /** + * Delete a preview content from the database. + * + * @param context DSpace context + * @param previewContent Deleted preview content + * @throws SQLException If a database error occurs + * @throws AuthorizeException If a user is not authorized + */ + void delete(Context context, PreviewContent previewContent) throws SQLException, AuthorizeException; + + /** + * Find preview content based on ID. + * + * @param context DSpace context + * @param valueId The ID of the preview content to search for + * @throws SQLException If a database error occurs + */ + PreviewContent find(Context context, int valueId) throws SQLException; + + /** + * Find all preview content based on bitstream. + * + * @param context DSpace context + * @param bitstream_id The ID of the bitstream + * @throws SQLException If a database error occurs + */ + List findByBitstream(Context context, UUID bitstream_id) throws SQLException; + + /** + * Find all preview content based on bitstream that are the root directory. + * + * @param context DSpace context + * @param bitstream_id The ID of the bitstream + * @throws SQLException If a database error occurs + */ + List findRootByBitstream(Context context, UUID bitstream_id) throws SQLException; + + /** + * Find all preview contents from database. + * + * @param context DSpace context + * @throws SQLException If a database error occurs + */ + List findAll(Context context) throws SQLException; +} diff --git a/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java b/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java index ebec91f7fd2f..d4e33939928a 100644 --- a/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java @@ -268,8 +268,9 @@ public String resolveToURL(Context context, String handleStr) throws SQLExceptio if (isInternalResource(handle)) { // Internal handle // Create url for internal handle - url = configurationService.getProperty("dspace.ui.url") - + "/handle/" + handleStr; + String currentUiUrl = configurationService.getProperty("dspace.ui.url"); + url = currentUiUrl.endsWith("/") ? currentUiUrl : currentUiUrl + "/"; + url += "handle/" + handleStr; } else { // External handle url = handle.getUrl(); diff --git a/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java b/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java index 1275ef81d695..a22736c179bb 100644 --- a/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java +++ b/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java @@ -254,6 +254,54 @@ public void scanNAs(ScanCallback callback) throws HandleException { // Resolving methods //////////////////////////////////////// + /** + * Resolve the given handle to DSpace object. + * + * @param context the context + * @param handle the handle to resolve + * @return the resolved DSpaceObject + * @throws HandleException if an error occurs during resolution + */ + private static DSpaceObject resolveHandleToObject(Context context, String handle) throws HandleException { + try { + return handleClarinService.resolveToObject(context, handle); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Exception in resolveHandleToObject", e); + } + throw new HandleException(HandleException.INTERNAL_ERROR); + } + } + + /** + * Retrieves handle values as a map. + * + * @param handle the handle to resolve + * @return a map containing the handle values + * @throws HandleException if an error occurs during handle resolution + */ + public static Map getMapHandleValues(String handle) throws HandleException { + if (log.isInfoEnabled()) { + log.info("Called getMapHandleValues"); + } + loadServices(); + Context context = new Context(); + try { + DSpaceObject dso = null; + boolean resolveMetadata = configurationService.getBooleanProperty("lr.pid.resolvemetadata", true); + if (resolveMetadata) { + dso = resolveHandleToObject(context, handle); + } + return extractMetadata(dso); + } finally { + try { + context.complete(); + } catch (SQLException sqle) { + // ignore + } + } + } + /** * Return the raw values for this handle. This implementation returns a * single URL value. @@ -285,15 +333,7 @@ public byte[][] getRawHandleValues(byte[] theHandle, int[] indexList, String handle = Util.decodeString(theHandle); context = new Context(); - - DSpaceObject dso = null; String url = handleClarinService.resolveToURL(context, handle); - - boolean resolveMetadata = configurationService.getBooleanProperty("lr.pid.resolvemetadata", true); - if (resolveMetadata) { - dso = handleClarinService.resolveToObject(context, handle); - } - if (Objects.isNull(url)) { // try with old prefix @@ -332,6 +372,11 @@ public byte[][] getRawHandleValues(byte[] theHandle, int[] indexList, rh = new ResolvedHandle(url, splits[1], splits[2], splits[3], splits[4], splits[5], splits[6], splits[7]); } else { + DSpaceObject dso = null; + boolean resolveMetadata = configurationService.getBooleanProperty("lr.pid.resolvemetadata", true); + if (resolveMetadata) { + dso = resolveHandleToObject(context, handle); + } rh = new ResolvedHandle(url, dso); } log.info(String.format("Handle [%s] resolved to [%s]", handle, url)); diff --git a/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java b/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java index 62c49fb86f69..373b31d89750 100644 --- a/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java @@ -402,6 +402,11 @@ protected String createId(Context context, DSpaceObject dso) throws SQLException // add subprefix for item handle PIDCommunityConfiguration pidCommunityConfiguration = PIDConfiguration .getPIDCommunityConfiguration(owningCommunityId); + + if (Objects.isNull(pidCommunityConfiguration)) { + return createdId; + } + //Which type is pis community configuration? if (pidCommunityConfiguration.isEpic()) { String handleId; @@ -426,18 +431,17 @@ protected String createId(Context context, DSpaceObject dso) throws SQLException } else if (pidCommunityConfiguration.isLocal()) { String prefix = pidCommunityConfiguration.getPrefix(); String handleSubprefix = pidCommunityConfiguration.getSubprefix(); - if (Objects.nonNull(handleSubprefix) && !handleSubprefix.isEmpty()) { - // create local handle - return prefix + (handlePrefix.endsWith("/") ? "" : "/") - + handleSubprefix + "-" + handleSuffix.toString(); + String validatedPrefix = prefix + (handlePrefix.endsWith("/") ? "" : "/"); + if (StringUtils.isEmpty(handleSubprefix)) { + // E.g., 13654/5553 + return validatedPrefix + handleSuffix.toString(); } + // E.g., 13645/1-5553 + return validatedPrefix + handleSubprefix + "-" + handleSuffix.toString(); } else { throw new IllegalStateException("Unsupported PID type: " + pidCommunityConfiguration.getType()); } - - //create handle for another type of dspace objects - return createdId; } @Override diff --git a/dspace-api/src/main/java/org/dspace/handle/PIDConfiguration.java b/dspace-api/src/main/java/org/dspace/handle/PIDConfiguration.java index 8bb8e9a178f9..20d2ee4c299c 100644 --- a/dspace-api/src/main/java/org/dspace/handle/PIDConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/handle/PIDConfiguration.java @@ -18,6 +18,7 @@ import java.util.Set; import java.util.UUID; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; @@ -110,16 +111,25 @@ public static PIDCommunityConfiguration getPIDCommunityConfiguration( UUID communityID) { instance = getInstance(); + if (MapUtils.isEmpty(pidCommunityConfigurations)) { + log.info("The configuration property " + CLARIN_PID_COMMUNITY_CONFIGURATIONS_KEYWORD + " is not defined." + + " Using default configuration of the `handle.prefix`."); + return null; + } + PIDCommunityConfiguration pidCommunityConfiguration = pidCommunityConfigurations .get(communityID); + if (Objects.isNull(pidCommunityConfiguration)) { + // Yes, there is a configuration for the community with ID `null`. pidCommunityConfiguration = pidCommunityConfigurations.get(null); } if (Objects.isNull(pidCommunityConfiguration)) { - throw new IllegalStateException("Missing configuration entry in " - + CLARIN_PID_COMMUNITY_CONFIGURATIONS_KEYWORD - + " for community with ID " + communityID); + log.info("Missing configuration entry in " + CLARIN_PID_COMMUNITY_CONFIGURATIONS_KEYWORD + + " for community with ID {}. Using default configuration of the `handle.prefix`.", communityID); + return null; } + return pidCommunityConfiguration; } @@ -247,8 +257,10 @@ public String convertPropertyToValidString(String[] pidCommunityConfigurationsAr * Reload community configuration. It is for testing purposes. */ public void reloadPidCommunityConfigurations() { - pidCommunityConfigurations.clear(); - pidCommunityConfigurations = null; + if (Objects.nonNull(pidCommunityConfigurations)) { + pidCommunityConfigurations.clear(); + pidCommunityConfigurations = null; + } initialize(); } } diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java index 8fb01cd36e92..c803f1407e05 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java @@ -67,6 +67,7 @@ public SubscriptionEmailNotificationServiceImpl(Map public void perform(Context context, DSpaceRunnableHandler handler, String subscriptionType, String frequency) { List communityItems = new ArrayList<>(); List collectionsItems = new ArrayList<>(); + EPerson currentEperson = context.getCurrentUser(); try { List subscriptions = findAllSubscriptionsBySubscriptionTypeAndFrequency(context, subscriptionType, frequency); @@ -77,7 +78,10 @@ public void perform(Context context, DSpaceRunnableHandler handler, String subsc for (Subscription subscription : subscriptions) { DSpaceObject dSpaceObject = subscription.getDSpaceObject(); EPerson ePerson = subscription.getEPerson(); - + // Set the current user to the subscribed eperson because the Solr query checks + // the permissions of the current user in the ANONYMOUS group. + // If there is no user (i.e., `current user = null`), it will send an email with no new items. + context.setCurrentUser(ePerson); if (!authorizeService.authorizeActionBoolean(context, ePerson, dSpaceObject, READ, true)) { iterator++; continue; @@ -126,6 +130,8 @@ public void perform(Context context, DSpaceRunnableHandler handler, String subsc handler.handleException(e); context.abort(); } + // Reset the current user because it was changed to subscriber eperson + context.setCurrentUser(currentEperson); } @SuppressWarnings("rawtypes") diff --git a/dspace-api/src/main/java/org/dspace/util/FileInfo.java b/dspace-api/src/main/java/org/dspace/util/FileInfo.java index 3a637e2255e9..fa9e75a06f6e 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileInfo.java +++ b/dspace-api/src/main/java/org/dspace/util/FileInfo.java @@ -22,6 +22,14 @@ public class FileInfo { public Hashtable sub = null; + public FileInfo(String name, String content, String size, boolean isDirectory, Hashtable sub) { + this.name = name; + this.content = content; + this.size = size; + this.isDirectory = isDirectory; + this.sub = sub; + } + public FileInfo(String name) { this.name = name; sub = new Hashtable(); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql index 6c433443a8ba..529577b1b800 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql @@ -410,4 +410,4 @@ ALTER TABLE handle ALTER TABLE eperson ADD welcome_info varchar(30); -ALTER TABLE eperson ADD can_edit_submission_metadata BOOL; \ No newline at end of file +ALTER TABLE eperson ADD can_edit_submission_metadata BOOL; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql new file mode 100644 index 000000000000..068f80f9430a --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql @@ -0,0 +1,78 @@ +-- +-- 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/ +-- + +-- +-- Name: previewcontent; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE previewcontent ( + previewcontent_id integer NOT NULL, + bitstream_id uuid NOT NULL, + name varchar(2000), + content varchar(2000), + isDirectory boolean DEFAULT false, + size varchar(256) +); + +-- +-- Name: previewcontent_previewcontent_id_seq; Type: SEQUENCE; Schema: public; Owner: dspace +-- + +CREATE SEQUENCE previewcontent_previewcontent_id_seq + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + +ALTER TABLE previewcontent ALTER COLUMN previewcontent_id SET DEFAULT nextval('previewcontent_previewcontent_id_seq'); + +-- +-- Name: previewcontent_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE previewcontent + ADD CONSTRAINT previewcontent_pkey PRIMARY KEY (previewcontent_id); + +-- +-- Name: previewcontent_bitstream_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE previewcontent + ADD CONSTRAINT previewcontent_bitstream_fk FOREIGN KEY (bitstream_id) REFERENCES bitstream(uuid) ON DELETE CASCADE; + +-- +-- Name: preview2preview; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE preview2preview ( + parent_id integer NOT NULL, + child_id integer NOT NULL, + name varchar(2000) +); + +-- +-- Name: preview2preview_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_pkey PRIMARY KEY (parent_id, child_id); + +-- +-- Name: preview2preview_parent_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_parent_fk FOREIGN KEY (parent_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; + +-- +-- Name: preview2preview_child_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_child_fk FOREIGN KEY (child_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql new file mode 100644 index 000000000000..57919fbfa8e6 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql @@ -0,0 +1,88 @@ +-- +-- 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/ +-- + +-- +-- Name: previewcontent; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE previewcontent ( + previewcontent_id integer NOT NULL, + bitstream_id uuid NOT NULL, + name varchar(2000), + content varchar(2000), + isDirectory boolean DEFAULT false, + size varchar(256) +); + +ALTER TABLE public.previewcontent OWNER TO dspace; + +-- +-- Name: previewcontent_previewcontent_id_seq; Type: SEQUENCE; Schema: public; Owner: dspace +-- + +CREATE SEQUENCE previewcontent_previewcontent_id_seq + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + +ALTER TABLE public.previewcontent_previewcontent_id_seq OWNER TO dspace; + +-- +-- Name: previewcontent_previewcontent_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: dspace +-- + +ALTER SEQUENCE previewcontent_previewcontent_id_seq OWNED BY previewcontent.previewcontent_id; + +-- +-- Name: previewcontent_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE ONLY previewcontent + ADD CONSTRAINT previewcontent_pkey PRIMARY KEY (previewcontent_id); + +-- +-- Name: previewcontent_bitstream_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE ONLY previewcontent + ADD CONSTRAINT previewcontent_bitstream_fk FOREIGN KEY (bitstream_id) REFERENCES bitstream(uuid) ON DELETE CASCADE; + +-- +-- Name: preview2preview; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE preview2preview ( + parent_id integer NOT NULL, + child_id integer NOT NULL, + name varchar(2000) +); + +ALTER TABLE public.preview2preview OWNER TO dspace; + +-- +-- Name: preview2preview_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_pkey PRIMARY KEY (parent_id, child_id); + +-- +-- Name: preview2preview_parent_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_parent_fk FOREIGN KEY (parent_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; + +-- +-- Name: preview2preview_child_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_child_fk FOREIGN KEY (child_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index cc9ccb26bbb2..51d050c296a2 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -219,7 +219,7 @@ featured.service.teitok.description = A web-based platform for viewing, creating ##### Shibboleth ##### -authentication-shibboleth.netid-header = SHIB-NETID +authentication-shibboleth.netid-header = SHIB-NETID,eppn,persistent-id authentication-shibboleth.email-header = SHIB-MAIL authentication-shibboleth.firstname-header = SHIB-GIVENNAME authentication-shibboleth.lastname-header = SHIB-SURNAME @@ -306,4 +306,8 @@ sync.storage.service.enabled = false signposting.enabled = true # Test configuration has only EN locale (submission-forms.xml) -webui.supported.locales = en \ No newline at end of file +webui.supported.locales = en + +# Type bind configuration for the submission form with special type-bind field +# When title is something like "Type-bind test" the type-bind field will popped up +submit.type-bind.field = dc.type,dc.identifier.citation=>dc.title \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java index 63340698ac00..ead338bc8e70 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -23,11 +23,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.AbstractIntegrationTest; +import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; @@ -38,7 +39,6 @@ import org.junit.Test; import org.w3c.dom.Attr; import org.w3c.dom.Node; -import org.xml.sax.SAXException; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.diff.Comparison; import org.xmlunit.diff.ComparisonFormatter; @@ -52,7 +52,7 @@ * @author Mark H. Wood */ public class StructBuilderIT - extends AbstractIntegrationTest { + extends AbstractIntegrationTestWithDatabase { private static final Logger log = LogManager.getLogger(); private static final CommunityService communityService @@ -79,7 +79,8 @@ public static void tearDownClass() { * @throws IOException passed through. */ @Before - public void setUp() throws SQLException, AuthorizeException, IOException { + public void setUp() throws Exception { + super.setUp(); // Clear out all communities and collections. context.turnOffAuthorisationSystem(); for (Community community : communityService.findAllTop(context)) { @@ -285,19 +286,15 @@ public void testImportStructureWithHandles() * @throws org.dspace.authorize.AuthorizeException passed through. */ @Test - public void testExportStructure() - throws ParserConfigurationException, SAXException, IOException, - SQLException, AuthorizeException { + public void testExportStructure() { // Create some structure to test. context.turnOffAuthorisationSystem(); - Community community0 = communityService.create(null, context); - communityService.setMetadataSingleValue(context, community0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Top Community 0"); - Collection collection0_0 = collectionService.create(context, community0); - collectionService.setMetadataSingleValue(context, collection0_0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Collection 0.0"); + // Top level community + Community community0 = CommunityBuilder.createCommunity(context) + .withName("Top Community 0").build(); + // Collection below top level community + Collection collection0_0 = CollectionBuilder.createCollection(context, community0) + .withName("Collection 0.0").build(); // Export the current structure. System.out.println("exportStructure"); 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..aeda97f818c2 100644 --- a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java @@ -24,6 +24,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; @@ -159,7 +160,7 @@ public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception { performExportScript(article.getHandle(), tempFile); UUID id = article.getID(); itemService.delete(context, article); - WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false); + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, col1, id).build(); installItemService.installItem(context, workspaceItem, "123456789/0100"); performImportNoForceScript(tempFile); Iterator items = itemService.findByCollection(context, col1); diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java index ee94ace18218..1eee780fbbbd 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java @@ -35,6 +35,7 @@ import org.dspace.content.service.ItemService; import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.SiteService; @@ -132,6 +133,7 @@ public abstract class AbstractBuilder { static ClarinUserMetadataService clarinUserMetadataService; static ClarinLicenseResourceUserAllowanceService clarinLicenseResourceUserAllowanceService; static CreativeCommonsService creativeCommonsService; + static PreviewContentService previewContentService; @@ -184,6 +186,7 @@ public static void init() { requestItemService = RequestItemServiceFactory.getInstance().getRequestItemService(); versioningService = DSpaceServicesFactory.getInstance().getServiceManager() .getServiceByName(VersioningService.class.getName(), VersioningService.class); + previewContentService = ContentServiceFactory.getInstance().getPreviewContentService(); // Temporarily disabled claimedTaskService = XmlWorkflowServiceFactory.getInstance().getClaimedTaskService(); @@ -259,6 +262,7 @@ public static void destroy() { subscribeService = null; supervisionOrderService = null; submissionConfigService = null; + previewContentService = null; } diff --git a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java index b3447dd8bd9a..c16fb696b0c3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java @@ -12,6 +12,9 @@ import java.util.UUID; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -51,6 +54,33 @@ public static GroupBuilder createGroup(final Context context) { return builder.create(context); } + public static GroupBuilder createCollectionAdminGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, collection); + } + + public static GroupBuilder createCollectionSubmitterGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createSubmitterGroup(context, collection); + } + + public static GroupBuilder createCollectionDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } + + public static GroupBuilder createCollectionWorkflowRoleGroup(final Context context, Collection collection, + String roleName) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createWorkflowRoleGroup(context, collection, roleName); + } + + public static GroupBuilder createCommunityAdminGroup(final Context context, Community community) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, community); + } + private GroupBuilder create(final Context context) { this.context = context; try { @@ -61,6 +91,54 @@ private GroupBuilder create(final Context context) { return this; } + private GroupBuilder createAdminGroup(final Context context, DSpaceObject container) { + this.context = context; + try { + if (container instanceof Collection) { + group = collectionService.createAdministrators(context, (Collection) container); + } else if (container instanceof Community) { + group = communityService.createAdministrators(context, (Community) container); + } else { + handleException(new IllegalArgumentException("DSpaceObject must be collection or community. " + + "Type: " + container.getType())); + } + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createSubmitterGroup(final Context context, Collection collection) { + this.context = context; + try { + group = collectionService.createSubmitters(context, collection); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + this.context = context; + try { + group = collectionService.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createWorkflowRoleGroup(final Context context, Collection collection, String roleName) { + this.context = context; + try { + group = workflowService.createWorkflowRoleGroup(context, collection, roleName); + } catch (Exception e) { + return handleException(e); + } + return this; + } + @Override protected DSpaceObjectService getService() { return groupService; diff --git a/dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java b/dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java new file mode 100644 index 000000000000..87b09d4f4bab --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java @@ -0,0 +1,97 @@ +/** + * 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.builder; + +import java.sql.SQLException; +import java.util.Map; +import java.util.Objects; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Bitstream; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.dspace.core.Context; + +public class PreviewContentBuilder extends AbstractBuilder { + + private PreviewContent previewContent; + + protected PreviewContentBuilder(Context context) { + super(context); + } + + public static PreviewContentBuilder createPreviewContent(final Context context, Bitstream bitstream, String name, + String content, boolean isDirectory, String size, + Map subPreviewContents) { + PreviewContentBuilder builder = new PreviewContentBuilder(context); + return builder.create(context, bitstream, name, content, isDirectory, size, subPreviewContents); + } + + private PreviewContentBuilder create(final Context context, Bitstream bitstream, String name, String content, + boolean isDirectory, String size, + Map subPreviewContents) { + this.context = context; + try { + previewContent = previewContentService.create(context, bitstream, name, content, + isDirectory, size, subPreviewContents); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + public static void deletePreviewContent(Integer id) throws Exception { + if (Objects.isNull(id)) { + return; + } + try (Context c = new Context()) { + c.turnOffAuthorisationSystem(); + PreviewContent previewContent = previewContentService.find(c, id); + + if (previewContent != null) { + previewContentService.delete(c, previewContent); + } + c.complete(); + } + } + + @Override + public void cleanup() throws Exception { + try (Context c = new Context()) { + c.turnOffAuthorisationSystem(); + // Ensure object and any related objects are reloaded before checking to see what needs cleanup + previewContent = c.reloadEntity(previewContent); + delete(c, previewContent); + c.complete(); + indexingService.commit(); + } + } + + @Override + public PreviewContent build() throws SQLException, AuthorizeException { + try { + context.dispatchEvents(); + indexingService.commit(); + return previewContent; + } catch (Exception e) { + return handleException(e); + } + } + + @Override + public void delete(Context c, PreviewContent dso) throws Exception { + if (dso != null) { + getService().delete(c, dso); + } + } + + @Override + protected PreviewContentService getService() { + return previewContentService; + } +} diff --git a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java index a63c24ce850e..3f2f46836819 100644 --- a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.util.UUID; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; @@ -41,14 +42,31 @@ protected WorkspaceItemBuilder(Context context) { public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col) { WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); - return builder.create(context, col); + return builder.create(context, col, null); } - private WorkspaceItemBuilder create(final Context context, final Collection col) { + public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col, UUID uuid) { + WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); + return builder.create(context, col, uuid); + } + + /** + * Create with a specific UUID (e.g. restoring items with Packager import) + * + * @param context DSpace context + * @param col Parent collection + * @param uuid Item UUID + * @return WorkspaceItemBuilder + */ + private WorkspaceItemBuilder create(final Context context, final Collection col, UUID uuid) { this.context = context; try { - workspaceItem = workspaceItemService.create(context, col, false); + if (uuid == null) { + workspaceItem = workspaceItemService.create(context, col, false); + } else { + workspaceItem = workspaceItemService.create(context, col, uuid, false); + } item = workspaceItem.getItem(); } catch (Exception e) { return handleException(e); diff --git a/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java b/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java index 6a8daa432eb6..7ff2ff720017 100644 --- a/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java +++ b/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java @@ -29,6 +29,7 @@ import org.dspace.builder.OrcidQueueBuilder; import org.dspace.builder.OrcidTokenBuilder; import org.dspace.builder.PoolTaskBuilder; +import org.dspace.builder.PreviewContentBuilder; import org.dspace.builder.ProcessBuilder; import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipTypeBuilder; @@ -83,6 +84,7 @@ private void initMap() { map.put(MetadataSchemaBuilder.class.getName(), new ArrayList<>()); map.put(SiteBuilder.class.getName(), new ArrayList<>()); map.put(ProcessBuilder.class.getName(), new ArrayList<>()); + map.put(PreviewContentBuilder.class.getName(), new ArrayList<>()); } /** diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 19104edd3f56..2da15e4d26fc 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -49,6 +49,7 @@ import org.dspace.builder.ItemBuilder; import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipTypeBuilder; +import org.dspace.builder.VersionBuilder; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; @@ -62,8 +63,6 @@ import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; -import org.dspace.versioning.factory.VersionServiceFactory; -import org.dspace.versioning.service.VersioningService; import org.hamcrest.Matcher; import org.junit.Assert; import org.junit.Before; @@ -74,8 +73,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa private final RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); - private final VersioningService versioningService = - VersionServiceFactory.getInstance().getVersionService(); private final WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); private final InstallItemService installItemService = @@ -84,7 +81,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -291,7 +287,7 @@ public void test_createNewVersionOfItemOnLeftSideOfRelationships() throws Except // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -567,7 +563,7 @@ public void test_createNewVersionOfItemAndModifyRelationships() throws Exception // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -927,7 +923,7 @@ public void test_createNewVersionOfItemOnRightSideOfRelationships() throws Excep // create a new version of the person // //////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPerson); + Version newVersion = VersionBuilder.createVersion(context, originalPerson, "test").build(); Item newPerson = newVersion.getItem(); assertNotSame(originalPerson, newPerson); @@ -1300,7 +1296,7 @@ public void test_createNewVersionOfItemAndVerifyMetadataOrder() throws Exception // create new version of publication // /////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1463,7 +1459,7 @@ public void test_createNewVersionOfItemWithAddRemoveMove() throws Exception { // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1782,7 +1778,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -1790,7 +1786,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -2316,7 +2312,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - person 3.2 // ///////////////////////////////////// - Item pe3_2 = versioningService.createNewVersion(context, pe3_1).getItem(); + Item pe3_2 = VersionBuilder.createVersion(context, pe3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pe3_2)); context.commit(); @@ -2324,7 +2320,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - project 3.2 // ////////////////////////////////////// - Item pr3_2 = versioningService.createNewVersion(context, pr3_1).getItem(); + Item pr3_2 = VersionBuilder.createVersion(context, pr3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pr3_2)); context.commit(); @@ -3056,7 +3052,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -3064,7 +3060,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -3509,7 +3505,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create a new version of publication 1 and archive // /////////////////////////////////////////////////////// - Item publication1V2 = versioningService.createNewVersion(context, publication1V1).getItem(); + Item publication1V2 = VersionBuilder.createVersion(context, publication1V1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, publication1V2)); context.dispatchEvents(); @@ -3517,7 +3513,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 1 // //////////////////////////////////// - Item person1V2 = versioningService.createNewVersion(context, person1V1).getItem(); + Item person1V2 = VersionBuilder.createVersion(context, person1V1, "test").build().getItem(); // update "Smith, Donald" to "Smith, D." itemService.replaceMetadata( context, person1V2, "person", "givenName", null, null, "D.", @@ -3853,7 +3849,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 2 // //////////////////////////////////// - Item person2V2 = versioningService.createNewVersion(context, person2V1).getItem(); + Item person2V2 = VersionBuilder.createVersion(context, person2V1, "test").build().getItem(); Relationship rel1 = getRelationship(publication1V2, isAuthorOfPublication, person2V2); assertNotNull(rel1); rel1.setRightwardValue("Doe, Jane Jr"); diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 2a07799deee5..8038a7153325 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,6 +10,8 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; @@ -19,7 +21,10 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; +import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.After; @@ -32,10 +37,23 @@ */ public class CreateMissingIdentifiersIT extends AbstractIntegrationTestWithDatabase { + private ServiceManager serviceManager; + private IdentifierServiceImpl identifierService; private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + + serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + // Clean out providers to avoid any being used for creation of community and collection + identifierService.setProviders(new ArrayList<>()); + } + @Test public void testPerform() throws IOException { @@ -67,11 +85,7 @@ public void testPerform() /* * Now install an incompatible provider to make the task fail. */ - DSpaceServicesFactory.getInstance() - .getServiceManager() - .registerServiceClass( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - VersionedHandleIdentifierProviderWithCanonicalHandles.class); + registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); curator.curate(context, item); System.out.format("With incompatible provider, result is '%s'.\n", @@ -86,4 +100,14 @@ public void destroy() throws Exception { super.destroy(); DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); } + + private void registerProvider(Class type) { + // Register our new provider + serviceManager.registerServiceClass(type.getName(), type); + IdentifierProvider identifierProvider = + (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService.setProviders(List.of(identifierProvider)); + } } diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 7e549f6cae33..57acf1f1c453 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -24,6 +24,7 @@ import org.dspace.content.Item; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -57,13 +58,30 @@ public void setUp() throws Exception { .build(); } + @After + @Override + public void destroy() throws Exception { + super.destroy(); + // After this test has finished running, refresh application context and + // set the expected 'default' versioned handle provider back to ensure other tests don't fail + DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); + } + private void registerProvider(Class type) { // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); identifierService.setProviders(List.of(identifierProvider)); } diff --git a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java index 60407823485b..aa4cd8bd4e49 100644 --- a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java @@ -18,6 +18,7 @@ import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; import org.dspace.builder.GroupBuilder; +import org.dspace.builder.SupervisionOrderBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; @@ -85,10 +86,10 @@ public void createSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA).build(); SupervisionOrder supervisionOrderTwo = - supervisionOrderService.create(context, item, groupB); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupB).build(); context.restoreAuthSystemState(); @@ -136,7 +137,8 @@ public void findSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -205,9 +207,12 @@ public void findAllSupervisionOrdersTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -259,9 +264,12 @@ public void findSupervisionOrderByItemTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -310,7 +318,8 @@ public void findSupervisionOrderByItemAndGroupTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA) + .build(); context.restoreAuthSystemState(); @@ -370,7 +379,8 @@ public void isSupervisorTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); diff --git a/dspace-oai/src/main/java/org/dspace/utils/LicenseUtil.java b/dspace-oai/src/main/java/org/dspace/utils/LicenseUtil.java index 975dbf39f1c7..815cc9edcce4 100644 --- a/dspace-oai/src/main/java/org/dspace/utils/LicenseUtil.java +++ b/dspace-oai/src/main/java/org/dspace/utils/LicenseUtil.java @@ -12,15 +12,13 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - /** * Class is copied from the LINDAT/CLARIAH-CZ (This class is taken from UFAL-clarin. * https://github.com/ufal/clarin-dspace/blob @@ -200,28 +198,15 @@ public static String uriToAvailability(String uri) { return "available-restrictedUse"; } - public static org.w3c.dom.NodeList uriToRestrictions(String uri) + public static List uriToRestrictions(String uri) throws ParserConfigurationException { - - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - javax.xml.parsers.DocumentBuilder builder; - builder = factory.newDocumentBuilder(); - - Document doc = builder.newDocument(); - Element root = doc.createElement("restrictions"); - String restrictions = _uri2restrictions.get(uri); if (Objects.isNull(restrictions)) { restrictions = "other"; } - - for (String restriction : restrictions.split(",")) { - Element res = doc.createElement("restriction"); - res.appendChild(doc.createTextNode(restriction)); - root.appendChild(res); - } - - return root.getElementsByTagName("restriction"); + List ret = new LinkedList<>(); + Collections.addAll(ret, restrictions.split(",")); + return ret; } public static void main(String[] args) throws Exception { diff --git a/dspace-oai/src/main/java/org/dspace/utils/SpecialItemService.java b/dspace-oai/src/main/java/org/dspace/utils/SpecialItemService.java index 068c8dc619d0..d62cbc481fcb 100644 --- a/dspace-oai/src/main/java/org/dspace/utils/SpecialItemService.java +++ b/dspace-oai/src/main/java/org/dspace/utils/SpecialItemService.java @@ -129,16 +129,27 @@ public static Node getFunding(String mdValue) { } String[] values = mdValue .split(DCInput.ComplexDefinitions.getSeparator(), -1); - // mind the order in input forms, org;code;projname;type - Element[] elements = {organization, code, projName, fundsType}; + // ORIGINAL order of funding was org;code;projname;type + // Element[] elements = {organization, code, projName, fundsType}; + + // TODO 2024/07 - order was changed to fundsType, code, org, projName + Element[] elements = {fundsType, code, organization, projName}; + for (int i = 0; i < elements.length; i++) { if (values.length <= i) { elements[i].appendChild(doc.createTextNode("")); } else { elements[i].appendChild(doc.createTextNode(values[i])); } - el.appendChild(elements[i]); + } + // swap to original order to display correctly + Element[] correctOrder = {organization, code, projName, fundsType}; + + for (Element e : correctOrder) { + el.appendChild(e); + } + return doc; } catch (ParserConfigurationException e) { return null; diff --git a/dspace-oai/src/main/java/org/dspace/xoai/filter/ColComFilter.java b/dspace-oai/src/main/java/org/dspace/xoai/filter/ColComFilter.java index 7efe6994dcb2..7ba3285377a2 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/filter/ColComFilter.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/filter/ColComFilter.java @@ -81,7 +81,19 @@ public boolean isShown(DSpaceItem item) { } private String getSetSpec() { - return "hdl_" + dso.getHandle().replace("/", "_"); + // Set prefix for the community as default value. + String handlePrefix; + if (dso instanceof Collection) { + // Prefix for the Collection. + handlePrefix = "col_"; + } else if (dso instanceof Community) { + handlePrefix = "com_"; + } else { + String message = "The DSO object must be of type Community or Collection."; + log.error(message); + throw new RuntimeException(message); + } + return handlePrefix + dso.getHandle().replace("/", "_"); } private DSpaceObject getDSpaceObject() { diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/NodeListXslFunction.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/NodeListXslFunction.java index 775d5c8c12be..a016aaff491d 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/NodeListXslFunction.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/NodeListXslFunction.java @@ -10,23 +10,25 @@ import static org.dspace.xoai.services.impl.resources.functions.StringXSLFunction.BASE; +import java.util.LinkedList; +import java.util.List; import java.util.Objects; -import javax.xml.transform.dom.DOMSource; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; -import net.sf.saxon.s9api.DocumentBuilder; import net.sf.saxon.s9api.ExtensionFunction; import net.sf.saxon.s9api.ItemType; import net.sf.saxon.s9api.OccurrenceIndicator; -import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.SequenceType; import net.sf.saxon.s9api.XdmAtomicValue; +import net.sf.saxon.s9api.XdmItem; import net.sf.saxon.s9api.XdmValue; import org.apache.logging.log4j.Logger; import org.bouncycastle.util.Arrays; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** @@ -40,7 +42,7 @@ public abstract class NodeListXslFunction implements ExtensionFunction { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(NodeListXslFunction.class); protected abstract String getFnName(); - protected abstract NodeList getNodeList(String param); + protected abstract List getList(String param); @Override final public QName getName() { return new QName(BASE, getFnName()); @@ -48,7 +50,7 @@ final public QName getName() { @Override final public SequenceType getResultType() { - return SequenceType.makeSequenceType(ItemType.ANY_NODE, OccurrenceIndicator.ZERO_OR_ONE); + return SequenceType.makeSequenceType(ItemType.STRING, OccurrenceIndicator.ZERO_OR_MORE); } @Override @@ -73,13 +75,24 @@ final public XdmValue call(XdmValue[] xdmValues) throws SaxonApiException { val = ""; } + List list = getList(val); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + javax.xml.parsers.DocumentBuilder db = null; + try { + db = dbf.newDocumentBuilder(); + Document newDoc = db.newDocument(); + Element rootElement = newDoc.createElement("root"); + newDoc.appendChild(rootElement); + + List items = new LinkedList<>(); + for (String item : list) { + items.add(new XdmAtomicValue(item)); + } + return new XdmValue(items); - NodeList nodeList = getNodeList(val); - Node oneNode = nodeList.item(0); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } - DocumentBuilder db = new Processor(false).newDocumentBuilder(); - DOMSource sourceObj = new DOMSource(oneNode); - var res = db.wrap(sourceObj); - return res; } } diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/UriToRestrictionsFn.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/UriToRestrictionsFn.java index 3487cde434ab..a9f63e035ab9 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/UriToRestrictionsFn.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/functions/UriToRestrictionsFn.java @@ -8,10 +8,12 @@ package org.dspace.xoai.services.impl.resources.functions; +import java.util.List; import javax.xml.parsers.ParserConfigurationException; import org.dspace.utils.LicenseUtil; + /** * Serves as proxy for call from XSL engine. Calls LicenseUtil * @author Marian Berger (marian.berger at dataquest.sk) @@ -23,7 +25,7 @@ protected String getFnName() { } @Override - protected org.w3c.dom.NodeList getNodeList(String param) { + protected List getList(String param) { try { return LicenseUtil.uriToRestrictions(param); } catch (ParserConfigurationException e) { diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index b32983581321..78f4571b6216 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -12,6 +12,7 @@ import java.io.InputStream; import java.sql.SQLException; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; @@ -29,13 +30,18 @@ import org.dspace.content.MetadataField; import org.dspace.content.MetadataValue; import org.dspace.content.authority.Choices; +import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.content.factory.ClarinServiceFactory; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipService; +import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; +import org.dspace.core.factory.CoreServiceFactory; +import org.dspace.core.service.LicenseService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xoai.data.DSpaceItem; @@ -45,6 +51,10 @@ */ @SuppressWarnings("deprecation") public class ItemUtils { + + + private static final ClarinLicenseResourceMappingService clarinLicenseResourceMappingService + = ClarinServiceFactory.getInstance().getClarinLicenseResourceMappingService(); private static final Logger log = LogManager.getLogger(ItemUtils.class); private static final MetadataExposureService metadataExposureService @@ -65,6 +75,9 @@ public class ItemUtils { private static final AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); + + private static final LicenseService licenseService = CoreServiceFactory.getInstance().getLicenseService(); + /** * Default constructor */ @@ -94,7 +107,8 @@ public static Element.Field createValue(String name, String value) { return e; } - private static Element createBundlesElement(Context context, Item item) throws SQLException { + private static Element createBundlesElement(Context context, Item item, AtomicBoolean restricted) + throws SQLException { Element bundles = create("bundles"); List bs; @@ -124,13 +138,24 @@ private static Element createBundlesElement(Context context, Item item) throws S // get handle of parent Item of this bitstream, if there // is one: List bn = bit.getBundles(); - if (!bn.isEmpty()) { + if (bn.size() > 0) { List bi = bn.get(0).getItems(); - if (!bi.isEmpty()) { + if (bi.size() > 0) { handle = bi.get(0).getHandle(); } } - url = baseUrl + "/bitstreams/" + bit.getID().toString() + "/download"; + if (bsName == null) { + List ext = bit.getFormat(context).getExtensions(); + bsName = "bitstream_" + sid + (ext.size() > 0 ? ext.get(0) : ""); + } + if (handle != null && baseUrl != null) { + url = baseUrl + "/bitstream/" + + handle + "/" + + sid + "/" + + URLUtils.encode(bsName); + } else { + url = URLUtils.encode(bsName); + } String cks = bit.getChecksum(); String cka = bit.getChecksumAlgorithm(); @@ -153,6 +178,18 @@ private static Element createBundlesElement(Context context, Item item) throws S bitstream.getField().add(createValue("checksum", cks)); bitstream.getField().add(createValue("checksumAlgorithm", cka)); bitstream.getField().add(createValue("sid", bit.getSequenceID() + "")); + bitstream.getField().add(createValue("id", bit.getID().toString())); + if (!restricted.get()) { + List clarinLicenseResourceMappingList = + clarinLicenseResourceMappingService.findByBitstreamUUID(context, bit.getID()); + for (ClarinLicenseResourceMapping clrm : clarinLicenseResourceMappingList) { + if (clrm.getLicense().getRequiredInfo() != null + && clrm.getLicense().getRequiredInfo().length() > 0) { + restricted.set( true); + break; + } + } + } } } @@ -277,8 +314,12 @@ public static Metadata retrieveMetadata(Context context, Item item) { // Done! Metadata has been read! // Now adding bitstream info + + //indicate restricted bitstreams -> restricted access + AtomicBoolean restricted = new AtomicBoolean(false); + try { - Element bundles = createBundlesElement(context, item); + Element bundles = createBundlesElement(context, item, restricted); metadata.getElement().add(bundles); } catch (SQLException e) { log.warn(e.getMessage(), e); @@ -290,6 +331,15 @@ public static Metadata retrieveMetadata(Context context, Item item) { other.getField().add(createValue("handle", item.getHandle())); other.getField().add(createValue("identifier", DSpaceItem.buildIdentifier(item.getHandle()))); other.getField().add(createValue("lastModifyDate", item.getLastModified().toString())); + + if (restricted.get()) { + other.getField().add(createValue("restrictedAccess", "true")); + } + // Because we reindex Solr, which is not done in vanilla + // The owning collection for workspace items is null + other.getField().add(createValue("owningCollection", + item.getOwningCollection() != null ? item.getOwningCollection().getName() : null)); + other.getField().add(createValue("itemId", item.getID().toString())); metadata.getElement().add(other); // Repository Info diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinAutoRegistrationController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinAutoRegistrationController.java index de2dccb9e866..af6c01714fa7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinAutoRegistrationController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinAutoRegistrationController.java @@ -95,12 +95,8 @@ public ResponseEntity sendEmail(HttpServletRequest request, HttpServletResponse log.error("Cannot load the `dspace.ui.url` property from the cfg."); throw new RuntimeException("Cannot load the `dspace.ui.url` property from the cfg."); } - // Generate token and create ClarinVerificationToken record with the token and user email. + // Generate token String verificationToken = Utils.generateHexKey(); - clarinVerificationToken.setEmail(email); - clarinVerificationToken.setToken(verificationToken); - clarinVerificationTokenService.update(context, clarinVerificationToken); - context.commit(); // Compose the url with the verification token. The user will be redirected to the UI. String autoregistrationURL = uiUrl + "/login/autoregistration?verification-token=" + verificationToken; @@ -121,6 +117,13 @@ public ResponseEntity sendEmail(HttpServletRequest request, HttpServletResponse return null; } + // Add ClarinVerificationToken record with the token and user email to the database only if the + // email was successfully send. + clarinVerificationToken.setEmail(email); + clarinVerificationToken.setToken(verificationToken); + clarinVerificationTokenService.update(context, clarinVerificationToken); + context.commit(); + return ResponseEntity.ok().build(); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinBitstreamImportController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinBitstreamImportController.java index 63380a756c2f..1620cb0609fc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinBitstreamImportController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinBitstreamImportController.java @@ -137,7 +137,11 @@ public BitstreamRest importBitstreamForExistingFile(HttpServletRequest request) Boolean deleted = Boolean.parseBoolean(deletedString); //set size bytes - bitstream.setSizeBytes(bitstreamRest.getSizeBytes()); + if (Objects.nonNull(bitstreamRest.getSizeBytes())) { + bitstream.setSizeBytes(bitstreamRest.getSizeBytes()); + } else { + log.info("SizeBytes is null. Bitstream UUID: " + bitstream.getID()); + } //set checksum bitstream.setChecksum(bitstreamRest.getCheckSum().getValue()); //set checksum algorithm @@ -206,7 +210,7 @@ public BitstreamRest importBitstreamForExistingFile(HttpServletRequest request) message += " for bundle with uuid: " + bundle.getID(); } log.error(message, e); - throw new RuntimeException("message", e); + throw new RuntimeException(message, e); } return bitstreamRest; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsDownloadService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsDownloadService.java index 7fdd9a9ade54..73b5d2b3dfef 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsDownloadService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsDownloadService.java @@ -34,7 +34,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; -import org.dspace.app.rest.utils.ClarinUtils; +import org.dspace.app.rest.utils.Utils; import org.dspace.services.ConfigurationService; import org.dspace.utils.DSpace; import org.json.simple.JSONArray; @@ -237,7 +237,7 @@ private static JSONArray downloadJSON(String url) { conn.setReadTimeout(10000); // Disable SSL certificate validation if (disableSSL && conn instanceof HttpsURLConnection) { - ClarinUtils.disableCertificateValidation((HttpsURLConnection) conn); + Utils.disableCertificateValidation((HttpsURLConnection) conn); } //Caution does not follow redirects, and even if you set it to http->https is not possible Object obj = parser.parse(new InputStreamReader(conn.getInputStream())); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java new file mode 100644 index 000000000000..5d89391ddd47 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java @@ -0,0 +1,40 @@ +/** + * 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.converter; + +import org.dspace.app.rest.model.PreviewContentRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.PreviewContent; +import org.springframework.stereotype.Component; + +/** + * This is the converter from/to the PreviewContent in the DSpace API data model and the + * REST data model + * + * @author Michaela Paurikova (michaela.paurikova at dataquest.sk) + */ +@Component +public class PreviewContentConverter implements DSpaceConverter { + + @Override + public PreviewContentRest convert(PreviewContent modelObject, Projection projection) { + PreviewContentRest previewContentRest = new PreviewContentRest(); + previewContentRest.setProjection(projection); + previewContentRest.setId(modelObject.getID()); + previewContentRest.setContent(modelObject.getContent()); + previewContentRest.setName(modelObject.getName()); + previewContentRest.setSize(modelObject.getSize()); + previewContentRest.setDirectory(modelObject.isDirectory()); + return previewContentRest; + } + + @Override + public Class getModelClass() { + return PreviewContent.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/hdlresolver/HdlResolverRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/hdlresolver/HdlResolverRestController.java index 540c3fd17243..ac2a4a1f7df6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/hdlresolver/HdlResolverRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/hdlresolver/HdlResolverRestController.java @@ -9,15 +9,18 @@ import java.text.MessageFormat; import java.util.List; +import java.util.Map; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import net.handle.hdllib.HandleException; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.handle.HandlePlugin; import org.dspace.handle.hdlresolver.HdlResolverDTO; import org.dspace.handle.hdlresolver.HdlResolverService; import org.springframework.beans.factory.annotation.Autowired; @@ -166,13 +169,28 @@ public ResponseEntity listHandles(HttpServletRequest request, @PathVaria /** * Maps the handle to a correct response. + * If the metadata parameter is provided, return additional handle values. * * @param request HttpServletRequest * @param handleResolver HdlResolverDTO - Handle resolver - * @return One element list using String if found, else null String. + * @return One element list using String if found, else map of metadata if param is entered, + * else "null" in case of an error */ private String resolveToURL(HttpServletRequest request, HdlResolverDTO handleResolver) { - return mapAsJson(this.hdlResolverService.resolveToURL(ContextUtil.obtainContext(request), handleResolver)); + String url = this.hdlResolverService.resolveToURL(ContextUtil.obtainContext(request), handleResolver); + String param = request.getParameter("metadata"); + if (StringUtils.isBlank(param)) { + return mapAsJson(url); + } + String handle = handleResolver.getHandle(); + try { + Map metadata = HandlePlugin.getMapHandleValues(handle); + metadata.put("URL", url); + return mapAsJson(metadata); + } catch (HandleException e) { + log.error("Failed to resolve handle values for handle: " + handle, e); + } + return "null"; } protected String mapAsJson(final String resolvedUrl) { @@ -183,6 +201,19 @@ protected String mapAsJson(final String resolvedUrl) { return json; } + protected String mapAsJson(final Map resolvedMap) { + ObjectMapper objectMapper = new ObjectMapper(); + String json = "null"; + try { + if (resolvedMap != null && !resolvedMap.isEmpty()) { + json = objectMapper.writeValueAsString(resolvedMap); + } + } catch (JsonProcessingException e) { + log.error("Error during conversion of response!", e); + } + return json; + } + protected String mapAsJson(final List jsonList) { String json = "null"; if (jsonList != null && !jsonList.isEmpty()) { @@ -194,5 +225,4 @@ protected String mapAsJson(final List jsonList) { } return json; } - } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java new file mode 100644 index 000000000000..410c3b941169 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java @@ -0,0 +1,70 @@ +/** + * 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.model; + +import org.dspace.app.rest.RestResourceController; + +public class PreviewContentRest extends BaseObjectRest { + public static final String NAME = "previewContent"; + public static final String CATEGORY = RestAddressableModel.CORE; + + private String name; + private String content; + private boolean isDirectory; + private String size; + + public PreviewContentRest() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isDirectory() { + return isDirectory; + } + + public void setDirectory(boolean directory) { + isDirectory = directory; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java new file mode 100644 index 000000000000..0dca4edbc6fb --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java @@ -0,0 +1,19 @@ +/** + * 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.model.hateoas; + +import org.dspace.app.rest.model.PreviewContentRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +@RelNameDSpaceResource(PreviewContentRest.NAME) +public class PreviewContentResource extends DSpaceResource { + public PreviewContentResource(PreviewContentRest ms, Utils utils) { + super(ms, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java index 3c03adb85d04..84a6c9b00890 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java @@ -9,6 +9,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.dspace.app.rest.utils.ContextUtil.obtainContext; +import static org.dspace.content.clarin.ClarinLicense.EXTRA_EMAIL; import static org.dspace.content.clarin.ClarinLicense.SEND_TOKEN; import static org.dspace.content.clarin.ClarinUserRegistration.ANONYMOUS_USER_REGISTRATION; import static org.springframework.web.bind.annotation.RequestMethod.POST; @@ -88,6 +89,9 @@ public class ClarinUserMetadataRestController { @Autowired ConfigurationService configurationService; + // Enum to distinguish between the two types of the email + enum MailType { ALLZIP, BITSTREAM } + @RequestMapping(value = "/zip", method = POST, consumes = APPLICATION_JSON) @PreAuthorize("permitAll()") public ResponseEntity manageUserMetadataForZIP(@RequestParam("itemUUID") UUID itemUUID, @@ -167,7 +171,7 @@ public ResponseEntity manageUserMetadataForZIP(@RequestParam("itemUUID") UUID it try { String email = getEmailFromUserMetadata(clarinUserMetadataRestList); this.sendEmailWithDownloadLink(context, item, clarinLicense, - email, downloadToken); + email, downloadToken, MailType.ALLZIP, clarinUserMetadataRestList); } catch (MessagingException e) { log.error("Cannot send the download email because: " + e.getMessage()); throw new RuntimeException("Cannot send the download email because: " + e.getMessage()); @@ -244,7 +248,7 @@ public ResponseEntity manageUserMetadata(@RequestParam("bitstreamUUID") UUID bit String email = getEmailFromUserMetadata(clarinUserMetadataRestList); ClarinLicense clarinLicense = this.getClarinLicense(clarinLicenseResourceMapping); this.sendEmailWithDownloadLink(context, bitstream, clarinLicense, - email, downloadToken); + email, downloadToken, MailType.BITSTREAM, clarinUserMetadataRestList); } catch (MessagingException e) { log.error("Cannot send the download email because: " + e.getMessage()); throw new RuntimeException("Cannot send the download email because: " + e.getMessage()); @@ -260,7 +264,9 @@ public ResponseEntity manageUserMetadata(@RequestParam("bitstreamUUID") UUID bit private void sendEmailWithDownloadLink(Context context, DSpaceObject dso, ClarinLicense clarinLicense, String email, - String downloadToken) + String downloadToken, + MailType mailType, + List clarinUserMetadataRestList) throws IOException, SQLException, MessagingException { if (StringUtils.isBlank(email)) { log.error("Cannot send email with download link because the email is empty."); @@ -287,13 +293,13 @@ private void sendEmailWithDownloadLink(Context context, DSpaceObject dso, // Redirect to `/api/bitstreams/{bitstreamId}/download?dtoken={downloadToken}` or // `/api/items/{itemId}/download?dtoken={downloadToken}` String downloadLink = uiUrl + "/" + (dso instanceof Item ? ItemRest.PLURAL_NAME : BitstreamRest.PLURAL_NAME) + - "/" + dso.getID() + "/download?dtoken=" + downloadToken; - + "/" + dso.getID() + "/download"; + String downloadLinkWithToken = downloadLink + "?dtoken=" + downloadToken; try { Locale locale = context.getCurrentLocale(); Email bean = Email.getEmail(I18nUtil.getEmailFilename(locale, "clarin_download_link")); bean.addArgument(dso.getName()); - bean.addArgument(downloadLink); + bean.addArgument(downloadLinkWithToken); bean.addArgument(clarinLicense.getDefinition()); bean.addArgument(helpDeskEmail); bean.addArgument(helpDeskPhoneNum); @@ -303,19 +309,104 @@ private void sendEmailWithDownloadLink(Context context, DSpaceObject dso, bean.addRecipient(email); bean.send(); } catch (MessagingException e) { - log.error("Cannot send the email because: " + e.getMessage()); + log.error("Cannot send the email with download link, because: " + e.getMessage()); + throw new MessagingException(e.getMessage()); + } + // If previous mail fails with exception, this block never executes = admin is NOT + // notified, if the mail is not really sent (if it fails HERE, not later, e.g. due to mail server issue). + sendAdminNotificationEmail(context, downloadLink, dso, clarinLicense, mailType, clarinUserMetadataRestList); + + } + + + private List getCCEmails(String ccAdmin, ClarinLicense clarinLicense) { + List ccEmails = new ArrayList<>(); + if (ccAdmin != null && !ccAdmin.isEmpty()) { + ccEmails.add(ccAdmin); + } + + String licenseName = clarinLicense.getName().trim().replace(" ", "_").toLowerCase(); + String[] licenseSpecialRecipients = configurationService.getArrayProperty("download.email.cc." + licenseName); + + if (licenseSpecialRecipients != null) { + for (String cc : licenseSpecialRecipients) { + if (!cc.isEmpty()) { + ccEmails.add(cc.trim()); + } + } + } + + return ccEmails; + } + + private void addAdminEmailArguments(Email mail, MailType mailType, DSpaceObject dso, String downloadLink, + ClarinLicense clarinLicense, Context context, + List extraMetadata) { + if (mailType == MailType.ALLZIP) { + mail.addArgument("all files requested"); + } else if (mailType == MailType.BITSTREAM) { + mail.addArgument(dso.getName()); + } else { + throw new IllegalArgumentException("The mail type is not supported."); + } + mail.addArgument(downloadLink); + mail.addArgument(clarinLicense.getDefinition()); + if (context.getCurrentUser() != null) { + mail.addArgument(context.getCurrentUser().getFullName()); + mail.addArgument(context.getCurrentUser().getEmail()); + + } else { + // used in place of Full Name of user + mail.addArgument("Anonymous user"); + // used in place of user's email + mail.addArgument("Anonymous user"); + } + StringBuilder exdata = new StringBuilder(); + for (ClarinUserMetadataRest data : extraMetadata) { + exdata.append(data.getMetadataKey()).append(": ").append(data.getMetadataValue()).append(", "); + } + mail.addArgument(exdata.toString()); + } + + private void sendAdminNotificationEmail(Context context, + String downloadLink, + DSpaceObject dso, + ClarinLicense clarinLicense, + MailType mailType, + List extraMetadata) + throws MessagingException, IOException { + try { + Locale locale = context.getCurrentLocale(); + Email email2Admin = Email.getEmail(I18nUtil.getEmailFilename(locale, "clarin_download_link_admin")); + String ccAdmin = configurationService.getProperty("download.email.cc"); + List ccEmails = getCCEmails(ccAdmin, clarinLicense); + + if (!ccEmails.isEmpty()) { + for (String cc : ccEmails) { + email2Admin.addRecipient(cc); + } + addAdminEmailArguments(email2Admin, mailType, dso, downloadLink, clarinLicense, context, extraMetadata); + + } + email2Admin.send(); + } catch (MessagingException e) { + log.error("Cannot send the notification email to admin because: " + e.getMessage()); throw new MessagingException(e.getMessage()); } } private String getEmailFromUserMetadata(List clarinUserMetadataRestList) { - String email = ""; + return getFieldFromUserMetadata(EXTRA_EMAIL, clarinUserMetadataRestList); + } + + private String getFieldFromUserMetadata(String field, List clarinUserMetadataRestList) { + String ret = ""; for (ClarinUserMetadataRest clarinUserMetadataRest : clarinUserMetadataRestList) { - if (StringUtils.equals(clarinUserMetadataRest.getMetadataKey(), SEND_TOKEN)) { - email = clarinUserMetadataRest.getMetadataValue(); + if (StringUtils.equals(clarinUserMetadataRest.getMetadataKey(), field)) { + ret = clarinUserMetadataRest.getMetadataValue(); } } - return email; + return ret; } public List processSignedInUser(Context context, EPerson currentUser, diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 4778cec279e3..224dc5a656c7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -12,25 +12,30 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.compress.archivers.ArchiveException; -import org.apache.commons.compress.archivers.ArchiveInputStream; -import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.Parameter; @@ -51,9 +56,11 @@ import org.dspace.content.Bundle; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.PreviewContent; import org.dspace.content.Thumbnail; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -76,6 +83,11 @@ @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamRestRepository extends DSpaceRestRepository { private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); + private final String ARCHIVE_TYPE_ZIP = "zip"; + private final String ARCHIVE_TYPE_TAR = "tar"; + // This constant is used to limit the length of the preview content stored in the database to prevent + // the database from being overloaded with large amounts of data. + private static final int MAX_PREVIEW_COUNT_LENGTH = 2000; @Autowired HandleService handleService; @@ -105,6 +117,9 @@ public class MetadataBitstreamRestRepository extends DSpaceRestRepository findByHandle(@Parameter(value = "handl List fileInfos = new ArrayList<>(); boolean canPreview = findOutCanPreview(context, bitstream); if (canPreview) { - fileInfos = getFilePreviewContent(context, bitstream, fileInfos); + List prContents = previewContentService.findRootByBitstream(context, + bitstream.getID()); + // Generate new content if we didn't find any + if (prContents.isEmpty()) { + fileInfos = getFilePreviewContent(context, bitstream, fileInfos); + // Do not store HTML content in the database because it could be longer than the limit + // of the database column + if (!StringUtils.equals("text/html", bitstream.getFormat(context).getMIMEType())) { + for (FileInfo fi : fileInfos) { + createPreviewContent(context, bitstream, fi); + } + } + } else { + for (PreviewContent pc : prContents) { + fileInfos.add(createFileInfo(pc)); + } + } } MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), @@ -165,6 +196,7 @@ public Page findByHandle(@Parameter(value = "handl metadataValueWrappers.add(bts); rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); } + context.commit(); } return new PageImpl<>(rs, pageable, rs.size()); @@ -220,6 +252,58 @@ private List getFilePreviewContent(Context context, Bitstream bitstrea return fileInfos; } + /** + * Define the hierarchy organization for preview content and file info. + * The hierarchy is established by the sub map. + * If a content item contains a sub map, it is considered a directory; if not, it is a file. + * @param sourceMap sub map that is used as a pattern + * @param creator creator function + * @return created sub map + */ + private Hashtable createSubMap(Map sourceMap, Function creator) { + if (sourceMap == null) { + return null; + } + + Hashtable sub = new Hashtable<>(); + for (Map.Entry entry : sourceMap.entrySet()) { + sub.put(entry.getKey(), creator.apply(entry.getValue())); + } + return sub; + } + + /** + * Create file info from preview content. + * @param pc preview content + * @return created file info + */ + private FileInfo createFileInfo(PreviewContent pc) { + Hashtable sub = createSubMap(pc.sub, this::createFileInfo); + return new FileInfo(pc.name, pc.content, pc.size, pc.isDirectory, sub); + } + + /** + * Create preview content from file info for bitstream. + * @param context DSpace context object + * @param bitstream bitstream + * @param fi file info + * @return created preview content + * @throws SQLException If database error is occurred + */ + private PreviewContent createPreviewContent(Context context, Bitstream bitstream, FileInfo fi) throws SQLException { + Hashtable sub = createSubMap(fi.sub, value -> { + try { + return createPreviewContent(context, bitstream, value); + } catch (SQLException e) { + String msg = "Database error occurred while creating new preview content " + + "for bitstream with ID = " + bitstream.getID() + " Error msg: " + e.getMessage(); + log.error(msg, e); + throw new RuntimeException(msg, e); + } + }); + return previewContentService.create(context, bitstream, fi.name, fi.content, fi.isDirectory, fi.size, sub); + } + /** * Convert InputStream of the ZIP file into FileInfo classes. * @@ -238,19 +322,28 @@ private List processInputStreamToFilePreview(Context context, Bitstrea List fileInfos, InputStream inputStream) throws IOException, SQLException, ParserConfigurationException, SAXException, ArchiveException { String bitstreamMimeType = bitstream.getFormat(context).getMIMEType(); - if (bitstreamMimeType.equals("text/plain") || bitstreamMimeType.equals("text/html")) { - String data = getFileContent(inputStream); + if (bitstreamMimeType.equals("text/plain")) { + String data = getFileContent(inputStream, true); + fileInfos.add(new FileInfo(data, false)); + } else if (bitstreamMimeType.equals("text/html")) { + String data = getFileContent(inputStream, false); fileInfos.add(new FileInfo(data, false)); } else { String data = ""; - if (bitstream.getFormat(context).getExtensions().contains("zip")) { - data = extractFile(inputStream, "zip"); - fileInfos = FileTreeViewGenerator.parse(data); - } else if (bitstream.getFormat(context).getExtensions().contains("tar")) { - ArchiveInputStream is = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.TAR, - inputStream); - data = extractFile(is, "tar"); - fileInfos = FileTreeViewGenerator.parse(data); + if (bitstream.getFormat(context).getMIMEType().equals("application/zip")) { + data = extractFile(inputStream, ARCHIVE_TYPE_ZIP); + try { + fileInfos = FileTreeViewGenerator.parse(data); + } catch (Exception e) { + log.error("Cannot extract file content because: {}", e.getMessage()); + } + } else if (bitstream.getFormat(context).getMIMEType().equals("application/x-tar")) { + data = extractFile(inputStream, ARCHIVE_TYPE_TAR); + try { + fileInfos = FileTreeViewGenerator.parse(data); + } catch (Exception e) { + log.error("Cannot extract file content because: {}", e.getMessage()); + } } } return fileInfos; @@ -293,76 +386,108 @@ private String composePreviewURL(Context context, Item item, Bitstream bitstream return url; } - /** - * Convert ZIP file into structured String. - * @param inputStream Input stream with ZIP content - * @param fileType ZIP/TAR - * @return structured String + * Creates a temporary file with the appropriate extension based on the specified file type. + * @param fileType the type of file for which to create a temporary file + * @return a Path object representing the temporary file + * @throws IOException if an I/O error occurs while creating the file */ - public String extractFile(InputStream inputStream, String fileType) { - List filePaths = new ArrayList<>(); - Path tempFile = null; - FileSystem zipFileSystem = null; + private Path createTempFile(String fileType) throws IOException { + String extension = ARCHIVE_TYPE_TAR.equals(fileType) ? + String.format(".%s", ARCHIVE_TYPE_TAR) : String.format(".%s", ARCHIVE_TYPE_ZIP); + return Files.createTempFile("temp", extension); + } - try { - switch (fileType) { - case "tar": - tempFile = Files.createTempFile("temp", ".tar"); - break; - default: - tempFile = Files.createTempFile("temp", ".zip"); + /** + * Adds a file path and its size to the list of file paths. + * If the path represents a directory, appends a "/" to the path. + * @param filePaths the list of file paths to add to + * @param path the file or directory path + * @param size the size of the file or directory + */ + private void addFilePath(List filePaths, String path, long size) { + String fileInfo = (Files.isDirectory(Paths.get(path))) ? path + "/|" + size : path + "|" + size; + filePaths.add(fileInfo); + } + /** + * Processes a TAR file, extracting its entries and adding their paths to the provided list. + * @param filePaths the list to populate with the extracted file paths + * @param tempFile the temporary TAR file to process + * @throws IOException if an I/O error occurs while reading the TAR file + */ + private void processTarFile(List filePaths, Path tempFile) throws IOException { + try (InputStream fi = Files.newInputStream(tempFile); + TarArchiveInputStream tis = new TarArchiveInputStream(fi)) { + TarArchiveEntry entry; + while ((entry = tis.getNextTarEntry()) != null) { + addFilePath(filePaths, entry.getName(), entry.getSize()); } + } + } - Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); + /** + * Processes a ZIP file, extracting its entries and adding their paths to the provided list. + * @param filePaths the list to populate with the extracted file paths + * @param zipFileSystem the FileSystem object representing the ZIP file + * @throws IOException if an I/O error occurs while reading the ZIP file + */ + private void processZipFile(List filePaths, FileSystem zipFileSystem) throws IOException { + Path root = zipFileSystem.getPath("/"); + Files.walk(root).forEach(path -> { + try { + long fileSize = Files.size(path); + addFilePath(filePaths, path.toString().substring(1), fileSize); + } catch (IOException e) { + log.error("An error occurred while getting the size of the zip file.", e); + } + }); + } - zipFileSystem = FileSystems.newFileSystem(tempFile, (ClassLoader) null); - Path root = zipFileSystem.getPath("/"); - Files.walk(root) - .forEach(path -> { - try { - long fileSize = Files.size(path); - if (Files.isDirectory(path)) { - filePaths.add(path.toString().substring(1) + "/|" + fileSize ); - } else { - filePaths.add(path.toString().substring(1) + "|" + fileSize ); - } - } catch (IOException e) { - e.printStackTrace(); - } - }); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (zipFileSystem != null) { - try { - zipFileSystem.close(); - } catch (IOException e) { - e.printStackTrace(); - } + /** + * Closes the specified FileSystem resource if it is not null. + * @param zipFileSystem the FileSystem to close + */ + private void closeFileSystem(FileSystem zipFileSystem) { + if (Objects.nonNull(zipFileSystem)) { + try { + zipFileSystem.close(); + } catch (IOException e) { + log.error("An error occurred while closing the zip file.", e); } + } + } - if (tempFile != null) { - try { - Files.delete(tempFile); - } catch (IOException e) { - e.printStackTrace(); - } + /** + * Deletes the specified temporary file if it is not null. + * @param tempFile the Path object representing the temporary file to delete + */ + private void deleteTempFile(Path tempFile) { + if (Objects.nonNull(tempFile)) { + try { + Files.delete(tempFile); + } catch (IOException e) { + log.error("An error occurred while deleting temp file.", e); } } + } + /** + * Builds an XML response string based on the provided list of file paths. + * @param filePaths the list of file paths to include in the XML response + * @return an XML string representation of the file paths + */ + private String buildXmlResponse(List filePaths) { // Is a folder regex String folderRegex = "/|\\d+"; Pattern pattern = Pattern.compile(folderRegex); StringBuilder sb = new StringBuilder(); - sb.append(("")); + sb.append(""); Iterator iterator = filePaths.iterator(); int fileCounter = 0; - while ((iterator.hasNext() && fileCounter < maxPreviewCount)) { + while (iterator.hasNext() && fileCounter < maxPreviewCount) { String filePath = iterator.next(); - // Check if the file is a folder Matcher matcher = pattern.matcher(filePath); if (!matcher.matches()) { @@ -375,27 +500,90 @@ public String extractFile(InputStream inputStream, String fileType) { if (fileCounter > maxPreviewCount) { sb.append("...too many files...|0"); } - sb.append(("")); + sb.append(""); return sb.toString(); } + /** + * Extracts files from an InputStream, processes them based on the specified file type (tar or zip), + * and returns an XML representation of the file paths. + * @param inputStream the InputStream containing the file data + * @param fileType the type of file to extract ("tar" or "zip") + * @return an XML string representing the extracted file paths + */ + public String extractFile(InputStream inputStream, String fileType) { + List filePaths = new ArrayList<>(); + Path tempFile = null; + FileSystem zipFileSystem = null; + + try { + // Create a temporary file based on the file type + tempFile = createTempFile(fileType); + + // Copy the input stream to the temporary file + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); + + // Process the file based on its type + if (ARCHIVE_TYPE_TAR.equals(fileType)) { + processTarFile(filePaths, tempFile); + } else { + zipFileSystem = FileSystems.newFileSystem(tempFile, (ClassLoader) null); + processZipFile(filePaths, zipFileSystem); + } + } catch (IOException e) { + log.error(String.format("An error occurred while extracting file of type %s.", fileType), e); + } finally { + closeFileSystem(zipFileSystem); + deleteTempFile(tempFile); + } + + return buildXmlResponse(filePaths); + } + /** * Read input stream and return content as String * @param inputStream to read * @return content of the inputStream as a String * @throws IOException */ - public String getFileContent(InputStream inputStream) throws IOException { + public String getFileContent(InputStream inputStream, boolean cutResult) throws IOException { StringBuilder content = new StringBuilder(); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - - String line; - while ((line = reader.readLine()) != null) { - content.append(line).append("\n"); + // Generate the preview content in the UTF-8 encoding + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + try { + String line; + while ((line = reader.readLine()) != null) { + content.append(line).append("\n"); + } + } catch (UnsupportedEncodingException e) { + log.error("UnsupportedEncodingException during creating the preview content because: ", e); + } catch (IOException e) { + log.error("IOException during creating the preview content because: ", e); } reader.close(); - return content.toString(); + return cutResult ? ensureMaxLength(content.toString()) : content.toString(); + } + + /** + * Trims the input string to ensure it does not exceed the maximum length for the database column. + * @param input The original string to be trimmed. + * @return A string that is truncated to the maximum length if necessary. + */ + private static String ensureMaxLength(String input) { + if (input == null) { + return null; + } + + // Check if the input string exceeds the maximum preview length + if (input.length() > MAX_PREVIEW_COUNT_LENGTH) { + // Truncate the string and append " . . ." + int previewLength = MAX_PREVIEW_COUNT_LENGTH - 6; // Subtract length of " . . ." + return input.substring(0, previewLength) + " . . ."; + } else { + // Return the input string as is if it's within the preview length + return input; + } } /** diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java new file mode 100644 index 000000000000..3fc352ec4f5d --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java @@ -0,0 +1,65 @@ +/** + * 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.List; +import java.util.Objects; + +import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.model.PreviewContentRest; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +@Component(PreviewContentRest.CATEGORY + "." + PreviewContentRest.NAME) +public class PreviewContentRestRepository extends DSpaceRestRepository { + + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(PreviewContentRestRepository.class); + + @Autowired + PreviewContentService previewContentService; + + @Override + public PreviewContentRest findOne(Context context, Integer integer) { + PreviewContent previewContent; + try { + previewContent = previewContentService.find(context, integer); + } catch (SQLException e) { + String msg = "Database error occurred while finding preview content " + + "for ID = " + integer + " Error msg: " + e.getMessage(); + log.error(msg, e); + throw new RuntimeException(msg, e); + } + if (Objects.isNull(previewContent)) { + return null; + } + return converter.toRest(previewContent, utils.obtainProjection()); + } + + @Override + public Page findAll(Context context, Pageable pageable) { + try { + List previewContentList = previewContentService.findAll(context); + return converter.toRestPage(previewContentList, pageable, utils.obtainProjection()); + } catch (SQLException e) { + String msg = "Database error occurred while finding all preview contents. Error msg: " + e.getMessage(); + log.error(msg, e); + throw new RuntimeException(msg, e); + } + } + + @Override + public Class getDomainClass() { + return PreviewContentRest.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/clarin/ClarinShibbolethLoginFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/clarin/ClarinShibbolethLoginFilter.java index 02ecaa593a90..821be38ed2a2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/clarin/ClarinShibbolethLoginFilter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/clarin/ClarinShibbolethLoginFilter.java @@ -126,7 +126,6 @@ public Authentication attemptAuthentication(HttpServletRequest req, // If the Idp doesn't send the email in the request header, send the redirect order to the FE for the user // to fill in the email. - String netidHeader = configurationService.getProperty("authentication-shibboleth.netid-header"); String emailHeader = configurationService.getProperty("authentication-shibboleth.email-header"); Context context = ContextUtil.obtainContext(req); @@ -154,34 +153,34 @@ public Authentication attemptAuthentication(HttpServletRequest req, shib_headers = new ShibHeaders(req); } - // Retrieve the netid and email values from the header. - String netid = shib_headers.get_single(netidHeader); String idp = shib_headers.get_idp(); // If the clarin verification object is not null load the email from there otherwise from header. - String email = Objects.isNull(clarinVerificationToken) ? - shib_headers.get_single(emailHeader) : clarinVerificationToken.getEmail(); + String email; + if (Objects.isNull(clarinVerificationToken)) { + email = shib_headers.get_single(emailHeader); + if (StringUtils.isNotEmpty(email)) { + email = ClarinShibAuthentication.sortEmailsAndGetFirst(email); + } + } else { + email = clarinVerificationToken.getEmail(); + } EPerson ePerson = null; - if (StringUtils.isNotEmpty(netid)) { + try { + ePerson = ClarinShibAuthentication.findEpersonByNetId(shib_headers.getNetIdHeaders(), shib_headers, ePerson, + ePersonService, context, false); + } catch (SQLException e) { + // It is logged in the ClarinShibAuthentication class. + } + + if (Objects.isNull(ePerson) && StringUtils.isNotEmpty(email)) { try { - // If email is null and netid exist try to find the eperson by netid and load its email - if (StringUtils.isEmpty(email)) { - ePerson = ePersonService.findByNetid(context, netid); - email = Objects.isNull(email) ? this.getEpersonEmail(ePerson) : null; - } else { - // Try to get user by the email because of possible duplicate of the user email - ePerson = ePersonService.findByEmail(context, email); - } - } catch (SQLException ignored) { - // + ePerson = ePersonService.findByEmail(context, email); + } catch (SQLException e) { + // It is logged in the ClarinShibAuthentication class. } } - // logging - log.info("Shib-Identity-Provider: " + idp); - log.info("authentication-shibboleth.netid-header: " + netidHeader + " with value: " + netid); - log.info("authentication-shibboleth.email-header: " + emailHeader + " with value: " + email); - try { if (StringUtils.isEmpty(idp)) { log.error("Cannot load the idp from the request headers."); @@ -307,7 +306,12 @@ private void redirectAfterSuccess(HttpServletRequest request, HttpServletRespons if (StringUtils.equalsAnyIgnoreCase(redirectHostName, allowedHostNames.toArray(new String[0]))) { log.debug("Shibboleth redirecting to " + redirectUrl); - response.sendRedirect(redirectUrl); + // Encode the UTF-8 characters from redirect URL to UTF-8, to ensure it's properly encoded for the browser + String encodedRedirectUrl = org.dspace.app.rest.utils.Utils.encodeNonAsciiCharacters(redirectUrl); + if (StringUtils.isEmpty(encodedRedirectUrl)) { + log.error("Invalid Encoded Shibboleth redirectURL=" + redirectUrl + ". URL is empty!"); + } + response.sendRedirect(encodedRedirectUrl); } else { log.error("Invalid Shibboleth redirectURL=" + redirectUrl + ". URL doesn't match hostname of server or UI!"); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java index a54473e66d1e..682983acc2ab 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest.submit.step; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -35,12 +36,14 @@ import org.dspace.app.rest.submit.SubmissionService; import org.dspace.app.rest.submit.factory.PatchOperationFactory; import org.dspace.app.rest.submit.factory.impl.PatchOperation; +import org.dspace.app.rest.utils.ContextUtil; import org.dspace.app.util.DCInput; import org.dspace.app.util.DCInputSet; import org.dspace.app.util.DCInputsReader; import org.dspace.app.util.DCInputsReaderException; import org.dspace.app.util.SubmissionStepConfig; import org.dspace.content.InProgressSubmission; +import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.RelationshipMetadataService; import org.dspace.content.factory.ContentServiceFactory; @@ -48,6 +51,7 @@ import org.dspace.core.Utils; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; /** @@ -59,6 +63,8 @@ */ public class DescribeStep extends AbstractProcessingStep { + public static final String KEY_VALUE_SEPARATOR = "=>"; + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DescribeStep.class); // Input reader for form configuration @@ -89,15 +95,34 @@ public DataDescribe getData(SubmissionService submissionService, InProgressSubmi private void readField(InProgressSubmission obj, SubmissionStepConfig config, DataDescribe data, DCInputSet inputConfig) throws DCInputsReaderException { - String documentTypeValue = ""; - List documentType = itemService.getMetadataByMetadataString(obj.getItem(), - configurationService.getProperty("submit.type-bind.field", "dc.type")); - if (documentType.size() > 0) { - documentTypeValue = documentType.get(0).getValue(); + List documentTypeValueList = new ArrayList<>(); + List typeBindFields = Arrays.asList( + configurationService.getArrayProperty("submit.type-bind.field", new String[0])); + + for (String typeBindField : typeBindFields) { + String typeBFKey = typeBindField; + if (typeBindField.contains(KEY_VALUE_SEPARATOR)) { + String[] parts = typeBindField.split(KEY_VALUE_SEPARATOR); + // Get the second part of the split - the metadata field + typeBFKey = parts[1]; + } + List documentType = itemService.getMetadataByMetadataString(obj.getItem(), typeBFKey); + if (documentType.size() > 0) { + documentTypeValueList.add(documentType.get(0).getValue()); + } } // Get list of all field names (including qualdrop names) allowed for this dc.type - List allowedFieldNames = inputConfig.populateAllowedFieldNames(documentTypeValue); + List allowedFieldNames = new ArrayList<>(); + + if (CollectionUtils.isEmpty(documentTypeValueList)) { + // If no dc.type is set, we allow all fields + allowedFieldNames.addAll(inputConfig.populateAllowedFieldNames(null)); + } else { + documentTypeValueList.forEach(documentTypeValue -> { + allowedFieldNames.addAll(inputConfig.populateAllowedFieldNames(documentTypeValue)); + }); + } // Loop input rows and process submitted metadata for (DCInput[] row : inputConfig.getFields()) { @@ -118,6 +143,19 @@ private void readField(InProgressSubmission obj, SubmissionStepConfig config, Da for (String fieldName : fieldsName) { List mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName); + // Use default value if the input type is dropdown and has defined a default value + if (CollectionUtils.isEmpty(mdv) && input.isDropdownValue() && input.hasDefaultValue()) { + Context context = ContextUtil.obtainCurrentRequestContext(); + try { + MetadataValue mv = itemService.addMetadata(context, obj.getItem(), input.getSchema(), + input.getElement(), input.getQualifier(), Item.ANY, input.getDefaultValue()); + mdv.add(mv); + } catch (SQLException e) { + log.error("Cannot create a metadata value object with the default value, because: " + + "{}", e.getMessage()); + throw new RuntimeException(e); + } + } for (MetadataValue md : mdv) { MetadataValueRest dto = new MetadataValueRest(); dto.setAuthority(md.getAuthority()); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java index 1ff9639906ac..fe46ee0ba9b3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java @@ -7,6 +7,8 @@ */ package org.dspace.app.rest.submit.step.validation; +import static org.dspace.app.rest.submit.step.DescribeStep.KEY_VALUE_SEPARATOR; + import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; @@ -68,22 +70,46 @@ public List validate(SubmissionService submissionService, InProgressS SubmissionStepConfig config) throws DCInputsReaderException, SQLException { List errors = new ArrayList<>(); - String documentTypeValue = ""; DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId()); - List documentType = itemService.getMetadataByMetadataString(obj.getItem(), - configurationService.getProperty("submit.type-bind.field", "dc.type")); - if (documentType.size() > 0) { - documentTypeValue = documentType.get(0).getValue(); + List documentTypeValueList = new ArrayList<>(); + // Get the list of type-bind fields. It could be in the form of schema.element.qualifier=>metadata_field, or + // just metadata_field + List typeBindFields = Arrays.asList( + configurationService.getArrayProperty("submit.type-bind.field", new String[0])); + + for (String typeBindField : typeBindFields) { + String typeBFKey = typeBindField; + // If the type-bind field is in the form of schema.element.qualifier=>metadata_field, split it and get the + // metadata field + if (typeBindField.contains(KEY_VALUE_SEPARATOR)) { + String[] parts = typeBindField.split(KEY_VALUE_SEPARATOR); + // Get the second part of the split - the metadata field + typeBFKey = parts[1]; + } + // Get the metadata value for the type-bind field + List documentType = itemService.getMetadataByMetadataString(obj.getItem(), typeBFKey); + if (documentType.size() > 0) { + documentTypeValueList.add(documentType.get(0).getValue()); + } } - // Get list of all field names (including qualdrop names) allowed for this dc.type - List allowedFieldNames = inputConfig.populateAllowedFieldNames(documentTypeValue); + // Get list of all field names (including qualdrop names) allowed for this dc.type, or specific type-bind field + List allowedFieldNames = new ArrayList<>(); + + if (CollectionUtils.isEmpty(documentTypeValueList)) { + // If no dc.type is set, we allow all fields + allowedFieldNames.addAll(inputConfig.populateAllowedFieldNames(null)); + } else { + documentTypeValueList.forEach(documentTypeValue -> { + allowedFieldNames.addAll(inputConfig.populateAllowedFieldNames(documentTypeValue)); + }); + } - // Begin the actual validation loop for (DCInput[] row : inputConfig.getFields()) { for (DCInput input : row) { String fieldKey = - metadataAuthorityService.makeFieldKey(input.getSchema(), input.getElement(), input.getQualifier()); + metadataAuthorityService.makeFieldKey(input.getSchema(), input.getElement(), + input.getQualifier()); boolean isAuthorityControlled = metadataAuthorityService.isAuthorityControlled(fieldKey); List fieldsName = new ArrayList(); @@ -99,10 +125,10 @@ public List validate(SubmissionService submissionService, InProgressS // Check the lookup list. If no other inputs of the same field name allow this type, // then remove. This includes field name without qualifier. - if (!input.isAllowedFor(documentTypeValue) && (!allowedFieldNames.contains(fullFieldname) + if (!input.isAllowedFor(documentTypeValueList) && (!allowedFieldNames.contains(fullFieldname) && !allowedFieldNames.contains(input.getFieldName()))) { itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(), - obj.getItem(), mdv); + obj.getItem(), mdv); } else { validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors); if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) { @@ -126,7 +152,7 @@ public List validate(SubmissionService submissionService, InProgressS for (String fieldName : fieldsName) { boolean valuesRemoved = false; List mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName); - if (!input.isAllowedFor(documentTypeValue)) { + if (!input.isAllowedFor(documentTypeValueList)) { // Check the lookup list. If no other inputs of the same field name allow this type, // then remove. Otherwise, do not if (!(allowedFieldNames.contains(fieldName))) { @@ -134,26 +160,26 @@ public List validate(SubmissionService submissionService, InProgressS obj.getItem(), mdv); valuesRemoved = true; log.debug("Stripping metadata values for " + input.getFieldName() + " on type " - + documentTypeValue + " as it is allowed by another input of the same field " + + + documentTypeValueList + " as it is allowed by another input of the same field " + "name"); } else { log.debug("Not removing unallowed metadata values for " + input.getFieldName() + " on type " - + documentTypeValue + " as it is allowed by another input of the same field " + + + documentTypeValueList + " as it is allowed by another input of the same field " + "name"); } } validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors); if (((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE) - && !valuesRemoved) + && !valuesRemoved) || !isValidComplexDefinitionMetadata(input, mdv)) { // Is the input required for *this* type? In other words, are we looking at a required // input that is also allowed for this document type - if (input.isAllowedFor(documentTypeValue)) { + if (input.isAllowedFor(documentTypeValueList)) { // since this field is missing add to list of error // fields addError(errors, ERROR_VALIDATION_REQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" + - input.getFieldName()); + input.getFieldName()); } } if (LOCAL_METADATA_HAS_CMDI.equals(fieldName)) { @@ -167,7 +193,9 @@ public List validate(SubmissionService submissionService, InProgressS } } } + } + return errors; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ClarinUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ClarinUtils.java deleted file mode 100644 index 2a93f5793205..000000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ClarinUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.dspace.app.rest.utils; - -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import org.springframework.stereotype.Component; - -/** - * Collection of utility methods for clarin customized operations - * - * @author Milan Majchrak (dspace at dataquest.sk) - */ -@Component -public class ClarinUtils { - - private ClarinUtils() { - } - - /** - * Disables SSL certificate validation for the given connection - * - * @param connection - */ - public static void disableCertificateValidation(HttpsURLConnection connection) { - try { - // Create a TrustManager that trusts all certificates - TrustManager[] trustAllCerts = { new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } } - }; - - // Install the TrustManager - SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, trustAllCerts, new SecureRandom()); - connection.setSSLSocketFactory(sslContext.getSocketFactory()); - - // Set a HostnameVerifier that accepts all hostnames - connection.setHostnameVerifier((hostname, session) -> true); - - } catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new RuntimeException("Error disabling SSL certificate validation", e); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java index ed6e26ed0fb7..347a23b86de5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java @@ -29,6 +29,11 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -44,6 +49,10 @@ import java.util.TreeSet; import java.util.UUID; import javax.annotation.Nullable; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; @@ -1076,4 +1085,52 @@ private BaseObjectRest findBaseObjectRest(Context context, String apiCategory, S context.restoreAuthSystemState(); } } + + /** + * Disables SSL certificate validation for the given connection + * + * @param connection + */ + public static void disableCertificateValidation(HttpsURLConnection connection) { + try { + // Create a TrustManager that trusts all certificates + TrustManager[] trustAllCerts = { new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { + } } + }; + + // Install the TrustManager + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + + // Set a HostnameVerifier that accepts all hostnames + connection.setHostnameVerifier((hostname, session) -> true); + + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new RuntimeException("Error disabling SSL certificate validation", e); + } + } + + /** + * Function to encode only non-ASCII characters + */ + public static String encodeNonAsciiCharacters(String input) { + StringBuilder result = new StringBuilder(); + for (char ch : input.toCharArray()) { + if (!StringUtils.isAsciiPrintable(String.valueOf(ch))) { // Use Apache Commons method + result.append(URLEncoder.encode(String.valueOf(ch), StandardCharsets.UTF_8)); + } else { + result.append(ch); // Leave ASCII characters intact + } + } + return result.toString(); + } } 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 b4c0a5c11e53..60379713d4ff 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -49,6 +49,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; import org.dspace.content.Bitstream; @@ -2768,10 +2769,12 @@ public void deleteBitstreamsInBulk_collectionAdmin() throws Exception { .withEmail("col2admin@test.com") .withPassword(password) .build(); - Group col1_AdminGroup = collectionService.createAdministrators(context, col1); - Group col2_AdminGroup = collectionService.createAdministrators(context, col2); - groupService.addMember(context, col1_AdminGroup, col1Admin); - groupService.addMember(context, col2_AdminGroup, col2Admin); + Group col1_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col1) + .addMember(col1Admin) + .build(); + Group col2_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col2) + .addMember(col2Admin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); @@ -2872,8 +2875,9 @@ public void deleteBitstreamsInBulk_communityAdmin() throws Exception { .withEmail("parentComAdmin@test.com") .withPassword(password) .build(); - Group parentComAdminGroup = communityService.createAdministrators(context, parentCommunity); - groupService.addMember(context, parentComAdminGroup, parentCommunityAdmin); + Group parentComAdminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity) + .addMember(parentCommunityAdmin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinBitstreamImportControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinBitstreamImportControllerIT.java index 9e0c0f339a7c..74ad6b65e687 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinBitstreamImportControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinBitstreamImportControllerIT.java @@ -270,6 +270,41 @@ public void importBitstreamForExistingFileValidationErrorTest() throws Exception assertEquals(bitstreamService.findAll(context).size(), 0); } + @Test + public void importDeletedBitstreamTest() throws Exception { + //input data + ObjectNode checksumNode = jsonNodeFactory.objectNode(); + checksumNode.set("checkSumAlgorithm", null); + checksumNode.set("value", null); + ObjectNode node = jsonNodeFactory.objectNode(); + node.set("sizeBytes", null); + node.set("checkSum", checksumNode); + + //create new bitstream for existing file + ObjectMapper mapper = new ObjectMapper(); + uuid = UUID.fromString(read( getClient(token).perform(post("/api/clarin/import/core/bitstream") + .content(mapper.writeValueAsBytes(node)) + .contentType(contentType) + .param("internal_id", internalId) + .param("storeNumber", "0") + .param("deleted", "true")) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + "$.id")); + + bitstream = bitstreamService.find(context, uuid); + assertEquals(bitstream.getSizeBytes(), 0); + assertEquals(bitstream.getInternalId(), internalId); + assertEquals(bitstream.getStoreNumber(), 0); + assertEquals(bitstream.getSequenceID(), -1); + assertEquals(bitstream.isDeleted(), true); + + //clean all + context.turnOffAuthorisationSystem(); + BitstreamBuilder.deleteBitstream(uuid); + context.restoreAuthSystemState(); + } + private void checkCreatedBitstream(UUID uuid, String internalId, int storeNumber, String bitstreamFormat, int sequence, boolean deleted, long sizeBytes, String checkSum) throws SQLException { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsControllerIT.java index 0075011fd0bf..d20298ac9116 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinDiscoJuiceFeedsControllerIT.java @@ -22,7 +22,7 @@ import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; -import org.dspace.app.rest.utils.ClarinUtils; +import org.dspace.app.rest.utils.Utils; import org.dspace.services.ConfigurationService; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; @@ -64,7 +64,7 @@ public void testDiscoFeedURL() throws Exception { // Disable SSL certificate validation if (disableSSL && conn instanceof HttpsURLConnection) { - ClarinUtils.disableCertificateValidation((HttpsURLConnection) conn); + Utils.disableCertificateValidation((HttpsURLConnection) conn); } Object obj = parser.parse(new InputStreamReader(conn.getInputStream())); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java index 592363ba4270..ed88186c33fa 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java @@ -170,12 +170,16 @@ public void notAuthorizedUser_shouldSendEmail() throws Exception { ClarinUserMetadataRest clarinUserMetadata3 = new ClarinUserMetadataRest(); clarinUserMetadata3.setMetadataKey("SEND_TOKEN"); - clarinUserMetadata3.setMetadataValue("test@test.edu"); + + ClarinUserMetadataRest clarinUserMetadata4 = new ClarinUserMetadataRest(); + clarinUserMetadata4.setMetadataKey("EXTRA_EMAIL"); + clarinUserMetadata4.setMetadataValue("test@test.edu"); List clarinUserMetadataRestList = new ArrayList<>(); clarinUserMetadataRestList.add(clarinUserMetadata1); clarinUserMetadataRestList.add(clarinUserMetadata2); clarinUserMetadataRestList.add(clarinUserMetadata3); + clarinUserMetadataRestList.add(clarinUserMetadata4); String adminToken = getAuthToken(admin.getEmail(), password); // Load bitstream from the item. @@ -256,12 +260,16 @@ public void authorizedUserWithoutMetadata_shouldSendEmail() throws Exception { ClarinUserMetadataRest clarinUserMetadata3 = new ClarinUserMetadataRest(); clarinUserMetadata3.setMetadataKey("SEND_TOKEN"); - clarinUserMetadata3.setMetadataValue("test@test.edu"); + + ClarinUserMetadataRest clarinUserMetadata4 = new ClarinUserMetadataRest(); + clarinUserMetadata4.setMetadataKey("EXTRA_EMAIL"); + clarinUserMetadata4.setMetadataValue("test@test.edu"); List clarinUserMetadataRestList = new ArrayList<>(); clarinUserMetadataRestList.add(clarinUserMetadata1); clarinUserMetadataRestList.add(clarinUserMetadata2); clarinUserMetadataRestList.add(clarinUserMetadata3); + clarinUserMetadataRestList.add(clarinUserMetadata4); String adminToken = getAuthToken(admin.getEmail(), password); @@ -359,12 +367,16 @@ public void authorizedUserWithMetadata_shouldSendEmail() throws Exception { ClarinUserMetadataRest clarinUserMetadata3 = new ClarinUserMetadataRest(); clarinUserMetadata3.setMetadataKey("SEND_TOKEN"); - clarinUserMetadata3.setMetadataValue("test@test.edu"); + + ClarinUserMetadataRest clarinUserMetadata4 = new ClarinUserMetadataRest(); + clarinUserMetadata4.setMetadataKey("EXTRA_EMAIL"); + clarinUserMetadata4.setMetadataValue("test@test.edu"); List clarinUserMetadataRestList = new ArrayList<>(); clarinUserMetadataRestList.add(clarinUserMetadata1); clarinUserMetadataRestList.add(clarinUserMetadata2); clarinUserMetadataRestList.add(clarinUserMetadata3); + clarinUserMetadataRestList.add(clarinUserMetadata4); String adminToken = getAuthToken(admin.getEmail(), password); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkflowItemRestRepositoryIT.java index 20ec8dc38f24..5815533b44c5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkflowItemRestRepositoryIT.java @@ -320,4 +320,49 @@ public void shouldCreateProvenanceMessageOnItemSubmit() throws Exception { } assertThat(containsSubmitterProvenance, is(true)); } + + // When some input field has ... in the submission-forms.xml + @Test + public void shouldCreateItemWithCustomTypeBindField() throws Exception { + context.turnOffAuthorisationSystem(); + String CITATION_VALUE = "Some citation"; + + //** GIVEN ** + //1. A community with one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + // Submitter group - allow deposit a new item without workflow + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .build(); + + //3. a workspace item + WorkspaceItem wsitem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Type-bind test") + .withIssueDate("2017-10-17") + .grantLicense() + .withMetadata("dc", "identifier", "citation", CITATION_VALUE) + .build(); + + context.restoreAuthSystemState(); + + // get the submitter auth token + String authToken = getAuthToken(admin.getEmail(), password); + + // submit the workspaceitem to start the workflow + getClient(authToken) + .perform(post(BASE_REST_SERVER_URL + "/api/workflow/workflowitems") + .content("/api/submission/workspaceitems/" + wsitem.getID()) + .contentType(textUriContentType)) + .andExpect(status().isCreated()); + + // Load deposited item and check the provenance metadata + Item depositedItem = itemService.find(context, wsitem.getItem().getID()); + List mvList = itemService.getMetadata(depositedItem, "dc", "identifier", + "citation", Item.ANY); + assertFalse(mvList.isEmpty()); + assertThat(mvList.get(0).getValue(), is(CITATION_VALUE)); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java index 2e3ad348e3df..1b55cc44288a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java @@ -836,7 +836,7 @@ public void createItemWithConfiguredNormalHandle() throws Exception { context.restoreAuthSystemState(); Assert.assertNotNull(installedItem); - Assert.assertTrue(installedItem.getHandle().startsWith("123456789/2")); + Assert.assertTrue(installedItem.getHandle().startsWith("123456789/")); } /** @@ -889,6 +889,84 @@ public void createItemWithConfiguredSpecificHandle() throws Exception { context.restoreAuthSystemState(); } + /** + * The `lr.pid.community.configurations` property is null. + */ + @Test + public void createItemWhenSpecificHandleIsNotConfigured() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + // Keep the value of the current configuration property + String[] currentCommunityConfig = configurationService.getArrayProperty("lr.pid.community.configurations"); + // Set property in the cfg + configurationService.setProperty("lr.pid.community.configurations", null); + // Reload the set property in the hash map. + PIDConfiguration pidConfiguration = PIDConfiguration.getInstance(); + pidConfiguration.reloadPidCommunityConfigurations(); + + Collection col = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection").build(); + WorkspaceItem wItem = WorkspaceItemBuilder.createWorkspaceItem(context, col) + .withTitle("Item with custom handle") + .withIssueDate("2017-10-17") + .build(); + + // Installing the Item assign a new Handle to the Item + Item installedItem = installItemService.installItem(context, wItem); + // It should not throw an exception + Assert.assertNotNull(installedItem); + + // Restore the configuration property + configurationService.setProperty("lr.pid.community.configurations", currentCommunityConfig); + pidConfiguration.reloadPidCommunityConfigurations(); + context.restoreAuthSystemState(); + } + + /** + * The `lr.pid.community.configurations` has no subprefix. That means the handle won't contain the `-` character. + */ + @Test + public void createItemWhenSpecificHandleHasNoSubprefix() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + // Keep the value of the current configuration property + String[] currentCommunityConfig = configurationService.getArrayProperty("lr.pid.community.configurations"); + + // Set property in the cfg + String specificCommunityHandleDef = "community=*,prefix=LRT," + + "type=local,canonical_prefix=http://hdl.handle.net/,subprefix="; + configurationService.setProperty("lr.pid.community.configurations", specificCommunityHandleDef); + // Reload the set property in the hash map. + PIDConfiguration pidConfiguration = PIDConfiguration.getInstance(); + pidConfiguration.reloadPidCommunityConfigurations(); + + Collection col = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection").build(); + WorkspaceItem wItem = WorkspaceItemBuilder.createWorkspaceItem(context, col) + .withTitle("Item with custom handle") + .withIssueDate("2017-10-17") + .build(); + + // Installing the Item assign a new Handle to the Item + Item installedItem = installItemService.installItem(context, wItem); + // It should not throw an exception + Assert.assertNotNull(installedItem); + Assert.assertTrue(installedItem.getHandle().startsWith("LRT/")); + Assert.assertFalse(installedItem.getHandle().contains("-")); + + // Restore the configuration property + configurationService.setProperty("lr.pid.community.configurations", currentCommunityConfig); + // Reload the set property in the hash map. + pidConfiguration.reloadPidCommunityConfigurations(); + context.restoreAuthSystemState(); + } + /** * Create Clarin License Label object for testing purposes. */ diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java index f6ab10c087ad..8d490109220d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java @@ -28,9 +28,9 @@ import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; -import org.dspace.content.service.CollectionService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; @@ -41,10 +41,6 @@ public class CollectionGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CollectionService collectionService; - @Autowired private GroupService groupService; @@ -68,7 +64,7 @@ public void setup() { @Test public void getCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -81,7 +77,7 @@ public void getCollectionAdminGroupTest() throws Exception { @Test public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -95,7 +91,7 @@ public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { @Test public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -109,7 +105,7 @@ public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -119,7 +115,7 @@ public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -413,7 +409,7 @@ public void postCollectionAdminGroupCreateAdminGroupUnProcessablePermanent() thr @Test public void deleteCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -428,7 +424,7 @@ public void deleteCollectionAdminGroupTest() throws Exception { @Test public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -443,7 +439,7 @@ public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exceptio @Test public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -458,7 +454,7 @@ public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -474,7 +470,7 @@ public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -493,7 +489,7 @@ public void deleteCollectionAdminGroupForbiddenTest() throws Exception { @Test public void deleteCollectionAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -512,7 +508,7 @@ public void deleteCollectionAdminGroupNotFoundTest() throws Exception { @Test public void getCollectionSubmittersGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -525,7 +521,7 @@ public void getCollectionSubmittersGroupTest() throws Exception { @Test public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -539,7 +535,7 @@ public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Except @Test public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -553,7 +549,7 @@ public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -563,7 +559,7 @@ public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -860,7 +856,7 @@ public void postCollectionSubmittersGroupCreateSubmittersGroupUnProcessablePerma @Test public void deleteCollectionSubmitterGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -875,7 +871,7 @@ public void deleteCollectionSubmitterGroupTest() throws Exception { @Test public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -890,7 +886,7 @@ public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exc @Test public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -905,7 +901,7 @@ public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exceptio @Test public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -924,7 +920,7 @@ public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -945,7 +941,7 @@ public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { @Test public void deleteCollectionSubmittersGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -961,7 +957,8 @@ public void getCollectionItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -977,7 +974,8 @@ public void getCollectionDefaultItemReadGroupTestParentCommunityAdmin() throws E String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -995,7 +993,8 @@ public void getCollectionDefaultItemReadGroupTestCollectionAdmin() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1012,7 +1011,8 @@ public void getCollectionDefaultItemReadGroupUnAuthorizedTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1025,7 +1025,8 @@ public void getCollectionDefaultItemReadGroupForbiddenTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1321,7 +1322,7 @@ public void deleteCollectionDefaultItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1345,7 +1346,7 @@ public void deleteCollectionDefaultItemReadGroupTestParentCommunityAdmin() throw String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1367,7 +1368,7 @@ public void deleteCollectionDefaultItemReadGroupTestCollectionAdmin() throws Exc String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1389,7 +1390,8 @@ public void deleteCollectionDefaultItemReadGroupUnAuthorizedTest() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1408,7 +1410,8 @@ public void deleteCollectionDefaultItemReadGroupForbiddenTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1430,7 +1433,7 @@ public void deleteCollectionDefaultItemReadGroupNotFoundTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1445,8 +1448,8 @@ public void getCollectionBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1462,8 +1465,8 @@ public void getCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() thr String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1480,8 +1483,8 @@ public void getCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1498,8 +1501,8 @@ public void getCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1512,8 +1515,8 @@ public void getCollectionDefaultBitstreamReadGroupForbiddenTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1811,8 +1814,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1835,8 +1838,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1859,8 +1862,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throw String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1882,8 +1885,8 @@ public void deleteCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1902,8 +1905,8 @@ public void deleteCollectionDefaultBitstreamReadGroupForbiddenTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1918,8 +1921,8 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1931,7 +1934,7 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep public void getWorkflowGroupForCollectionAndRole() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1944,7 +1947,7 @@ public void getWorkflowGroupForCollectionAndRole() throws Exception { @Test public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1958,7 +1961,7 @@ public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Ex @Test public void getWorkflowGroupForCollectionAndRoleWrongUUIDCollectionNotFound() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1979,7 +1982,7 @@ public void getWorkflowGroupForCollectionAndRoleWrongRoleNotFound() throws Excep public void getWorkflowGroupCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1994,7 +1997,7 @@ public void getWorkflowGroupCommunityAdmin() throws Exception { @Test public void getWorkflowGroupCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2009,7 +2012,7 @@ public void getWorkflowGroupCollectionAdmin() throws Exception { @Test public void getWorkflowGroupUnAuthorized() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2019,7 +2022,7 @@ public void getWorkflowGroupUnAuthorized() throws Exception { @Test public void getWorkflowGroupForbidden() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2324,7 +2327,7 @@ public void postCollectionWorkflowGroupCreateWorkflowGroupUnProcessablePermanent @Test public void deleteCollectionWorkflowGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -2339,7 +2342,7 @@ public void deleteCollectionWorkflowGroupTest() throws Exception { @Test public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2355,7 +2358,7 @@ public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Excep @Test public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2371,7 +2374,7 @@ public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception @Test public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2387,7 +2390,7 @@ public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2406,7 +2409,7 @@ public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { @Test public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2418,7 +2421,7 @@ public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { @Test public void deleteCollectionWorkflowGroupWithPooledTaskTest() throws Exception { context.turnOffAuthorisationSystem(); - Group reviewer = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group reviewer = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); // Submit an Item into the workflow -> moves to the "reviewer" step's pool. // The role must have at least one EPerson, otherwise the WSI gets archived immediately diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java index 37548553b143..074a7e6a3557 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java @@ -34,8 +34,6 @@ import org.dspace.builder.GroupBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -49,19 +47,12 @@ public class CommunityAdminGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CommunityService communityService; - @Autowired private GroupService groupService; @Autowired private AuthorizeService authorizeService; - @Autowired - private CollectionService collectionService; - @Autowired private ConfigurationService configurationService; @@ -78,7 +69,7 @@ public void setup() { @Test public void getCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -91,7 +82,8 @@ public void getCommunityAdminGroupTest() throws Exception { @Test public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); + // TODO: this should actually be "add member", not directly setting a policy, right? authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -106,7 +98,7 @@ public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -116,7 +108,7 @@ public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -379,7 +371,7 @@ public void postCommunityAdminGroupCreateAdminGroupUnProcessablePermanent() thro @Test public void deleteCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -397,7 +389,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) .withName("Sub Community") .build(); - Group adminGroup = communityService.createAdministrators(context, child1); + GroupBuilder.createCommunityAdminGroup(context, child1).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -412,7 +404,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -429,7 +421,7 @@ public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -449,7 +441,7 @@ public void deleteCommunityAdminGroupForbiddenTest() throws Exception { @Test public void deleteCommunityAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -462,7 +454,7 @@ public void deleteCommunityAdminGroupNotFoundTest() throws Exception { public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -489,7 +481,7 @@ public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() th public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -526,7 +518,7 @@ public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -554,7 +546,7 @@ public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -591,7 +583,9 @@ public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFa public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -617,7 +611,9 @@ public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Ex public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -653,7 +649,7 @@ public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thro public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -681,7 +677,7 @@ public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() t public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -719,7 +715,7 @@ public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFals public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -748,7 +744,7 @@ public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse( public void communityAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); 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 4300c987589a..4e22702b7c13 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -59,15 +59,11 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.GroupService; import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; @@ -85,9 +81,6 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest { ResourcePolicyService resourcePolicyService; @Autowired private ConfigurationService configurationService; - @Autowired - private CollectionService collectionService; - @Autowired private AuthorizeService authorizeService; @@ -761,259 +754,136 @@ public void addChildGroupTest() throws Exception { @Test public void addChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(parentGroup, childGroup1) - ); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - assertTrue( - groupService.isMember(parentGroup, childGroup2) - ); + assertTrue( + groupService.isMember(parentGroup, childGroup1) + ); - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + assertTrue( + groupService.isMember(parentGroup, childGroup2) + ); } @Test public void addChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + context.turnOffAuthorisationSystem(); - context.commit(); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.commit(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnauthorized()); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - groupService.addMember(context, childGroup1, parentGroup); - groupService.update(context, childGroup1); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); } @Test @@ -1081,252 +951,118 @@ public void addMemberTest() throws Exception { @Test public void addMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(context, member1, parentGroup) - ); + parentGroup = context.reloadEntity(parentGroup); + member1 = context.reloadEntity(member1); + member2 = context.reloadEntity(member2); - assertTrue( - groupService.isMember(context, member2, parentGroup) - ); + assertTrue( + groupService.isMember(context, member1, parentGroup) + ); - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + assertTrue( + groupService.isMember(context, member2, parentGroup) + ); } @Test public void addMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1379,28 +1115,17 @@ public void removeChildGroupTest() throws Exception { @Test public void removeChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - Group parentGroup = null; - Group childGroup = null; - - try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup = groupService.create(context); - - groupService.addMember(context, parentGroup, childGroup); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); @@ -1414,163 +1139,71 @@ public void removeChildGroupCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(parentGroup, childGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } } @Test public void removeChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1614,32 +1247,19 @@ public void removeMemberTest() throws Exception { @Test public void removeMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member = null; - try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member = ePersonService.create(context); - - groupService.addMember(context, parentGroup, member); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + Community community = CommunityBuilder.createCommunity(context).build(); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(member) + .addMember(eperson) + .build(); assertTrue(groupService.isMember(context, member, parentGroup)); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1652,151 +1272,68 @@ public void removeMemberCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(context, member, parentGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test public void removeMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isForbidden()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isUnauthorized()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); @@ -1804,15 +1341,6 @@ public void removeMemberUnprocessableTest() throws Exception { getClient(authToken).perform( delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + UUID.randomUUID()) ).andExpect(status().isUnprocessableEntity()); - - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test @@ -2586,7 +2114,8 @@ public void commAdminAndColAdminCanManageItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group itemReadGroup = collectionService.createDefaultReadGroup(context, col1, itemGroupString, defaultItemRead); + Group itemReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, + col1, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); @@ -2670,8 +2199,9 @@ public void commAdminAndColAdminCanManageBitstreamReadGroupTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group bitstreamReadGroup = collectionService.createDefaultReadGroup(context, col1, bitstreamGroupString, - defaultBitstreamRead); + Group bitstreamReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, col1, bitstreamGroupString, + defaultBitstreamRead) + .build(); context.restoreAuthSystemState(); @@ -2792,7 +2322,8 @@ public void commAdminAndColAdminCanManageWorkflowGroupsTest() throws Exception { public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2827,7 +2358,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2853,7 +2385,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws E public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2889,7 +2422,8 @@ public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thr public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2923,7 +2457,8 @@ public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2961,7 +2496,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFal public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2990,7 +2526,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse public void collectionAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java index a4a69ca8b1d7..55be6be9e428 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java @@ -83,7 +83,7 @@ public void findAll() throws Exception { context.restoreAuthSystemState(); getClient().perform(get("/api/core/metadatafields") - .param("size", String.valueOf(100))) + .param("size", String.valueOf(200))) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.metadatafields", Matchers.hasItems( @@ -95,7 +95,7 @@ public void findAll() throws Exception { .andExpect(jsonPath("$._links.next.href", Matchers.containsString("/api/core/metadatafields"))) .andExpect(jsonPath("$._links.last.href", Matchers.containsString("/api/core/metadatafields"))) - .andExpect(jsonPath("$.page.size", is(100))); + .andExpect(jsonPath("$.page.size", is(200))); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java new file mode 100644 index 000000000000..494c7cffc889 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java @@ -0,0 +1,105 @@ +/** + * 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.status; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.BundleBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.PreviewContentBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class PreviewContentRestRepositoryIT extends AbstractControllerIntegrationTest { + + private PreviewContent previewContent; + private Bitstream bitstream; + + @Autowired + private PreviewContentService previewContentService; + + @Before + public void setup() throws SQLException, AuthorizeException, IOException { + context.turnOffAuthorisationSystem(); + // create bitstream + Community comm = CommunityBuilder.createCommunity(context) + .withName("Community Test") + .build(); + Collection col = CollectionBuilder.createCollection(context, comm).withName("Collection Test").build(); + Item publicItem = ItemBuilder.createItem(context, col) + .withTitle("Test") + .withIssueDate("2010-10-17") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .build(); + Bundle bundle1 = BundleBuilder.createBundle(context, publicItem) + .withName("Bundle Test") + .build(); + String bitstreamContent = "ThisIsSomeDummyText"; + bitstream = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder. + createBitstream(context, bundle1, is) + .withName("Bitstream1 Test") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + // create content previews + previewContent = PreviewContentBuilder.createPreviewContent(context, bitstream, "test1.txt", + null, false, "100", null).build(); + } + + @After + @Override + public void destroy() throws Exception { + //clean all + BitstreamBuilder.deleteBitstream(bitstream.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent.getID()); + super.destroy(); + } + + @Test + public void findOne() throws Exception { + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/core/previewContents/" + previewContent.getID())) + .andExpect(status().isOk()); + } + + @Test + public void findAll() throws Exception { + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/core/previewContents") + .param("size", String.valueOf(100))) + .andExpect(status().isOk()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java new file mode 100644 index 000000000000..5a4fbf7f2ddf --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java @@ -0,0 +1,132 @@ +/** + * 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 java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.BundleBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.PreviewContentBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class PreviewContentServiceImplIT extends AbstractControllerIntegrationTest { + + @Autowired + PreviewContentService previewContentService; + PreviewContent previewContent0; + PreviewContent previewContent1; + PreviewContent previewContent2; + Bitstream bitstream1; + Bitstream bitstream2; + + @Before + public void setup() throws SQLException, AuthorizeException, IOException { + context.turnOffAuthorisationSystem(); + // create bitstream + Community comm = CommunityBuilder.createCommunity(context) + .withName("Community Test") + .build(); + Collection col = CollectionBuilder.createCollection(context, comm).withName("Collection Test").build(); + Item publicItem = ItemBuilder.createItem(context, col) + .withTitle("Test") + .withIssueDate("2010-10-17") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .build(); + Bundle bundle1 = BundleBuilder.createBundle(context, publicItem) + .withName("Bundle Test") + .build(); + String bitstreamContent = "ThisIsSomeDummyText"; + bitstream1 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream1 = BitstreamBuilder. + createBitstream(context, bundle1, is) + .withName("Bitstream1 Test") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + bitstream2 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream2 = BitstreamBuilder. + createBitstream(context, bundle1, is) + .withName("Bitstream2 Test") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + // create content previews + previewContent0 = PreviewContentBuilder.createPreviewContent(context, bitstream1, "test1.txt", + null, false, "100", null).build(); + Map previewContentMap = new HashMap<>(); + previewContentMap.put(previewContent0.getName(), previewContent0); + previewContent1 = PreviewContentBuilder.createPreviewContent(context, bitstream1, "", null, + true, "0", previewContentMap).build(); + previewContent2 = PreviewContentBuilder.createPreviewContent(context, bitstream2, "test2.txt", null, + false, "200", previewContentMap).build(); + } + + @After + @Override + public void destroy() throws Exception { + //clean all + BitstreamBuilder.deleteBitstream(bitstream1.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent0.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent1.getID()); + BitstreamBuilder.deleteBitstream(bitstream2.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent2.getID()); + super.destroy(); + } + + @Test + public void testFindAll() throws Exception { + List previewContentList = previewContentService.findAll(context); + Assert.assertEquals(previewContentList.size(), 3); + Assert.assertEquals(previewContent0.getID(), previewContentList.get(0).getID()); + Assert.assertEquals(previewContent1.getID(), previewContentList.get(1).getID()); + Assert.assertEquals(previewContent2.getID(), previewContentList.get(2).getID()); + } + + @Test + public void testFindByBitstream() throws Exception { + List previewContentList = previewContentService.findByBitstream(context, bitstream2.getID()); + Assert.assertEquals(previewContentList.size(), 1); + Assert.assertEquals(previewContent2.getID(), previewContentList.get(0).getID()); + } + + @Test + public void testFindRootByBitstream() throws Exception { + List previewContentList = + previewContentService.findRootByBitstream(context, bitstream1.getID()); + Assert.assertEquals(previewContentList.size(), 1); + Assert.assertEquals(previewContent1.getID(), previewContentList.get(0).getID()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java index a42f219909aa..7a412ad425b3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java @@ -103,7 +103,7 @@ public void testAdmin() throws Exception { // Verify the general admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -116,7 +116,7 @@ public void testCommunityAdmin() throws Exception { // Verify the community admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -129,7 +129,7 @@ public void testSubCommunityAdmin() throws Exception { // Verify the subcommunity admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -142,7 +142,7 @@ public void testCollectionAdmin() throws Exception { // Verify the collection admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -155,7 +155,7 @@ public void testSubmitter() throws Exception { // Verify a submitter doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -176,7 +176,7 @@ public void testSubGroupOfAdminGroup() throws Exception { // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -197,7 +197,7 @@ public void testSubGroupOfCommunityAdminGroup() throws Exception { // Verify an ePerson in a subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -218,7 +218,7 @@ public void testSubGroupOfSubCommunityAdminGroup() throws Exception { // Verify an ePerson in a subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -239,7 +239,7 @@ public void testSubGroupOfCollectionAdminGroup() throws Exception { // Verify an ePerson in a subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -260,7 +260,7 @@ public void testSubGroupOfSubmitterGroup() throws Exception { // Verify an ePerson in a subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -285,7 +285,7 @@ public void testSubSubGroupOfAdminGroup() throws Exception { // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -310,7 +310,7 @@ public void testSubSubGroupOfCommunityAdminGroup() throws Exception { // Verify an ePerson in a sub-subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -335,7 +335,7 @@ public void testSubSubGroupOfSubCommunityAdminGroup() throws Exception { // Verify an ePerson in a sub-subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -360,7 +360,7 @@ public void testSubSubGroupOfCollectionAdminGroup() throws Exception { // Verify an ePerson in a sub-subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -385,7 +385,7 @@ public void testSubSubGroupOfSubmitterGroup() throws Exception { // Verify an ePerson in a sub-subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -404,7 +404,7 @@ public void testAdminNoCommunityGroupPermission() throws Exception { // Verify the general admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -422,7 +422,7 @@ public void testCommunityAdminNoCommunityGroupPermission() throws Exception { // Verify the community admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -440,7 +440,7 @@ public void testSubCommunityAdminNoCommunityGroupPermission() throws Exception { // Verify the subcommunity admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -458,7 +458,7 @@ public void testCollectionAdminNoCommunityGroupPermission() throws Exception { // Verify the collection admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -476,7 +476,7 @@ public void testSubmitterNoCommunityGroupPermission() throws Exception { // Verify a submitter doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -502,7 +502,7 @@ public void testSubGroupOfAdminGroupNoCommunityGroupPermission() throws Exceptio // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -528,7 +528,7 @@ public void testSubGroupOfCommunityAdminGroupNoCommunityGroupPermission() throws // Verify an ePerson in a subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -554,7 +554,7 @@ public void testSubGroupOfSubCommunityAdminGroupNoCommunityGroupPermission() thr // Verify an ePerson in a subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -580,7 +580,7 @@ public void testSubGroupOfCollectionAdminGroupNoCommunityGroupPermission() throw // Verify an ePerson in a subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -606,7 +606,7 @@ public void testSubGroupOfSubmitterGroupNoCommunityGroupPermission() throws Exce // Verify an ePerson in a subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -636,7 +636,7 @@ public void testSubSubGroupOfAdminGroupNoCommunityGroupPermission() throws Excep // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -666,7 +666,7 @@ public void testSubSubGroupOfCommunityAdminGroupNoCommunityGroupPermission() thr // Verify an ePerson in a sub-subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -696,7 +696,7 @@ public void testSubSubGroupOfSubCommunityAdminGroupNoCommunityGroupPermission() // Verify an ePerson in a sub-subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -726,7 +726,7 @@ public void testSubSubGroupOfCollectionAdminGroupNoCommunityGroupPermission() th // Verify an ePerson in a sub-subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -756,7 +756,7 @@ public void testSubSubGroupOfSubmitterGroupNoCommunityGroupPermission() throws E // Verify an ePerson in a sub-subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -779,7 +779,7 @@ public void testAdminNoCollectionGroupPermission() throws Exception { // Verify the general admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -801,7 +801,7 @@ public void testCommunityAdminNoCollectionGroupPermission() throws Exception { // Verify the community admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -823,7 +823,7 @@ public void testSubCommunityAdminNoCollectionGroupPermission() throws Exception // Verify the subcommunity admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -845,7 +845,7 @@ public void testCollectionAdminNoCollectionGroupPermission() throws Exception { // Verify the collection admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -867,7 +867,7 @@ public void testSubmitterNoCollectionGroupPermission() throws Exception { // Verify a submitter doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -897,7 +897,7 @@ public void testSubGroupOfAdminGroupNoCollectionGroupPermission() throws Excepti // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -927,7 +927,7 @@ public void testSubGroupOfCommunityAdminGroupNoCollectionGroupPermission() throw // Verify an ePerson in a subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -957,7 +957,7 @@ public void testSubGroupOfSubCommunityAdminGroupNoCollectionGroupPermission() th // Verify an ePerson in a subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -987,7 +987,7 @@ public void testSubGroupOfCollectionAdminGroupNoCollectionGroupPermission() thro // Verify an ePerson in a subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1017,7 +1017,7 @@ public void testSubGroupOfSubmitterGroupNoCollectionGroupPermission() throws Exc // Verify an ePerson in a subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1051,7 +1051,7 @@ public void testSubSubGroupOfAdminGroupNoCollectionGroupPermission() throws Exce // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1085,7 +1085,7 @@ public void testSubSubGroupOfCommunityAdminGroupNoCollectionGroupPermission() th // Verify an ePerson in a sub-subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1119,7 +1119,7 @@ public void testSubSubGroupOfSubCommunityAdminGroupNoCollectionGroupPermission() // Verify an ePerson in a sub-subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1153,7 +1153,7 @@ public void testSubSubGroupOfCollectionAdminGroupNoCollectionGroupPermission() t // Verify an ePerson in a sub-subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1187,7 +1187,7 @@ public void testSubSubGroupOfSubmitterGroupNoCollectionGroupPermission() throws // Verify an ePerson in a sub-subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1214,7 +1214,7 @@ public void testAdminNoComColGroupPermission() throws Exception { // Verify the general admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1240,7 +1240,7 @@ public void testCommunityAdminNoComColGroupPermission() throws Exception { // Verify the community admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1266,7 +1266,7 @@ public void testSubCommunityAdminNoComColGroupPermission() throws Exception { // Verify the subcommunity admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1292,7 +1292,7 @@ public void testCollectionAdminNoComColGroupPermission() throws Exception { // Verify the collection admin has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1318,7 +1318,7 @@ public void testSubmitterNoComColGroupPermission() throws Exception { // Verify a submitter doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1352,7 +1352,7 @@ public void testSubGroupOfAdminGroupNoComColGroupPermission() throws Exception { // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1386,7 +1386,7 @@ public void testSubGroupOfCommunityAdminGroupNoComColGroupPermission() throws Ex // Verify an ePerson in a subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1420,7 +1420,7 @@ public void testSubGroupOfSubCommunityAdminGroupNoComColGroupPermission() throws // Verify an ePerson in a subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1454,7 +1454,7 @@ public void testSubGroupOfCollectionAdminGroupNoComColGroupPermission() throws E // Verify an ePerson in a subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1488,7 +1488,7 @@ public void testSubGroupOfSubmitterGroupNoComColGroupPermission() throws Excepti // Verify an ePerson in a subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1526,7 +1526,7 @@ public void testSubSubGroupOfAdminGroupNoComColGroupPermission() throws Exceptio // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1564,7 +1564,7 @@ public void testSubSubGroupOfCommunityAdminGroupNoComColGroupPermission() throws // Verify an ePerson in a sub-subgroup of a community admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1602,7 +1602,7 @@ public void testSubSubGroupOfSubCommunityAdminGroupNoComColGroupPermission() thr // Verify an ePerson in a sub-subgroup of a subcommunity admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1640,7 +1640,7 @@ public void testSubSubGroupOfCollectionAdminGroupNoComColGroupPermission() throw // Verify an ePerson in a sub-subgroup of a collection admin group has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1678,7 +1678,7 @@ public void testSubSubGroupOfSubmitterGroupNoComColGroupPermission() throws Exce // Verify an ePerson in a sub-subgroup of submitter group doesn't have this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/sites/" + siteService.findSite(context).getID())) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/hdlresolver/HdlResolverRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/hdlresolver/HdlResolverRestControllerIT.java index 8227caffe616..f19f4944478e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/hdlresolver/HdlResolverRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/hdlresolver/HdlResolverRestControllerIT.java @@ -9,6 +9,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -84,7 +85,36 @@ public void givenMappedIdentifierWhenCallHdlresolverThenReturnsMappedURL() throw getClient() .perform(get("/wrongController/" + publicItem1.getHandle())) .andExpect(status().isNotFound()); + } + + @Test + public void givenMappedIdentifierWhenCallHdlresolverThenReturnsMappedParams() throws Exception { + context.turnOffAuthorisationSystem(); + + // ** START GIVEN ** + parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1") + .withLogo("TestingContentForLogo").build(); + Item publicItem1 = ItemBuilder.createItem(context, col1).withTitle("Public item 1").withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John").withSubject("ExtraEntry") + .withHandle("123456789/testHdlResolver").build(); + + context.restoreAuthSystemState(); + + // ** END GIVEN ** + getClient() + .perform(get(HdlResolverRestController.RESOLVE + publicItem1.getHandle()) + .param("metadata", "true")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.URL", + StringContains.containsString("123456789/testHdlResolver"))) + .andExpect(jsonPath("$.TITLE", StringContains.containsString("Public item 1"))) + .andExpect(jsonPath("$.REPOSITORY", is(configurationService.getProperty("dspace.name")))) + .andExpect(jsonPath("$.REPORTEMAIL", + StringContains.containsString("dspace-help@ufal.mff.cuni.cz"))) + .andExpect(jsonPath("$.SUBMITDATE").exists()); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/ClarinShibbolethLoginFilterIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/ClarinShibbolethLoginFilterIT.java index 8b62e95bed79..6cf8c6058d7a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/ClarinShibbolethLoginFilterIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/ClarinShibbolethLoginFilterIT.java @@ -9,6 +9,7 @@ import static org.dspace.app.rest.security.ShibbolethLoginFilterIT.PASS_ONLY; import static org.dspace.app.rest.security.clarin.ClarinShibbolethLoginFilter.VERIFICATION_TOKEN_HEADER; +import static org.dspace.rdf.negotiation.MediaRange.token; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -19,6 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -53,8 +55,12 @@ public class ClarinShibbolethLoginFilterIT extends AbstractControllerIntegrationTest { public static final String[] SHIB_ONLY = {"org.dspace.authenticate.clarin.ClarinShibAuthentication"}; + private static final String NET_ID_EPPN_HEADER = "eppn"; + private static final String NET_ID_PERSISTENT_ID = "persistent-id"; private static final String NET_ID_TEST_EPERSON = "123456789"; private static final String IDP_TEST_EPERSON = "Test Idp"; + private static final String KNIHOVNA_KUN_TEST_ZLUTOUCKY = "knihovna Kůň test Žluťoučký"; + private EPersonRest ePersonRest; private final String feature = CanChangePasswordFeature.NAME; @@ -183,10 +189,7 @@ public void userFillInEmailAndShouldBeRegisteredByVerificationToken() throws Exc .andExpect(status().isOk()); // Check if was created a user with such email and netid. - EPerson ePerson = ePersonService.findByNetid(context, Util.formatNetId(netId, idp)); - assertTrue(Objects.nonNull(ePerson)); - assertEquals(ePerson.getEmail(), email); - assertEquals(ePerson.getNetid(), Util.formatNetId(netId, idp)); + EPerson ePerson = checkUserWasCreated(netId, idp, email, null); // The user is registered now log him getClient().perform(post("/api/authn/shibboleth") @@ -207,7 +210,7 @@ public void userFillInEmailAndShouldBeRegisteredByVerificationToken() throws Exc .andExpect(status().isFound()); // Delete created eperson - clean after the test - EPersonBuilder.deleteEPerson(ePerson.getID()); + deleteShibbolethUser(ePerson); } @Test @@ -226,11 +229,7 @@ public void testShouldReturnDuplicateUserError() throws Exception { .andExpect(redirectedUrl("http://localhost:4000")) .andReturn().getResponse().getHeader("Authorization"); - - getClient(token).perform(get("/api/authn/status")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authenticated", is(true))) - .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))); + checkUserIsSignedIn(token); // Check if was created a user with such email and netid. EPerson ePerson = ePersonService.findByNetid(context, Util.formatNetId(netId, IDP_TEST_EPERSON)); @@ -252,6 +251,36 @@ public void testShouldReturnDuplicateUserError() throws Exception { EPersonBuilder.deleteEPerson(ePerson.getID()); } + // Login with email without netid, but the user with such email already exists and it has assigned netid. + @Test + public void testShouldReturnDuplicateUserErrorLoginWithoutNetId() throws Exception { + String email = "test@email.sk"; + String netId = email; + + // login through shibboleth + String token = getClient().perform(get("/api/authn/shibboleth") + .header("SHIB-MAIL", email) + .header("SHIB-NETID", netId) + .header("Shib-Identity-Provider", IDP_TEST_EPERSON)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000")) + .andReturn().getResponse().getHeader("Authorization"); + + checkUserIsSignedIn(token); + + // Should not login because the user with such email already exists + getClient().perform(get("/api/authn/shibboleth") + .header("SHIB-MAIL", email) + .header("Shib-Identity-Provider", IDP_TEST_EPERSON)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000/login/duplicate-user?email=" + email)) + .andReturn().getResponse().getHeader("Authorization"); + + // Check if was created a user with such email and netid. + EPerson ePerson = checkUserWasCreated(netId, IDP_TEST_EPERSON, email, null); + deleteShibbolethUser(ePerson); + } + // This test is copied from the `ShibbolethLoginFilterIT` and modified following the Clarin updates. @Test public void testRedirectToGivenTrustedUrl() throws Exception { @@ -264,10 +293,7 @@ public void testRedirectToGivenTrustedUrl() throws Exception { .andExpect(redirectedUrl("http://localhost:8080/server/api/authn/status")) .andReturn().getResponse().getHeader("Authorization"); - getClient(token).perform(get("/api/authn/status")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authenticated", is(true))) - .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))); + checkUserIsSignedIn(token); getClient(token).perform( get("/api/authz/authorizations/search/object") @@ -299,11 +325,7 @@ public void patchPassword() throws Exception { .andExpect(redirectedUrl("http://localhost:4000")) .andReturn().getResponse().getHeader("Authorization"); - - getClient(token).perform(get("/api/authn/status")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authenticated", is(true))) - .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))); + checkUserIsSignedIn(token); // updates password getClient(token).perform(patch("/api/eperson/epersons/" + clarinEperson.getID()) @@ -328,11 +350,7 @@ public void testRedirectToDefaultDspaceUrl() throws Exception { .andExpect(redirectedUrl("http://localhost:4000")) .andReturn().getResponse().getHeader("Authorization"); - - getClient(token).perform(get("/api/authn/status")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authenticated", is(true))) - .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))); + checkUserIsSignedIn(token); getClient(token).perform( get("/api/authz/authorizations/search/object") @@ -463,28 +481,10 @@ public void testISOShibHeaders() throws Exception { .andExpect(redirectedUrl("http://localhost:4000")) .andReturn().getResponse().getHeader("Authorization"); - - getClient(token).perform(get("/api/authn/status")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authenticated", is(true))) - .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))); - + checkUserIsSignedIn(token); // Check if was created a user with such email and netid. - EPerson ePerson = ePersonService.findByNetid(context, Util.formatNetId(testNetId, testIdp)); - assertTrue(Objects.nonNull(ePerson)); - assertEquals(ePerson.getEmail(), testMail); - assertEquals(ePerson.getFirstName(), "knihovna Kůň test Žluťoučký"); - - EPersonBuilder.deleteEPerson(ePerson.getID()); - - getClient(token).perform( - get("/api/authz/authorizations/search/object") - .param("embed", "feature") - .param("feature", feature) - .param("uri", utils.linkToSingleResource(ePersonRest, "self").getHref())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.page.totalElements", is(0))) - .andExpect(jsonPath("$._embedded").doesNotExist()); + EPerson ePerson = checkUserWasCreated(testNetId, testIdp, testMail, KNIHOVNA_KUN_TEST_ZLUTOUCKY); + deleteShibbolethUser(ePerson); } @Test @@ -500,25 +500,144 @@ public void testUTF8ShibHeaders() throws Exception { .header("SHIB-MAIL", testMail) .header("Shib-Identity-Provider", testIdp) .header("SHIB-NETID", testNetId) - .header("SHIB-GIVENNAME", "knihovna Kůň test Žluťoučký")) + .header("SHIB-GIVENNAME", KNIHOVNA_KUN_TEST_ZLUTOUCKY)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000")) + .andReturn().getResponse().getHeader("Authorization"); + + checkUserIsSignedIn(token); + // Check if was created a user with such email and netid. + EPerson ePerson = checkUserWasCreated(testNetId, testIdp, testMail, KNIHOVNA_KUN_TEST_ZLUTOUCKY); + deleteShibbolethUser(ePerson); + } + + @Test + public void testRedirectToMissingHeadersWithRedirectUrlParam() throws Exception { + String expectedMissingHeadersUrl = configurationService.getProperty("dspace.ui.url") + "/login/missing-headers"; + + getClient().perform(get("/api/authn/shibboleth") + .param("redirectUrl", "http://localhost:8080/server/api/authn/status") + .header("SHIB-MAIL", clarinEperson.getEmail()) + .header("SHIB-NETID", NET_ID_TEST_EPERSON)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl(expectedMissingHeadersUrl)); + } + + // eppn is set + @Test + public void testSuccessFullLoginEppnNetId() throws Exception { + String token = getClient().perform(get("/api/authn/shibboleth") + .header("Shib-Identity-Provider", IDP_TEST_EPERSON) + .header("SHIB-MAIL", clarinEperson.getEmail()) + .header(NET_ID_EPPN_HEADER, NET_ID_TEST_EPERSON)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000")) + .andReturn().getResponse().getHeader("Authorization"); + + checkUserIsSignedIn(token); + + EPerson ePerson = checkUserWasCreated(NET_ID_TEST_EPERSON, IDP_TEST_EPERSON, clarinEperson.getEmail(), null); + deleteShibbolethUser(ePerson); + } + + // persistent-id is set + @Test + public void testSuccessFullLoginPersistentIdNetId() throws Exception { + String token = getClient().perform(get("/api/authn/shibboleth") + .header("Shib-Identity-Provider", IDP_TEST_EPERSON) + .header("SHIB-MAIL", clarinEperson.getEmail()) + .header(NET_ID_PERSISTENT_ID, NET_ID_TEST_EPERSON)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000")) + .andReturn().getResponse().getHeader("Authorization"); + + checkUserIsSignedIn(token); + EPerson ePerson = checkUserWasCreated(NET_ID_TEST_EPERSON, IDP_TEST_EPERSON, clarinEperson.getEmail(), null); + deleteShibbolethUser(ePerson); + } + + @Test + public void testSuccessFullLoginWithTwoEmails() throws Exception { + String firstEmail = "efg@test.edu"; + String secondEmail = "abc@test.edu"; + String token = getClient().perform(get("/api/authn/shibboleth") + .header("Shib-Identity-Provider", IDP_TEST_EPERSON) + .header("SHIB-MAIL", firstEmail + ";" + secondEmail)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000")) + .andReturn().getResponse().getHeader("Authorization"); + + checkUserIsSignedIn(token); + // Find the user by the second email + EPerson ePerson = checkUserWasCreated(null, IDP_TEST_EPERSON, secondEmail, null); + assertTrue(Objects.nonNull(ePerson)); + deleteShibbolethUser(ePerson); + } + + // The user has changed the email. But that email is already used by another user. + @Test + public void testDuplicateEmailError() throws Exception { + String userWithEppnEmail = "user@eppn.sk"; + String customEppn = "custom eppn"; + + // Create a user with netid and email + String tokenEppnUser = getClient().perform(get("/api/authn/shibboleth") + .header("Shib-Identity-Provider", IDP_TEST_EPERSON) + .header(NET_ID_PERSISTENT_ID, customEppn) + .header("SHIB-MAIL", userWithEppnEmail)) .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost:4000")) .andReturn().getResponse().getHeader("Authorization"); + checkUserIsSignedIn(tokenEppnUser); + + // Try to update an email of existing user - the email is already used by another user - the user should be + // redirected to the login page + getClient().perform(get("/api/authn/shibboleth") + .header("Shib-Identity-Provider", IDP_TEST_EPERSON) + .header(NET_ID_PERSISTENT_ID, NET_ID_TEST_EPERSON) + .header("SHIB-MAIL", userWithEppnEmail)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost:4000/login/duplicate-user?email=" + userWithEppnEmail)); + + // Check if was created a user with such email and netid. + EPerson ePerson = checkUserWasCreated(customEppn, IDP_TEST_EPERSON, userWithEppnEmail, null); + // Delete created eperson - clean after the test + deleteShibbolethUser(ePerson); + } + + private EPerson checkUserWasCreated(String netIdValue, String idpValue, String email, String name) + throws SQLException { + // Check if was created a user with such email and netid. + EPerson ePerson = null; + if (netIdValue != null) { + ePerson = ePersonService.findByNetid(context, Util.formatNetId(netIdValue, idpValue)); + } else { + ePerson = ePersonService.findByEmail(context, email); + } + assertTrue(Objects.nonNull(ePerson)); + if (email != null) { + assertEquals(ePerson.getEmail(), email); + } + + if (name != null) { + assertEquals(ePerson.getFirstName(), name); + } + return ePerson; + } + private void checkUserIsSignedIn(String token) throws Exception { getClient(token).perform(get("/api/authn/status")) .andExpect(status().isOk()) .andExpect(jsonPath("$.authenticated", is(true))) .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))); + } - // Check if was created a user with such email and netid. - EPerson ePerson = ePersonService.findByNetid(context, Util.formatNetId(testNetId, testIdp)); - assertTrue(Objects.nonNull(ePerson)); - assertEquals(ePerson.getEmail(), testMail); - assertEquals(ePerson.getFirstName(), "knihovna Kůň test Žluťoučký"); + private void deleteShibbolethUser(EPerson ePerson) throws Exception { EPersonBuilder.deleteEPerson(ePerson.getID()); + // Check it was correctly deleted getClient(token).perform( get("/api/authz/authorizations/search/object") .param("embed", "feature") @@ -528,16 +647,4 @@ public void testUTF8ShibHeaders() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(0))) .andExpect(jsonPath("$._embedded").doesNotExist()); } - - @Test - public void testRedirectToMissingHeadersWithRedirectUrlParam() throws Exception { - String expectedMissingHeadersUrl = configurationService.getProperty("dspace.ui.url") + "/login/missing-headers"; - - getClient().perform(get("/api/authn/shibboleth") - .param("redirectUrl", "http://localhost:8080/server/api/authn/status") - .header("SHIB-MAIL", clarinEperson.getEmail()) - .header("SHIB-NETID", NET_ID_TEST_EPERSON)) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl(expectedMissingHeadersUrl)); - } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java index 6d1d242cad7f..a65357f97bfe 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java @@ -40,15 +40,12 @@ import org.dspace.content.RelationshipType; import org.dspace.content.WorkspaceItem; import org.dspace.content.authority.Choices; -import org.dspace.content.authority.service.ChoiceAuthorityService; -import org.dspace.content.authority.service.MetadataAuthorityService; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.util.SimpleMapConverter; import org.hamcrest.Matchers; import org.junit.Before; @@ -67,12 +64,6 @@ public class LinksetRestControllerIT extends AbstractControllerIntegrationTest { @Autowired private ConfigurationService configurationService; - @Autowired - private MetadataAuthorityService metadataAuthorityService; - - @Autowired - private ChoiceAuthorityService choiceAuthorityService; - @Autowired private ItemService itemService; @@ -735,10 +726,6 @@ public void findTypedLinkForBitstream() throws Exception { .andExpect(jsonPath("$[?(@.href == '" + uiUrl + "/signposting/linksets/" + item.getID() + "/json" + "' && @.rel == 'linkset' " + "&& @.type == 'application/linkset+json')]").exists()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -780,10 +767,6 @@ public void findTypedLinkForBitstreamWithType() throws Exception { "&& @.type == 'application/linkset+json')]").exists()) .andExpect(jsonPath("$[?(@.href == 'https://schema.org/ScholarlyArticle' " + "&& @.rel == 'type')]").exists()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -813,10 +796,6 @@ public void findTypedLinkForRestrictedBitstream() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -844,10 +823,6 @@ public void findTypedLinkForBitstreamUnderEmbargo() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -874,10 +849,6 @@ public void findTypedLinkForBitstreamOfWorkspaceItem() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -890,10 +861,6 @@ public void findTypedLinkForUnDiscoverableItem() throws Exception { getClient().perform(get("/signposting/links/" + item.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test diff --git a/dspace/bin/make-handle-config b/dspace/bin/make-handle-config index 127034ee5ddb..f4194e66d09e 100755 --- a/dspace/bin/make-handle-config +++ b/dspace/bin/make-handle-config @@ -38,7 +38,7 @@ echo "" >>$tempfile # Dual-stack (IPv4 and IPv6)? (default=n) echo $dshostip >>$tempfile # IP address echo "" >>$tempfile # DBind address (default=same as IP) echo "" >>$tempfile # Port to listen to (default=2641) -echo "" >>$tempfile # Port for HTTP i/f to listen to (default=8000) +echo "8010" >>$tempfile # Port for HTTP i/f to listen to (default=8000) echo "n" >>$tempfile # Log all accesses? ("y" or "n", default=y) echo "" >>$tempfile # Version/serial no. of site (default=1) echo $dsname Handle Server >>$tempfile # Server description diff --git a/dspace/bin/start-handle-server b/dspace/bin/start-handle-server index 6d5c9a4b4406..06c4fb5d90c9 100755 --- a/dspace/bin/start-handle-server +++ b/dspace/bin/start-handle-server @@ -23,9 +23,9 @@ HANDLEDIR=`$BINDIR/dspace dsprop --property handle.dir` LOGDIR=$DSPACEDIR/log #Allow user to specify java options through JAVA_OPTS variable -if [ "$JAVA_OPTS" = "" ]; then +if [ "$JAVA_OPTS_HANDLE" = "" ]; then #Default Java to use 256MB of memory - JAVA_OPTS=-Xmx256m + $JAVA_OPTS_HANDLE=-Xmx256m fi # Remove lock file, in case the old Handle server did not shut down properly @@ -34,7 +34,7 @@ rm -f $HANDLEDIR/txns/lock # Start the Handle server, with a special log4j properties file. # We cannot simply write to the same logs, since log4j # does not support more than one JVM writing to the same rolling log. -nohup java $JAVA_OPTS -classpath `$BINDIR/dspace classpath` \ +nohup java $JAVA_OPTS_HANDLE -classpath `$BINDIR/dspace classpath` \ -Ddspace.log.init.disable=true \ -Dlog4j.configuration=log4j-handle-plugin.properties \ net.handle.server.Main $HANDLEDIR \ diff --git a/dspace/config/clarin-dspace.cfg b/dspace/config/clarin-dspace.cfg index a3002aea7ade..6c00f45b5a1e 100644 --- a/dspace/config/clarin-dspace.cfg +++ b/dspace/config/clarin-dspace.cfg @@ -169,7 +169,7 @@ statistics.cache-server.uri = http://cache-server.none # citace.pro.allowed = true #google config -# google.analytics.key = +google.analytics.key = # The max number of events held in the GA buffer (default: 256) # google.analytics.buffer.limit = 256 @@ -245,7 +245,7 @@ shibboleth.name.conversion.outputEncoding = UTF-8 ### File preview ### # File preview is enabled by default -file.preview.enabled = false +file.preview.enabled = true # It the ZIP file contains more than 1000 files show only the first 1000 files file.preview.zip.limit.length = 1000 @@ -282,4 +282,9 @@ download.all.limit.max.file.size = 1073741824 # minimum total size of files for enabling download alert: download.all.alert.min.file.size = 10485760 # used in elg crosswalk exposing download locations -elg.download-location.exposed = 0 \ No newline at end of file +elg.download-location.exposed = 0 + +# this was used in the past, but info.recipient no longer exists +# left here for reference +#download.email.cc = ${info.recipient} +download.email.cc = ${mail.admin} diff --git a/dspace/config/crosswalks/oai/metadataFormats/metasharev2.xsl b/dspace/config/crosswalks/oai/metadataFormats/metasharev2.xsl index e716844112e6..9ec5f893d21e 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/metasharev2.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/metasharev2.xsl @@ -236,7 +236,7 @@ - + @@ -285,10 +285,12 @@ - + + - + + diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 971757f34794..759983b45535 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -932,6 +932,7 @@ registry.metadata.load = iiif-types.xml # This property cannot be added in the `clarin-dspace.cfg` because then some Unit tests are failing.. registry.metadata.load = metashare-schema.xml registry.metadata.load = edm.xml +registry.metadata.load = datacite.xml #---------------------------------------------------------------# #-----------------UI-Related CONFIGURATIONS---------------------# @@ -1552,7 +1553,8 @@ request.item.reject.email = true #------------------SUBMISSION CONFIGURATION------------------------# #------------------------------------------------------------------# # Field to use for type binding, default dc.type -submit.type-bind.field = dc.type +# It could be in the form of schema.element.qualifier=>metadata_field, or just metadata_field +submit.type-bind.field = dc.type,dc.language.iso=>edm.type #---------------------------------------------------------------# #----------SOLR DATABASE RESYNC SCRIPT CONFIGURATION------------# diff --git a/dspace/config/emails/clarin_autoregistration b/dspace/config/emails/clarin_autoregistration index 01727722b7e0..ba5b17cc8327 100644 --- a/dspace/config/emails/clarin_autoregistration +++ b/dspace/config/emails/clarin_autoregistration @@ -10,7 +10,7 @@ ## See org.dspace.core.Email for information on the format of this file. ## #set($subject = 'Account Registration') -To complete registration for a ${params[3]} repository account at {params[5]}, please click the link below: +To complete registration for a ${params[3]} repository account at ${params[5]}, please click the link below: ${params[0]} diff --git a/dspace/config/emails/clarin_download_link_admin b/dspace/config/emails/clarin_download_link_admin new file mode 100644 index 000000000000..ecb909bf5d31 --- /dev/null +++ b/dspace/config/emails/clarin_download_link_admin @@ -0,0 +1,35 @@ +## E-mail to admin about a download request +## +## Parameters: {0} is expanded to filename +## {1} to a download URL +## {2} to license url +## {3} user name +## {4} user email +## {5} extra metadata provided +## +## See org.dspace.core.Email for information on the format of this file. +## +#set($subject = "${config.get('dspace.name.short')}: New File Download Request (CC)") + +This is an information for administrators and other configured people about a new download request. + +User Details: + Name: ${params[3]} + Email: ${params[4]} + +Link of the requested file (does not contain the download token): + ${params[1]} + +The file is distributed under specific license: + ${params[2]} + +Extra information filled by the user: + ${params[5]} + +${config.get('dspace.name.short')} Team + +_____________________________________ +${config.get('dspace.name')}, +WWW: ${config.get('dspace.url')} +Email: ${config.get('help.mail')} +Tel.: ${config.get('help.phone')} diff --git a/dspace/config/hibernate.cfg.xml b/dspace/config/hibernate.cfg.xml index eee136f64146..82e4fd738038 100644 --- a/dspace/config/hibernate.cfg.xml +++ b/dspace/config/hibernate.cfg.xml @@ -49,6 +49,7 @@ + diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index 1c193b001527..cd6d3b66f346 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -104,6 +104,11 @@ org.dspace.app.rest.submit.step.ClarinNoticeStep clarin-notice + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + @@ -275,6 +280,11 @@ org.dspace.app.rest.submit.step.DescribeStep submission-form + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + @@ -323,6 +333,7 @@ + + + @@ -41,6 +45,7 @@ + diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index 25ec8554ebc6..1a63054ec9d2 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -1776,6 +1776,9 @@ Please give us a description + dc @@ -1783,7 +1786,7 @@ iso true - TEXT + TEXT autocomplete Select the language of the main content of the item. Multiple languages are possible. Start typing the language and use autocomplete form that will appear if applicable. Better to list all the languages then to use the 'mul' iso code (if there are too many, contact support). @@ -1798,7 +1801,7 @@ iso true - VIDEO,IMAGE,SOUND,3D + VIDEO,IMAGE,SOUND,3D autocomplete Optionally, select the language of the main content of the item. Multiple languages are possible. Start @@ -2023,7 +2026,9 @@ false + dropdown + teachingMaterials This is here to autofill a value. The value should not be changed. Please select a resource type for your submission. @@ -2089,6 +2094,52 @@ +
+ + + local + submission + note + false + + textarea + Leave a note for the editors/reviewers + + + + + + + + dc + relation + replaces + true + + onebox + URL to a related resource that is supplanted, displaced, or superseded by the described resource. If the replaced resource is in this repository start typing its name or handle and select the resource from the autocomplete popup. + + + + + + + + dc + relation + isreplacedby + true + + onebox + A related resource that supplants, displaces, or supersedes the described resource. + + + policy=deny,action=read,grantee-type=user,grantee-id=* + + + +
+ diff --git a/dspace/config/submission-forms_cs.xml b/dspace/config/submission-forms_cs.xml index e7f7f31cf8d4..bd06c7fdeac4 100644 --- a/dspace/config/submission-forms_cs.xml +++ b/dspace/config/submission-forms_cs.xml @@ -111,13 +111,40 @@ dc - title - alternative + source + uri + false + + onebox + Uveďte URL projektu + + http.* + + + + + local + demo + uri + false + + onebox + URL se vzorky dat, v případě nástrojů předvedení výstupu. + + http.* + + + + + dc + relation + isreferencedby true onebox If the item has any alternative titles, please enter them here. + http.* @@ -207,12 +234,124 @@ iso false + corpus,lexicalConceptualResource,languageDescription dropdown Select the language of the main content of the item. If the language does not appear in the list, please select 'Other'. If the content does not really have a language (for example, if it is a dataset or an image) please select 'N/A'. - + Please choose a language for the resource. + + + + + dc + language + iso + true + + toolService + onebox + If the tool/service is language dependent, select the appropriate language(s). Otherwise leave the field empty. Multiple languages are possible. Start typing the language and use autocomplete form that will appear. + + + + + + local + size + info + true + + corpus,languageDescription,lexicalConceptualResource + complex + You can state the extent of the submitted data, eg. the number of tokens. + + + + + metashare + ResourceInfo#ContentInfo + mediaType + false + + corpus,lexicalConceptualResource + dropdown + Media type of the main content of the item e.g., "text" for + textual corpora or "audio" for audio recordings. + + Media type is required + + + + + metashare + ResourceInfo#ContentInfo + mediaType + false + + languageDescription + dropdown + Media type of the main content of the item e.g., "text" for + textual corpora or "audio" for audio recordings. + + Media type is required + + + + + + + metashare + ResourceInfo#ContentInfo + detailedType + false + + toolService + dropdown + + Choose one of the types + + + + + metashare + ResourceInfo#ContentInfo + detailedType + false + + languageDescription + dropdown + + Choose one of the types + + + + + metashare + ResourceInfo#ContentInfo + detailedType + false + + lexicalConceptualResource + dropdown + + Choose one of the types + + + + + metashare + ResourceInfo#ResourceComponentType#ToolServiceInfo + languageDependent + false + + toolService + list + Indicate whether the operation of the tool or service is + language dependent or not + + Please indicate whether the tool is language dependent @@ -254,10 +393,10 @@ onebox - The actual maximum upload size of the file is 4GB. To upload the file bigger than maximum - upload size type the URL of that big file. Admin must know URL to that bitstream file. - Then click on the 'Save' button and the file will start to upload. The file will be loaded - from the '/temp' folder of the server. Example: /tomcat/temp/bitstream.png + The actual maximum upload size of the file is 4GB. To upload a file bigger than the + maximum upload size, enter the URL of that large file. The admin must know the URL + of that bitstream file. Then, click on the 'Save' button, and the file will start + to upload. The file path must be an absolute path. @@ -279,7 +418,11 @@ true tag - Enter appropriate subject keywords or phrases. + Enter appropriate subject keyword or phrase and press the Add button. You can repeat it for + multiple keywords or use separators i.e., Enter and comma, which will split it accordingly. + Start typing the keyword and use autocomplete form that will appear. End your input by pressing + ESC if you don't want to use the preselected value. + Please enter at least one subject related to your submission srsc @@ -1432,674 +1575,1631 @@ - - - - - - - - - - - - - - - - - - - - Yes - true - - - - - ISSN - issn - - - Other - other - - - ISMN - ismn - - - Gov't Doc # - govdoc - - - URI - uri - - - ISBN - isbn - - - - - - - N/A - N/A - - - EU - euFunds - - - Own funds - ownFunds - - - National - nationalFunds - - - other - Other - - - - - - Animation - Animation - - - Article - Article - - - Book - Book - - - Book chapter - Book chapter - - - Dataset - Dataset - - - Learning Object - Learning Object - - - Image - Image - - - Image, 3-D - Image, 3-D - - - Map - Map - - - Musical Score - Musical Score - - - Plan or blueprint - Plan or blueprint - - - Preprint - Preprint - - - Presentation - Presentation - - - Recording, acoustical - Recording, acoustical - - - Recording, musical - Recording, musical - - - Recording, oral - Recording, oral - - - Software - Software - - - Technical Report - Technical Report - - - Thesis - Thesis - - - Video - Video - - - Working Paper - Working Paper - - - Other - Other - - - - - - - N/A - - - - English (United States) - en_US - - - English - en - - - Spanish - es - - - German - de - - - French - fr - - - Italian - it - - - Japanese - ja - - - Chinese - zh - - - Portuguese - pt - - - Turkish - tr - - - (Other) - other - - - +
+ + + edm + type + + false + + dropdown + Choose one of TEXT, VIDEO, SOUND, IMAGE, 3D. If choosing + TEXT consider adding the resource among other Language Resources. Images are visual resources for users to + look at. Text materials are meant to be read and not looked at. + Please select one of the options. + + + + + dc + title + + false + + onebox + Enter the main title of the item in English. + You must enter a main title for this item. + + + + + local + demo + uri + false + + onebox + A url with samples of the resource or, in the case of tools, + of samples of the output. + + + http.* + + + + + dc + relation + isreferencedby + true + + onebox + Link to original paper that references this dataset. + + http.* + + + + + dc + date + issued + false + + date + Please give the date when the submission data were issued if any e.g., 2014-01-21 or at least + the year. + + You must enter the date or at least the year in a valid format. + + + + + local + hidden + + false + + list + Indicate whether you want to hide this item from browse and search. Combine with "Upload cmdi" + for weblicht submissions. + + + + policy=deny,action=read,grantee-type=user,grantee-id=* + + + + + + local + hasMetadata + + false + + list + + Indicate whether you will upload cmdi file in the next step. Combine with "hide" for weblicht + submissions. + + + + policy=deny,action=read,grantee-type=user,grantee-id=* + + + +
+
+ + + dc + contributor + author + true + + clarin-name + Enter the names of the authors of this item. Start typing the author's last name and use + autocomplete form that will appear if applicable. End your input by pressing ESC if you don't + want to use the preselected value. + + Please add author(s) + + + + + dc + publisher + + true + + autocomplete + The name of the publisher of the original analog or born + digital object. Use your home institution if this is a born digital object being published now. Start typing the + publisher and use autocomplete form that will appear if applicable. End your input by pressing ESC if you + don't want to use the preselected value. + + You must enter the name of the publisher. + + + + + local + dataProvider + + false + + autocomplete + This concerns the digital object (not the analog + original). An institution from which the data come. Used eg. to give proper attribution. Generally + different from publisher. + + + + + + local + contact + person + true + + complex + Person to contact in case of any issues with this submission. + Please fill all the fields for the contact person. + + + + + local + sponsor + true + + complex + Acknowledge sponsors and funding that supported work described by this submission. + + +
+
+ + + dc + type + + false + + autocomplete + The type should be different from what you have + entered in the first step. Examples: photo or painting for IMAGE, book or letter for TEXT, etc. + Type is required + + + + + + dc + description + + false + + aaa + textarea + Enter a description of the submitted data. + Please give us a description + + + + + dc + language + iso + true + + TEXT + autocomplete + Select the language of the main content of the item. Multiple languages are possible. Start + typing the language and use autocomplete form that will appear if applicable. Better to list all the languages then to use the 'mul' iso code (if there are too many, contact support). + + The language is required for TEXT resources + + + + + dc + language + iso + true + + VIDEO,IMAGE,SOUND,3D + autocomplete + Optionally, select the language of the main content + of the item. Multiple languages are possible. Start + typing the language and use autocomplete form that will appear if applicable. Better to list all the languages then to use the 'mul' iso code (if there are too many, contact support). + + + + + + + dc + subject + + + true + + tag + Enter appropriate subject keyword or phrase and press + the Add button. Use keywords to specify also people, places and times (period, era, date range etc) the resource + is about. You can use hierarchical subjects, separate the hierarchy levels with two colons (::). Eg. + People::John Doe, Places::New York, Times::WWII. + You can repeat it for multiple keywords or use separators i.e., comma and semicolon, which will split it accordingly. + Start typing the keyword and use autocomplete form that will appear. End your input by pressing + ESC if you don't want to use the preselected value. + + Please enter at least one subject related to your submission + + + + + dc + identifier + other + true + + + onebox + The item will get a handle. If the item has any + identification numbers or codes associated with it, please enter the types and the actual numbers or codes. + + + + + + + local + size + info + true + + complex + You can state the extent of the submitted data, eg. the number of tokens. + + +
+ +
+ + + + + dc + title + + false + + onebox + Enter the main title of the item in English. + You must enter a main title for this item. + + + + + local + demo + uri + false + + onebox + Course homepage + + http.* + + + + + dc + relation + isreferencedby + true + + onebox + Link to original paper that references this dataset. + + http.* + + + + + dc + date + issued + false + + date + Please give the date when the submission data were issued if any e.g., 2014-01-21 or at least + the year. + + You must enter the date or at least the year in a valid format. + + + + + local + hidden + + false + + list + Indicate whether you want to hide this item from browse and search. Combine with "Upload cmdi" + for weblicht submissions. + + + + policy=deny,action=read,grantee-type=user,grantee-id=* + + + + + + local + hasMetadata + + false + + list + + Indicate whether you will upload cmdi file in the next step. Combine with "hide" for weblicht + submissions. + + + + policy=deny,action=read,grantee-type=user,grantee-id=* + + + +
+ +
+ + + + dc + contributor + author + true + + clarin-name + Enter the names of the authors of this item. Start typing the author's last name and use + autocomplete form that will appear if applicable. End your input by pressing ESC if you don't + want to use the preselected value. + + Please add author(s) + + + + + dc + publisher + + true + + autocomplete + The name of the publisher of the original analog or born + digital object. Use your home institution if this is a born digital object being published now. Start typing the + publisher and use autocomplete form that will appear if applicable. End your input by pressing ESC if you + don't want to use the preselected value. + + You must enter the name of the publisher. + + + + + local + contact + person + true + + complex + Person to contact in case of any issues with this submission. + Please fill all the fields for the contact person. + + + + + local + sponsor + true + + complex + Acknowledge sponsors and funding that supported work described by this submission. + + +
+ +
+ + + dc + type + + false + + + dropdown + teachingMaterials + This is here to autofill a value. The value should not be changed. + Please select a resource type for your submission. + + + + + dc + description + + false + + textarea + Enter a description of the submitted data. + Please give us a description + + + + + dc + language + iso + true + + autocomplete + Select the language of the main content of the item. Multiple languages are possible. Start + typing the language and use autocomplete form that will appear if applicable. Better to list all the languages then to use the 'mul' iso code (if there are too many, contact support). + + Please choose a language for the resource. + + + + + dc + subject + + + true + + autocomplete + Enter appropriate subject keyword or phrase and press the Add button. You can repeat it for + multiple keywords or use separators i.e., comma and semicolon, which will split it accordingly. + Start typing the keyword and use autocomplete form that will appear. End your input by pressing + ESC if you don't want to use the preselected value. + + Please enter at least one subject related to your submission + + + + + dc + identifier + other + true + + + onebox + The item will get a handle. If the item has any + identification numbers or codes associated with it, please enter the types and the actual numbers or codes. + + + + +
+ +
+ + + local + submission + note + false + + textarea + Zde můžete zanechat vzkaz editorům. + + + + + + + + dc + relation + replaces + true + + onebox + URL příbuzného záznamu, který je tímto záznamem nahrazen. Pokud je příbuzný záznam v tomto repozitáři, začněte psát jeho název, nebo handle a vyberte záznam z nabídky. + + + + + + + + dc + relation + isreplacedby + true + + onebox + Příbuzný záznam, který nahrazuje tento. + + + policy=deny,action=read,grantee-type=user,grantee-id=* + + + +
+ + + + + + + + + + + + + + + + + + + + + Yes + true + + + + + ISSN + issn + + + Other + other + + + ISMN + ismn + + + Gov't Doc # + govdoc + + + URI + uri + + + ISBN + isbn + + + + + + + N/A + N/A + + + EU + euFunds + + + Own funds + ownFunds + + + National + nationalFunds + + + other + Other + + + + + + N/A + + + + Corpus + corpus + + + Lexical conceptual + lexicalConceptualResource + + + Language description + languageDescription + + + Technology / Tool / Service + toolService + + + + + + + + + N/A + + + + English (United States) + en_US + + + English + en + + + Spanish + es + + + German + de + + + French + fr + + + Italian + it + + + Japanese + ja + + + Chinese + zh + + + Portuguese + pt + + + Turkish + tr + + + (Other) + other + + + - + + + + + + Without License + + + + Attribution (CC-BY) + http://creativecommons.org/licenses/by/4.0/ + + + Attribution, No Derivative Works (CC-BY-ND) + http://creativecommons.org/licenses/by-nd/4.0/ + + + Attribution, Share-alike (CC-BY-SA) + http://creativecommons.org/licenses/by-sa/4.0/ + + + Attribution, Non-commercial (CC-BY-NC) + http://creativecommons.org/licenses/by-nc/4.0/ + + + Attribution, Non-commercial, No Derivative Works (CC-BY-NC-ND) + http://creativecommons.org/licenses/by-nc-nd/4.0/ + + + Attribution, Non-commercial, Share-alike (CC-BY-NC-SA) + http://creativecommons.org/licenses/by-nc-sa/4.0/ + + + + Other + other + + + + + + + + Interactive Resource + interactive resource + + + - Website + website + + + Dataset + dataset + + + - Interview + interview + + + Image + image + + + - Moving Image + moving image + + + -- Video + video + + + - Still Image + still image + + + Other + other + + + Software + software + + + - Research Software + research software + + + Workflow + workflow + + + Cartographic Material + cartographic material + + + - Map + map + + + Sound + sound + + + - Musical Composition + musical composition + + + Text + text + + + - Annotation + annotation + + + - Bibliography + bibliography + + + - Book + book + + + -- Book Part + book part + + + - Conference Object + conference object + + + -- Conference Proceedings + conference proceedings + + + --- Conference Paper + conference paper + + + --- Conference Poster + conference poster + + + -- Conference Paper Not In Proceedings + conference paper not in proceedings + + + -- Conference Poster Not In Proceedings + conference poster not in proceedings + + + - Lecture + lecture + + + - Letter + letter + + + - Periodical + periodical + + + -- Journal + journal + + + --- Contribution to Journal + contribution to journal + + + ---- Journal Article + journal article + + + ----- Data Paper + data paper + + + ----- Review Article + review article + + + ----- Research Article + research article + + + ----- Corrigendum + corrigendum + + + ----- Software Paper + software paper + + + ---- Editorial + editorial + + + ---- Letter to the Editor + letter to the editor + + + -- Newspaper + newspaper + + + --- Newspaper Article + newspaper article + + + -- Magazine + magazine + + + - Patent + patent + + + - Preprint + preprint + + + - Report + report + + + -- Report Part + report part + + + -- Internal Report + internal report + + + -- Memorandum + memorandum + + + -- Other Type of Report + other type of report + + + -- Policy Report + policy report + + + -- Project Deliverable + project deliverable + + + --- Data Management Plan + data management plan + + + -- Report to Funding Agency + report to funding agency + + + -- Research Report + research report + + + -- Technical Report + technical report + + + - Research Proposal + research proposal + + + - Review + review + + + -- Book Review + book review + + + - Technical Documentation + technical documentation + + + - Working Paper + working paper + + + - Thesis + thesis + + + -- Bachelor Thesis + bachelor thesis + + + -- Doctoral Thesis + doctoral thesis + + + -- Master Thesis + master thesis + + + - Musical Notation + musical notation + + + - Blog Post + blog post + + + - Manuscript + website + + + Learning Object + learning object + + + Clinical Trial + clinical trial + + + Clinical Study + clinical study + + + + + + Author’s Original + http://purl.org/coar/version/c_b1a7d7d4d402bcce + + + Submitted Manuscript Under Review + http://purl.org/coar/version/c_71e4c1898caa6e32 + + + Accepted Manuscript + http://purl.org/coar/version/c_ab4af688f83e57aa + + + Proof + http://purl.org/coar/version/c_fa2ee174bc00049f + + + Version of Record + http://purl.org/coar/version/c_970fb48d4fbd8a85 + + + Corrected Version of Record + http://purl.org/coar/version/c_e19f295774971610 + + + Enhanced Version of Record + http://purl.org/coar/version/c_dc82b40f9837b551 + + + Not Applicable (or Unknown) + http://purl.org/coar/version/c_be7fb7dd8ff6fe43 + + - + + + open access + http://purl.org/coar/access_right/c_abf2 + + + embargoed access + http://purl.org/coar/access_right/c_f1cf + + + restricted access + http://purl.org/coar/access_right/c_16ec + + + metadata only access + http://purl.org/coar/access_right/c_14cb + + + - Without License - + Scopus Author ID + scopus-author-id - Attribution (CC-BY) - http://creativecommons.org/licenses/by/4.0/ + Ciencia ID + ciencia-id - Attribution, No Derivative Works (CC-BY-ND) - http://creativecommons.org/licenses/by-nd/4.0/ + Google Scholar ID + gsid - Attribution, Share-alike (CC-BY-SA) - http://creativecommons.org/licenses/by-sa/4.0/ + Open Researcher and Contributor ID (ORCID) + orcid - Attribution, Non-commercial (CC-BY-NC) - http://creativecommons.org/licenses/by-nc/4.0/ + Web of Science ResearcherID + rid - Attribution, Non-commercial, No Derivative Works (CC-BY-NC-ND) - http://creativecommons.org/licenses/by-nc-nd/4.0/ + ISNI - International Standard Name Identifier + isni - Attribution, Non-commercial, Share-alike (CC-BY-NC-SA) - http://creativecommons.org/licenses/by-nc-sa/4.0/ + Other + + + + + + + ISNI - International Standard Name Identifier + isni + + + Ringgold identifier + rin + + + Research Organization Registry + ror - Other - other + + + + + + + N/A + + + + Is a Funding Organization + FundingOrganization + + + + TEXT + TEXT + + + VIDEO + VIDEO + + + SOUND + SOUND + + + IMAGE + IMAGE + + + 3D + 3D + - - + + + + + + + N/A + + - Interactive Resource - interactive resource + Web Executable + webExecutable - - Website - website + Paper copy + paperCopy - Dataset - dataset + HardDisk + hardDisk - - Interview - interview + Blu Ray + bluRay - Image - image + DVD-R + DVD-R - - Moving Image - moving image + CD-ROM + CD-ROM - -- Video - video + Download + downloadable - - Still Image - still image + Accessible Through Interface + accessibleThroughInterface - Other + other other + + + + - Software - software + N/A + - - Research Software - research software + True + True - Workflow - workflow + False + False + + + + - Cartographic Material - cartographic material + N/A + - - Map - map + text + text - Sound - sound + audio + audio - - Musical Composition - musical composition + video + video - Text + image + image + + + + + + N/A + + + + text text - - Annotation - annotation + video + video - - Bibliography - bibliography + image + image + + + + - - Book - book + N/A + - -- Book Part - book part + tool + tool - - Conference Object - conference object + service + service - -- Conference Proceedings - conference proceedings + platform + platform - --- Conference Paper - conference paper + suiteOfTools + suiteOfTools - --- Conference Poster - conference poster + infrastructure + infrastructure - -- Conference Paper Not In Proceedings - conference paper not in proceedings + architecture + architecture - -- Conference Poster Not In Proceedings - conference poster not in proceedings + nlpDevelopmentEnvironment + nlpDevelopmentEnvironment - - Lecture - lecture + other + other + + - - Letter - letter + N/A + - - Periodical - periodical + wordList + wordList - -- Journal - journal + computationalLexicon + computationalLexicon - --- Contribution to Journal - contribution to journal + ontology + ontology - ---- Journal Article - journal article + wordnet + wordnet - ----- Data Paper - data paper + thesaurus + thesaurus - ----- Review Article - review article + framenet + framenet - ----- Research Article - research article + terminologicalResource + terminologicalResource - ----- Corrigendum - corrigendum + machineReadableDictionary + machineReadableDictionary - ----- Software Paper - software paper + lexicon + lexicon - ---- Editorial - editorial + other + other + + - ---- Letter to the Editor - letter to the editor + N/A + - -- Newspaper - newspaper + grammar + grammar - --- Newspaper Article - newspaper article + machine learning model + mlmodel - -- Magazine - magazine + n-gram model + ngrammodel - - Patent - patent + other + other + + + + - - Preprint - preprint + N/A + + - - Report - report + terms + terms - -- Report Part - report part + entries + entries - -- Internal Report - internal report + turns + turns - -- Memorandum - memorandum + utterances + utterances - -- Other Type of Report - other type of report + articles + articles - -- Policy Report - policy report + files + files - -- Project Deliverable - project deliverable + items + items - --- Data Management Plan - data management plan + seconds + seconds - -- Report to Funding Agency - report to funding agency + elements + elements - -- Research Report - research report + units + units - -- Technical Report - technical report + minutes + minutes - - Research Proposal - research proposal + hours + hours - - Review - review + texts + texts - -- Book Review - book review + sentences + sentences - - Technical Documentation - technical documentation + pages + pages - - Working Paper - working paper + bytes + bytes - - Thesis - thesis + tokens + tokens - -- Bachelor Thesis - bachelor thesis + words + words - -- Doctoral Thesis - doctoral thesis + keywords + keywords - -- Master Thesis - master thesis + idiomaticExpressions + idiomaticExpressions - - Musical Notation - musical notation + neologisms + neologisms - - Blog Post - blog post + multiWordUnits + multiWordUnits - - Manuscript - website + expressions + expressions - Learning Object - learning object + synsets + synsets - Clinical Trial - clinical trial + classes + classes - Clinical Study - clinical study + concepts + concepts - - - - Author’s Original - http://purl.org/coar/version/c_b1a7d7d4d402bcce + lexicalTypes + lexicalTypes - Submitted Manuscript Under Review - http://purl.org/coar/version/c_71e4c1898caa6e32 + phoneticUnits + phoneticUnits - Accepted Manuscript - http://purl.org/coar/version/c_ab4af688f83e57aa + syntacticUnits + syntacticUnits - Proof - http://purl.org/coar/version/c_fa2ee174bc00049f + semanticUnits + semanticUnits - Version of Record - http://purl.org/coar/version/c_970fb48d4fbd8a85 + predicates + predicates - Corrected Version of Record - http://purl.org/coar/version/c_e19f295774971610 + phonemes + phonemes - Enhanced Version of Record - http://purl.org/coar/version/c_dc82b40f9837b551 + diphones + diphones - Not Applicable (or Unknown) - http://purl.org/coar/version/c_be7fb7dd8ff6fe43 + T-HPairs + T-HPairs - - - - open access - http://purl.org/coar/access_right/c_abf2 + syllables + syllables - embargoed access - http://purl.org/coar/access_right/c_f1cf + frames + frames - restricted access - http://purl.org/coar/access_right/c_16ec + images + images - metadata only access - http://purl.org/coar/access_right/c_14cb + kb + kb - - - - Scopus Author ID - scopus-author-id + mb + mb - Ciencia ID - ciencia-id + gb + gb - Google Scholar ID - gsid + rb + rb - Open Researcher and Contributor ID (ORCID) - orcid + shots + shots - Web of Science ResearcherID - rid + unigrams + unigrams - ISNI - International Standard Name Identifier - isni + bigrams + bigrams - Other - + trigrams + trigrams - - - - ISNI - International Standard Name Identifier - isni + 4-grams + 4-grams - Ringgold identifier - rin + 5-grams + 5-grams - Research Organization Registry - ror + n-grams + n-grams - Other - + rules + rules + + + other + other - + - N/A - + Yes + true - Is a Funding Organization - FundingOrganization + No + false - Hidden @@ -2107,6 +3207,13 @@ + + + teachingMaterials + teachingMaterials + + + + 32 1000 ${solr.lock.type:native} - + false @@ -48,7 +50,7 @@ 10000 - ${solr.autoCommit.maxTime:900000} + ${solr.autoCommit.maxTime:10000} true @@ -62,14 +64,16 @@ ${solr.max.booleanClauses:1024} + unordered sets of *all* documents that match a + query. Caches results of 'fq' search param. --> - + 1000 - + - + uuid @@ -126,7 +132,8 @@ - + uid diff --git a/scripts/fast-build/config-update.bat b/scripts/fast-build/config-update.bat index 6f80ae31ce7d..b3bb9025e0dc 100644 --- a/scripts/fast-build/config-update.bat +++ b/scripts/fast-build/config-update.bat @@ -7,4 +7,4 @@ xcopy /e /h /i /q /y %dspace_source%\dspace\config\ %dspace_application%\config\ cd %dspace_source%\scripts\fast-build\ -call update-solr-configsets.bat +REM call update-solr-configsets.bat diff --git a/scripts/regenerate.bat b/scripts/regenerate.bat deleted file mode 100644 index 535474bdd978..000000000000 --- a/scripts/regenerate.bat +++ /dev/null @@ -1 +0,0 @@ -java -jar regenerate.jar diff --git a/scripts/regenerate.jar b/scripts/regenerate.jar deleted file mode 100644 index 1308ca5b7379..000000000000 Binary files a/scripts/regenerate.jar and /dev/null differ diff --git a/scripts/sourceversion.py b/scripts/sourceversion.py index 145f8d8ac8e8..b838d70708c8 100644 --- a/scripts/sourceversion.py +++ b/scripts/sourceversion.py @@ -1,10 +1,19 @@ import subprocess import sys -from datetime import datetime +from datetime import datetime, timezone + +def get_time_in_timezone(zone: str = "Europe/Bratislava"): + try: + from zoneinfo import ZoneInfo + my_tz = ZoneInfo(zone) + except Exception as e: + my_tz = timezone.utc + return datetime.now(my_tz) + if __name__ == '__main__': - ts = datetime.now() - print(f"This info was generated on: {ts}") + ts = get_time_in_timezone() + print(f"This info was generated on: {ts.strftime('%Y-%m-%d %H:%M:%S %Z%z')}") cmd = 'git log -1 --pretty=format:"Git hash: %H Date of commit: %ai"' subprocess.check_call(cmd, shell=True)