Merge remote-tracking branch 'upstream/main' into DS-626

This commit is contained in:
Yana De Pauw
2020-07-22 16:35:11 +02:00
541 changed files with 38721 additions and 4732 deletions

22
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug, needs triage
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is. Include the version(s) of DSpace where you've seen this problem. Link to examples if they are public.
**To Reproduce**
Steps to reproduce the behavior:
1. Do this
2. Then this...
**Expected behavior**
A clear and concise description of what you expected to happen.
**Related work**
Link to any related tickets or PRs here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest a new feature for this project
title: ''
labels: new feature, needs triage
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives or workarounds you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,26 @@
# This workflow runs whenever a new pull request is created
# TEMPORARILY DISABLED. Unfortunately this doesn't work for PRs created from forked repositories (which is how we tend to create PRs).
# There is no known workaround yet. See https://github.community/t/how-to-use-github-token-for-prs-from-forks/16818
name: Pull Request opened
# Only run for newly opened PRs against the "main" branch
on:
pull_request:
types: [opened]
branches:
- main
jobs:
automation:
runs-on: ubuntu-latest
steps:
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
# See https://github.com/marketplace/actions/pull-request-assigner
- name: Assign PR to creator
uses: thomaseizinger/assign-pr-creator-action@v1.0.0
# Note, this authentication token is created automatically
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Ignore errors. It is possible the PR was created by someone who cannot be assigned
continue-on-error: true

27
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,27 @@
## References
_Add references/links to any related tickets or PRs. These may include:_
* Link to [JIRA](https://jira.lyrasis.org/projects/DS/summary) ticket(s), if any
* Link to [REST Contract](https://github.com/DSpace/Rest7Contract) or an open REST Contract PR, if any
* Link to [Angular issue or PR](https://github.com/DSpace/dspace-angular/issues) related to this PR, if any
## Description
Short summary of changes (1-2 sentences).
## Instructions for Reviewers
Please add a more detailed description of the changes made by your PR. At a minimum, providing a bulleted list of changes in your PR is helpful to reviewers.
List of changes in this PR:
* First, ...
* Second, ...
**Include guidance for how to test or review your PR.** This may include: steps to reproduce a bug, screenshots or description of a new feature, or reasons behind specific changes.
## Checklist
_This checklist provides a reminder of what we are going to look for when reviewing your PR. You need not complete this checklist prior to creating your PR (draft PRs are always welcome). If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!_
- [ ] My PR is small in size (e.g. less than 1,000 lines of code, not including comments & integration tests). Exceptions may be made if previously agreed upon.
- [ ] My PR passes Checkstyle validation based on the [Code Style Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Style+Guide).
- [ ] My PR includes Javadoc for _all new (or modified) public methods and classes_. It also includes Javadoc for large or complex private methods.
- [ ] My PR passes all tests and includes new/updated Unit or Integration Tests based on the [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide).
- [ ] If my PR includes new, third-party dependencies (in any `pom.xml`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation.
- [ ] If my PR modifies the REST API, I've linked to the REST Contract page (or open PR) related to this change.

29
.github/workflows/issue_opened.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
# This workflow runs whenever a new issue is created
name: Issue opened
on:
issues:
types: [opened]
jobs:
automation:
runs-on: ubuntu-latest
steps:
# Add the new issue to a project board, if it needs triage
# See https://github.com/marketplace/actions/create-project-card-action
- name: Add issue to project 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: technote-space/create-project-card-action@v1
# Note, the authentication token below is an ORG level Secret.
# It must be created/recreated manually via a personal access token with "public_repo" and "admin:org" 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.ORG_PROJECT_TOKEN }}
PROJECT: DSpace Backlog
COLUMN: Triage
CHECK_ORG_PROJECT: true
# Ignore errors.
continue-on-error: true

9
.lgtm.yml Normal file
View File

@@ -0,0 +1,9 @@
# LGTM Settings (https://lgtm.com/)
# For reference, see https://lgtm.com/help/lgtm/lgtm.yml-configuration-file
# or template at https://lgtm.com/static/downloads/lgtm.template.yml
extraction:
java:
index:
# Specify the Java version required to build the project
java_version: 11

View File

@@ -1,5 +1,5 @@
# This image will be published as dspace/dspace
# See https://github.com/DSpace/DSpace/tree/master/dspace/src/main/docker for usage details
# See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details
#
# This version is JDK11 compatible
# - tomcat:8-jdk11

View File

@@ -1,5 +1,5 @@
# This image will be published as dspace/dspace-cli
# See https://github.com/DSpace/DSpace/tree/master/dspace/src/main/docker for usage details
# See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details
#
# This version is JDK11 compatible
# - openjdk:11

View File

@@ -1,5 +1,5 @@
# This image will be published as dspace/dspace
# See https://github.com/DSpace/DSpace/tree/master/dspace/src/main/docker for usage details
# See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details
#
# This version is JDK11 compatible
# - tomcat:8-jdk11

11
LICENSE
View File

@@ -1,7 +1,6 @@
DSpace source code license:
DSpace source code BSD License:
Copyright (c) 2002-2016, DuraSpace. All rights reserved.
Copyright (c) 2002-2020, LYRASIS. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -34,7 +33,7 @@ DAMAGE.
DSpace uses third-party libraries which may be distributed under
different licenses to the above. Information about these licenses
is detailed in the LICENSES_THIRD_PARTY file at the root of the source
tree. You must agree to the terms of these licenses, in addition to
different licenses to the above. Information about these licenses
is detailed in the LICENSES_THIRD_PARTY file at the root of the source
tree. You must agree to the terms of these licenses, in addition to
the above DSpace source code license, in order to use this software.

21
NOTICE
View File

@@ -1,15 +1,18 @@
Licensing Notices
=================
Licensing Notice
[July 2019] DuraSpace joined with LYRASIS (another 501(c)3 organization) in July 2019.
LYRASIS holds the copyrights of DuraSpace.
Fedora Commons joined with the DSpace Foundation and began operating under
[July 2009] Fedora Commons joined with the DSpace Foundation and began operating under
the new name DuraSpace in July 2009. DuraSpace holds the copyrights of
the DSpace Foundation, Inc.
The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007
with a mission to promote and advance the dspace platform enabling management,
access and preservation of digital works. The Foundation was able to transfer
the legal copyright from Hewlett-Packard Company (HP) and Massachusetts
Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many
of the files in the source code may contain a copyright statement stating HP
and MIT possess the copyright, in these instances please note that the copy
[July 2007] The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007
with a mission to promote and advance the dspace platform enabling management,
access and preservation of digital works. The Foundation was able to transfer
the legal copyright from Hewlett-Packard Company (HP) and Massachusetts
Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many
of the files in the source code may contain a copyright statement stating HP
and MIT possess the copyright, in these instances please note that the copy
right has transferred to the DSpace foundation, and subsequently to DuraSpace.

View File

@@ -1,24 +1,24 @@
# DSpace
[![Build Status](https://travis-ci.org/DSpace/DSpace.png?branch=master)](https://travis-ci.org/DSpace/DSpace)
[![Build Status](https://travis-ci.com/DSpace/DSpace.png?branch=main)](https://travis-ci.com/DSpace/DSpace)
[DSpace Documentation](https://wiki.duraspace.org/display/DSDOC/) |
[DSpace Documentation](https://wiki.lyrasis.org/display/DSDOC/) |
[DSpace Releases](https://github.com/DSpace/DSpace/releases) |
[DSpace Wiki](https://wiki.duraspace.org/display/DSPACE/Home) |
[Support](https://wiki.duraspace.org/display/DSPACE/Support)
[DSpace Wiki](https://wiki.lyrasis.org/display/DSPACE/Home) |
[Support](https://wiki.lyrasis.org/display/DSPACE/Support)
DSpace open source software is a turnkey repository application used by more than
2,000 organizations and institutions worldwide to provide durable access to digital resources.
For more information, visit http://www.dspace.org/
***
:warning: **Work on DSpace 7 has begun on our `master` branch.** This means that there is temporarily NO user interface on this `master` branch. DSpace 7 will feature a new, unified [Angular](https://angular.io/) user interface, along with an enhanced, rebuilt REST API. The latest status of this work can be found on the [DSpace 7 UI Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group) page. Additionally, the codebases can be found in the following places:
* DSpace 7 REST API work is occurring on the [`master` branch](https://github.com/DSpace/DSpace/tree/master/dspace-server-webapp) of this repository.
* The REST Contract is being documented at https://github.com/DSpace/Rest7Contract
:warning: **Work on DSpace 7 has begun on our `main` branch.** This means that there is NO user interface on this `main` branch. DSpace 7 will feature a new, unified [Angular](https://angular.io/) user interface, along with an enhanced, rebuilt REST API. The latest status of this work can be found on the [DSpace 7 Working Group](https://wiki.lyrasis.org/display/DSPACE/DSpace+7+Working+Group) page. Additionally, the codebases can be found in the following places:
* DSpace 7 REST API work is occurring on the [`main` branch](https://github.com/DSpace/DSpace/tree/main/dspace-server-webapp) of this repository.
* The REST Contract is at https://github.com/DSpace/Rest7Contract
* DSpace 7 Angular UI work is occurring at https://github.com/DSpace/dspace-angular
**If you would like to get involved in our DSpace 7 development effort, we welcome new contributors.** Just join one of our meetings or get in touch via Slack. See the [DSpace 7 UI Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group) wiki page for more info.
**If you would like to get involved in our DSpace 7 development effort, we welcome new contributors.** Just join one of our meetings or get in touch via Slack. See the [DSpace 7 Working Group](https://wiki.lyrasis.org/display/DSPACE/DSpace+7+Working+Group) wiki page for more info.
**If you are looking for the ongoing maintenance work for DSpace 6 (or prior releases)**, you can find that work on the corresponding maintenance branch (e.g. [`dspace-6_x`](https://github.com/DSpace/DSpace/tree/dspace-6_x)) in this repository.
***
@@ -31,10 +31,10 @@ Past releases are all available via GitHub at https://github.com/DSpace/DSpace/r
## Documentation / Installation
Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.duraspace.org/display/DSDOC/).
Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.lyrasis.org/display/DSDOC/).
The latest DSpace Installation instructions are available at:
https://wiki.duraspace.org/display/DSDOC6x/Installing+DSpace
https://wiki.lyrasis.org/display/DSDOC6x/Installing+DSpace
Please be aware that, as a Java web application, DSpace requires a database (PostgreSQL or Oracle)
and a servlet container (usually Tomcat) in order to function.
@@ -49,14 +49,14 @@ DSpace is a community built and supported project. We do not have a centralized
but have a dedicated group of volunteers who help us improve the software, documentation, resources, etc.
We welcome contributions of any type. Here's a few basic guides that provide suggestions for contributing to DSpace:
* [How to Contribute to DSpace](https://wiki.duraspace.org/display/DSPACE/How+to+Contribute+to+DSpace): How to contribute in general (via code, documentation, bug reports, expertise, etc)
* [Code Contribution Guidelines](https://wiki.duraspace.org/display/DSPACE/Code+Contribution+Guidelines): How to give back code or contribute features, bug fixes, etc.
* [DSpace Community Advisory Team (DCAT)](https://wiki.duraspace.org/display/cmtygp/DSpace+Community+Advisory+Team): If you are not a developer, we also have an interest group specifically for repository managers. The DCAT group meets virtually, once a month, and sends open invitations to join their meetings via the [DCAT mailing list](https://groups.google.com/d/forum/DSpaceCommunityAdvisoryTeam).
* [How to Contribute to DSpace](https://wiki.lyrasis.org/display/DSPACE/How+to+Contribute+to+DSpace): How to contribute in general (via code, documentation, bug reports, expertise, etc)
* [Code Contribution Guidelines](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines): How to give back code or contribute features, bug fixes, etc.
* [DSpace Community Advisory Team (DCAT)](https://wiki.lyrasis.org/display/cmtygp/DSpace+Community+Advisory+Team): If you are not a developer, we also have an interest group specifically for repository managers. The DCAT group meets virtually, once a month, and sends open invitations to join their meetings via the [DCAT mailing list](https://groups.google.com/d/forum/DSpaceCommunityAdvisoryTeam).
We also encourage GitHub Pull Requests (PRs) at any time. Please see our [Development with Git](https://wiki.duraspace.org/display/DSPACE/Development+with+Git) guide for more info.
We also encourage GitHub Pull Requests (PRs) at any time. Please see our [Development with Git](https://wiki.lyrasis.org/display/DSPACE/Development+with+Git) guide for more info.
In addition, a listing of all known contributors to DSpace software can be
found online at: https://wiki.duraspace.org/display/DSPACE/DSpaceContributors
found online at: https://wiki.lyrasis.org/display/DSPACE/DSpaceContributors
## Getting Help
@@ -64,12 +64,12 @@ DSpace provides public mailing lists where you can post questions or raise topic
We welcome everyone to participate in these lists:
* [dspace-community@googlegroups.com](https://groups.google.com/d/forum/dspace-community) : General discussion about DSpace platform, announcements, sharing of best practices
* [dspace-tech@googlegroups.com](https://groups.google.com/d/forum/dspace-tech) : Technical support mailing list. See also our guide for [How to troubleshoot an error](https://wiki.duraspace.org/display/DSPACE/Troubleshoot+an+error).
* [dspace-tech@googlegroups.com](https://groups.google.com/d/forum/dspace-tech) : Technical support mailing list. See also our guide for [How to troubleshoot an error](https://wiki.lyrasis.org/display/DSPACE/Troubleshoot+an+error).
* [dspace-devel@googlegroups.com](https://groups.google.com/d/forum/dspace-devel) : Developers / Development mailing list
Great Q&A is also available under the [DSpace tag on Stackoverflow](http://stackoverflow.com/questions/tagged/dspace)
Additional support options are listed at https://wiki.duraspace.org/display/DSPACE/Support
Additional support options are at https://wiki.lyrasis.org/display/DSPACE/Support
DSpace also has an active service provider network. If you'd rather hire a service provider to
install, upgrade, customize or host DSpace, then we recommend getting in touch with one of our
@@ -77,47 +77,46 @@ install, upgrade, customize or host DSpace, then we recommend getting in touch w
## Issue Tracker
The DSpace Issue Tracker can be found at: https://jira.duraspace.org/projects/DS/summary
DSpace uses GitHub to track issues:
* Backend (REST API) issues: https://github.com/DSpace/DSpace/issues
* Frontend (User Interface) issues: https://github.com/DSpace/dspace-angular/issues
## Testing
### Running Tests
By default, in DSpace, Unit Tests and Integration Tests are disabled. However, they are
run automatically by [Travis CI](https://travis-ci.org/DSpace/DSpace/) for all Pull Requests and code commits.
run automatically by [Travis CI](https://travis-ci.com/DSpace/DSpace/) for all Pull Requests and code commits.
* How to run both Unit Tests (via `maven-surefire-plugin`) and Integration Tests (via `maven-failsafe-plugin`):
```
# NOTE: while "mvn test" runs Unit Tests,
# Integration Tests only run for "verify" or "install" phases
mvn clean install -Dmaven.test.skip=false -DskipITs=false
mvn clean test -Dmaven.test.skip=false -DskipITs=false
```
* How to run just Unit Tests:
```
mvn clean test -Dmaven.test.skip=false
mvn test -Dmaven.test.skip=false
```
* How to run a *single* Unit Test
```
# Run all tests in a specific test class
# NOTE: testClassName is just the class name, do not include package
mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]
# NOTE: failIfNoTests=false is required to skip tests in other modules
mvn test -Dmaven.test.skip=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
# Run one test method in a specific test class
mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]#[testMethodName]
mvn test -Dmaven.test.skip=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
```
* How to run Integration Tests (requires running Unit tests too)
* How to run Integration Tests (requires enabling Unit tests too)
```
mvn clean verify -Dmaven.test.skip=false -DskipITs=false
mvn verify -Dmaven.test.skip=false -DskipITs=false
```
* How to run a *single* Integration Test (requires running Unit tests too)
* How to run a *single* Integration Test (requires enabling Unit tests too)
```
# Run all integration tests in a specific test class
# NOTE: Integration Tests only run for "verify" or "install" phases
# NOTE: testClassName is just the class name, do not include package
mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]
# NOTE: failIfNoTests=false is required to skip tests in other modules
mvn test -Dmaven.test.skip=false -DskipITs=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
# Run one test method in a specific test class
mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]#[testMethodName]
mvn test -Dmaven.test.skip=false -DskipITs=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
```
* How to run only tests of a specific DSpace module
```
@@ -133,4 +132,4 @@ run automatically by [Travis CI](https://travis-ci.org/DSpace/DSpace/) for all P
## License
DSpace source code is freely available under a standard [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause).
The full license is available at http://www.dspace.org/license/
The full license is available in the [LICENSE](LICENSE) file or online at http://www.dspace.org/license/

View File

@@ -44,15 +44,16 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
with @SuppressWarnings. See also SuppressWarningsHolder below -->
<module name="SuppressWarningsFilter" />
<!-- Maximum line length is 120 characters -->
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<!-- Only exceptions for packages, imports, URLs, and JavaDoc {@link} tags -->
<property name="ignorePattern" value="^package.*|^import.*|http://|https://|@link"/>
</module>
<!-- Check individual Java source files for specific rules -->
<module name="TreeWalker">
<!-- Maximum line length is 120 characters -->
<module name="LineLength">
<property name="max" value="120"/>
<!-- Only exceptions for packages, imports, URLs, and JavaDoc {@link} tags -->
<property name="ignorePattern" value="^package.*|^import.*|http://|https://|@link"/>
</module>
<!-- Highlight any TODO or FIXME comments in info messages -->
<module name="TodoComment">
<property name="severity" value="info"/>
@@ -94,11 +95,8 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
<!-- <property name="scope" value="public"/> -->
<!-- TODO: Above rule has been disabled because of large amount of missing public method Javadocs -->
<property name="scope" value="nothing"/>
<!-- Allow RuntimeExceptions to be undeclared -->
<property name="allowUndeclaredRTE" value="true"/>
<!-- Allow params, throws and return tags to be optional -->
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
</module>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.0-SNAPSHOT</version>
<version>7.0-beta4-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
@@ -98,20 +98,6 @@
</execution>
</executions>
</plugin>
<!-- Verify OS license headers for all source code files -->
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/src/test/resources/**</exclude>
<exclude>**/src/test/data/**</exclude>
<exclude>**/.gitignore</exclude>
<exclude>**/src/main/resources/rebel.xml</exclude>
<exclude>src/test/data/dspaceFolder/config/spiders/**</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -305,19 +291,25 @@
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<groupId>net.handle</groupId>
<artifactId>handle</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
<groupId>net.cnri</groupId>
<artifactId>cnri-servlet-container</artifactId>
<exclusions>
<!-- Newer versions provided in our parent POM -->
<exclusion>
<artifactId>javax.servlet</artifactId>
<groupId>org.eclipse.jetty.orbit</groupId>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Jetty is needed to run Handle Server -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>jargon</artifactId>
@@ -489,6 +481,61 @@
<groupId>org.apache.solr</groupId>
<artifactId>solr-cell</artifactId>
<version>${solr.client.version}</version>
<exclusions>
<!-- Newer versions provided in our parent POM -->
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>

View File

@@ -115,7 +115,7 @@ public final class CreateAdministrator {
String lastName = null;
char[] password1 = null;
char[] password2 = null;
String language = I18nUtil.DEFAULTLOCALE.getLanguage();
String language = I18nUtil.getDefaultLocale().getLanguage();
while (!dataOK) {
System.out.print("E-mail address: ");

View File

@@ -8,14 +8,10 @@
package org.dspace.app.bulkedit;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -27,6 +23,7 @@ import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.factory.AuthorityServiceFactory;
@@ -141,18 +138,18 @@ public class DSpaceCSV implements Serializable {
/**
* Create a new instance, reading the lines in from file
*
* @param f The file to read from
* @param inputStream the inputstream to read from
* @param c The DSpace Context
* @throws Exception thrown if there is an error reading or processing the file
*/
public DSpaceCSV(File f, Context c) throws Exception {
public DSpaceCSV(InputStream inputStream, Context c) throws Exception {
// Initialise the class
init();
// Open the CSV file
BufferedReader input = null;
try {
input = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
input = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
// Read the heading line
String head = input.readLine();
@@ -623,21 +620,15 @@ public class DSpaceCSV implements Serializable {
}
/**
* Save the CSV file to the given filename
*
* @param filename The filename to save the CSV file to
* @throws IOException Thrown if an error occurs when writing the file
* Creates and returns an InputStream from the CSV Lines in this DSpaceCSV
* @return The InputStream created from the CSVLines in this DSpaceCSV
*/
public final void save(String filename) throws IOException {
// Save the file
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(filename), "UTF-8"));
public InputStream getInputStream() {
StringBuilder stringBuilder = new StringBuilder();
for (String csvLine : getCSVLinesAsStringArray()) {
out.write(csvLine + "\n");
stringBuilder.append(csvLine + "\n");
}
out.flush();
out.close();
return IOUtils.toInputStream(stringBuilder.toString(), StandardCharsets.UTF_8);
}
/**

View File

@@ -8,271 +8,84 @@
package org.dspace.app.bulkedit;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.google.common.collect.Iterators;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.content.service.MetadataDSpaceCsvExportService;
import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.utils.DSpace;
/**
* Metadata exporter to allow the batch export of metadata into a file
*
* @author Stuart Lewis
*/
public class MetadataExport {
/**
* The items to export
*/
protected Iterator<Item> toExport;
public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfiguration> {
protected ItemService itemService;
private boolean help = false;
private String filename = null;
private String handle = null;
private boolean exportAllMetadata = false;
private boolean exportAllItems = false;
protected Context context;
private static final String EXPORT_CSV = "exportCSV";
/**
* Whether to export all metadata, or just normally edited metadata
*/
protected boolean exportAll;
private MetadataDSpaceCsvExportService metadataDSpaceCsvExportService = new DSpace().getServiceManager()
.getServicesByType(MetadataDSpaceCsvExportService.class).get(0);
protected MetadataExport() {
itemService = ContentServiceFactory.getInstance().getItemService();
}
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
/**
* Set up a new metadata export
*
* @param c The Context
* @param toExport The ItemIterator of items to export
* @param exportAll whether to export all metadata or not (include handle, provenance etc)
*/
public MetadataExport(Context c, Iterator<Item> toExport, boolean exportAll) {
itemService = ContentServiceFactory.getInstance().getItemService();
// Store the export settings
this.toExport = toExport;
this.exportAll = exportAll;
this.context = c;
}
/**
* Method to export a community (and sub-communities and collections)
*
* @param c The Context
* @param toExport The Community to export
* @param exportAll whether to export all metadata or not (include handle, provenance etc)
*/
public MetadataExport(Context c, Community toExport, boolean exportAll) {
itemService = ContentServiceFactory.getInstance().getItemService();
@Override
public void internalRun() throws Exception {
if (help) {
handler.logInfo("\nfull export: metadata-export -f filename");
handler.logInfo("partial export: metadata-export -i handle -f filename");
printHelp();
return;
}
Context context = new Context();
context.turnOffAuthorisationSystem();
try {
// Try to export the community
this.toExport = buildFromCommunity(c, toExport, 0);
this.exportAll = exportAll;
this.context = c;
} catch (SQLException sqle) {
// Something went wrong...
System.err.println("Error running exporter:");
sqle.printStackTrace(System.err);
System.exit(1);
context.setCurrentUser(ePersonService.find(context, this.getEpersonIdentifier()));
} catch (SQLException e) {
handler.handleException(e);
}
DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService
.handleExport(context, exportAllItems, exportAllMetadata, handle,
handler);
handler.writeFilestream(context, filename, dSpaceCSV.getInputStream(), EXPORT_CSV);
context.restoreAuthSystemState();
context.complete();
}
/**
* Build an array list of item ids that are in a community (include sub-communities and collections)
*
* @param context DSpace context
* @param community The community to build from
* @param indent How many spaces to use when writing out the names of items added
* @return The list of item ids
* @throws SQLException if database error
*/
protected Iterator<Item> buildFromCommunity(Context context, Community community, int indent)
throws SQLException {
// Add all the collections
List<Collection> collections = community.getCollections();
Iterator<Item> result = null;
for (Collection collection : collections) {
for (int i = 0; i < indent; i++) {
System.out.print(" ");
}
Iterator<Item> items = itemService.findByCollection(context, collection);
result = addItemsToResult(result, items);
}
// Add all the sub-communities
List<Community> communities = community.getSubcommunities();
for (Community subCommunity : communities) {
for (int i = 0; i < indent; i++) {
System.out.print(" ");
}
Iterator<Item> items = buildFromCommunity(context, subCommunity, indent + 1);
result = addItemsToResult(result, items);
}
return result;
@Override
public MetadataExportScriptConfiguration getScriptConfiguration() {
return new DSpace().getServiceManager().getServiceByName("metadata-export",
MetadataExportScriptConfiguration.class);
}
private Iterator<Item> addItemsToResult(Iterator<Item> result, Iterator<Item> items) {
if (result == null) {
result = items;
} else {
result = Iterators.concat(result, items);
}
@Override
public void setup() throws ParseException {
return result;
}
/**
* Run the export
*
* @return the exported CSV lines
*/
public DSpaceCSV export() {
try {
Context.Mode originalMode = context.getCurrentMode();
context.setMode(Context.Mode.READ_ONLY);
// Process each item
DSpaceCSV csv = new DSpaceCSV(exportAll);
while (toExport.hasNext()) {
Item item = toExport.next();
csv.addItem(item);
context.uncacheEntity(item);
}
context.setMode(originalMode);
// Return the results
return csv;
} catch (Exception e) {
// Something went wrong...
System.err.println("Error exporting to CSV:");
e.printStackTrace();
return null;
}
}
/**
* Print the help message
*
* @param options The command line options the user gave
* @param exitCode the system exit code to use
*/
private static void printHelp(Options options, int exitCode) {
// print the help message
HelpFormatter myhelp = new HelpFormatter();
myhelp.printHelp("MetadataExport\n", options);
System.out.println("\nfull export: metadataexport -f filename");
System.out.println("partial export: metadataexport -i handle -f filename");
System.exit(exitCode);
}
/**
* main method to run the metadata exporter
*
* @param argv the command line arguments given
* @throws Exception if error occurs
*/
public static void main(String[] argv) throws Exception {
// Create an options object and populate it
CommandLineParser parser = new PosixParser();
Options options = new Options();
options.addOption("i", "id", true, "ID or handle of thing to export (item, collection, or community)");
options.addOption("f", "file", true, "destination where you want file written");
options.addOption("a", "all", false,
"include all metadata fields that are not normally changed (e.g. provenance)");
options.addOption("h", "help", false, "help");
CommandLine line = null;
try {
line = parser.parse(options, argv);
} catch (ParseException pe) {
System.err.println("Error with commands.");
printHelp(options, 1);
System.exit(0);
}
if (line.hasOption('h')) {
printHelp(options, 0);
if (commandLine.hasOption('h')) {
help = true;
return;
}
// Check a filename is given
if (!line.hasOption('f')) {
System.err.println("Required parameter -f missing!");
printHelp(options, 1);
if (!commandLine.hasOption('f')) {
throw new ParseException("Required parameter -f missing!");
}
String filename = line.getOptionValue('f');
filename = commandLine.getOptionValue('f');
// Create a context
Context c = new Context(Context.Mode.READ_ONLY);
c.turnOffAuthorisationSystem();
exportAllMetadata = commandLine.hasOption('a');
// The things we'll export
Iterator<Item> toExport = null;
MetadataExport exporter = null;
// Export everything?
boolean exportAll = line.hasOption('a');
ContentServiceFactory contentServiceFactory = ContentServiceFactory.getInstance();
// Check we have an item OK
ItemService itemService = contentServiceFactory.getItemService();
if (!line.hasOption('i')) {
System.out.println("Exporting whole repository WARNING: May take some time!");
exporter = new MetadataExport(c, itemService.findAll(c), exportAll);
} else {
String handle = line.getOptionValue('i');
DSpaceObject dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(c, handle);
if (dso == null) {
System.err.println("Item '" + handle + "' does not resolve to an item in your repository!");
printHelp(options, 1);
}
if (dso.getType() == Constants.ITEM) {
System.out.println("Exporting item '" + dso.getName() + "' (" + handle + ")");
List<Item> item = new ArrayList<>();
item.add((Item) dso);
exporter = new MetadataExport(c, item.iterator(), exportAll);
} else if (dso.getType() == Constants.COLLECTION) {
System.out.println("Exporting collection '" + dso.getName() + "' (" + handle + ")");
Collection collection = (Collection) dso;
toExport = itemService.findByCollection(c, collection);
exporter = new MetadataExport(c, toExport, exportAll);
} else if (dso.getType() == Constants.COMMUNITY) {
System.out.println("Exporting community '" + dso.getName() + "' (" + handle + ")");
exporter = new MetadataExport(c, (Community) dso, exportAll);
} else {
System.err.println("Error identifying '" + handle + "'");
System.exit(1);
}
if (!commandLine.hasOption('i')) {
exportAllItems = true;
}
// Perform the export
DSpaceCSV csv = exporter.export();
// Save the files to the file
csv.save(filename);
// Finish off and tidy up
c.restoreAuthSystemState();
c.complete();
handle = commandLine.getOptionValue('i');
}
}

View File

@@ -0,0 +1,74 @@
/**
* 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.bulkedit;
import java.io.OutputStream;
import java.sql.SQLException;
import org.apache.commons.cli.Options;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The {@link ScriptConfiguration} for the {@link MetadataExport} script
*/
public class MetadataExportScriptConfiguration<T extends MetadataExport> extends ScriptConfiguration<T> {
@Autowired
private AuthorizeService authorizeService;
private Class<T> dspaceRunnableClass;
@Override
public Class<T> getDspaceRunnableClass() {
return dspaceRunnableClass;
}
/**
* Generic setter for the dspaceRunnableClass
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this MetadataExportScriptConfiguration
*/
@Override
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
this.dspaceRunnableClass = dspaceRunnableClass;
}
@Override
public boolean isAllowedToExecute(Context context) {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e);
}
}
@Override
public Options getOptions() {
if (options == null) {
Options options = new Options();
options.addOption("i", "id", true, "ID or handle of thing to export (item, collection, or community)");
options.getOption("i").setType(String.class);
options.addOption("f", "file", true, "destination where you want file written");
options.getOption("f").setType(OutputStream.class);
options.getOption("f").setRequired(true);
options.addOption("a", "all", false,
"include all metadata fields that are not normally changed (e.g. provenance)");
options.getOption("a").setType(boolean.class);
options.addOption("h", "help", false, "help");
options.getOption("h").setType(boolean.class);
super.options = options;
}
return options;
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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.bulkedit;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
/**
* CLI variant for the {@link MetadataImport} class
* This has been made so that we can specify the behaviour of the determineChanges method to be specific for the CLI
*/
public class MetadataImportCLI extends MetadataImport {
@Override
protected boolean determineChange(DSpaceRunnableHandler handler) throws IOException {
handler.logInfo("Do you want to make these changes? [y/n] ");
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) {
String yn = bufferedReader.readLine();
if ("y".equalsIgnoreCase(yn)) {
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,16 @@
/**
* 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.bulkedit;
import org.dspace.scripts.configuration.ScriptConfiguration;
/**
* The {@link ScriptConfiguration} for the {@link org.dspace.app.bulkedit.MetadataImportCLI} CLI script
*/
public class MetadataImportCliScriptConfiguration extends MetadataImportScriptConfiguration<MetadataImportCLI> {
}

View File

@@ -0,0 +1,84 @@
/**
* 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.bulkedit;
import java.io.InputStream;
import java.sql.SQLException;
import org.apache.commons.cli.Options;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The {@link ScriptConfiguration} for the {@link MetadataImport} script
*/
public class MetadataImportScriptConfiguration<T extends MetadataImport> extends ScriptConfiguration<T> {
@Autowired
private AuthorizeService authorizeService;
private Class<T> dspaceRunnableClass;
@Override
public Class<T> getDspaceRunnableClass() {
return dspaceRunnableClass;
}
/**
* Generic setter for the dspaceRunnableClass
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this MetadataImportScriptConfiguration
*/
@Override
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
this.dspaceRunnableClass = dspaceRunnableClass;
}
@Override
public boolean isAllowedToExecute(Context context) {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e);
}
}
@Override
public Options getOptions() {
if (options == null) {
Options options = new Options();
options.addOption("f", "file", true, "source file");
options.getOption("f").setType(InputStream.class);
options.getOption("f").setRequired(true);
options.addOption("e", "email", true, "email address or user id of user (required if adding new items)");
options.getOption("e").setType(String.class);
options.getOption("e").setRequired(true);
options.addOption("s", "silent", false,
"silent operation - doesn't request confirmation of changes USE WITH CAUTION");
options.getOption("s").setType(boolean.class);
options.addOption("w", "workflow", false, "workflow - when adding new items, use collection workflow");
options.getOption("w").setType(boolean.class);
options.addOption("n", "notify", false,
"notify - when adding new items using a workflow, send notification emails");
options.getOption("n").setType(boolean.class);
options.addOption("v", "validate-only", false,
"validate - just validate the csv, don't run the import");
options.getOption("v").setType(boolean.class);
options.addOption("t", "template", false,
"template - when adding new items, use the collection template (if it exists)");
options.getOption("t").setType(boolean.class);
options.addOption("h", "help", false, "help");
options.getOption("h").setType(boolean.class);
super.options = options;
}
return options;
}
}

View File

@@ -16,9 +16,11 @@ import java.util.TreeMap;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.dspace.scripts.factory.ScriptServiceFactory;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
import org.dspace.scripts.handler.impl.CommandLineDSpaceRunnableHandler;
import org.dspace.scripts.service.ScriptService;
import org.dspace.servicemanager.DSpaceKernelImpl;
import org.dspace.servicemanager.DSpaceKernelInit;
import org.dspace.services.RequestService;
@@ -44,7 +46,8 @@ public class ScriptLauncher {
/**
* Default constructor
*/
private ScriptLauncher() { }
private ScriptLauncher() {
}
/**
* Execute the DSpace script launcher
@@ -54,7 +57,7 @@ public class ScriptLauncher {
* @throws FileNotFoundException if file doesn't exist
*/
public static void main(String[] args)
throws FileNotFoundException, IOException {
throws FileNotFoundException, IOException, IllegalAccessException, InstantiationException {
// Initialise the service manager kernel
try {
kernelImpl = DSpaceKernelInit.getKernel(null);
@@ -107,13 +110,18 @@ public class ScriptLauncher {
* @param commandConfigs The Document
* @param dSpaceRunnableHandler The DSpaceRunnableHandler for this execution
* @param kernelImpl The relevant DSpaceKernelImpl
* @return A 1 or 0 depending on whether the script failed or passed respectively
* @return A 1 or 0 depending on whether the script failed or passed respectively
*/
public static int handleScript(String[] args, Document commandConfigs,
DSpaceRunnableHandler dSpaceRunnableHandler,
DSpaceKernelImpl kernelImpl) {
DSpaceRunnableHandler dSpaceRunnableHandler,
DSpaceKernelImpl kernelImpl) throws InstantiationException, IllegalAccessException {
int status;
DSpaceRunnable script = ScriptServiceFactory.getInstance().getScriptService().getScriptForName(args[0]);
ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService();
ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]);
DSpaceRunnable script = null;
if (scriptConfiguration != null) {
script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration);
}
if (script != null) {
status = executeScript(args, dSpaceRunnableHandler, script);
} else {
@@ -127,12 +135,12 @@ public class ScriptLauncher {
* @param args The arguments of the script with the script name as first place in the array
* @param dSpaceRunnableHandler The relevant DSpaceRunnableHandler
* @param script The script to be executed
* @return A 1 or 0 depending on whether the script failed or passed respectively
* @return A 1 or 0 depending on whether the script failed or passed respectively
*/
private static int executeScript(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler,
DSpaceRunnable script) {
try {
script.initialize(args, dSpaceRunnableHandler);
script.initialize(args, dSpaceRunnableHandler, null);
script.run();
return 0;
} catch (ParseException e) {

View File

@@ -9,7 +9,10 @@ package org.dspace.app.util;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.Logger;
import org.dspace.authenticate.factory.AuthenticateServiceFactory;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
@@ -19,12 +22,22 @@ import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.GroupService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService;
/**
* This class is an addition to the AuthorizeManager that perform authorization
@@ -34,6 +47,7 @@ import org.dspace.core.Context;
*/
public class AuthorizeUtil {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(AuthorizeUtil.class);
/**
* Default constructor
*/
@@ -190,10 +204,11 @@ public class AuthorizeUtil {
public static void authorizeManageCCLicense(Context context, Item item)
throws AuthorizeException, SQLException {
AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
ItemService itemService = ContentServiceFactory.getInstance().getItemService();
try {
authorizeService.authorizeAction(context, item, Constants.ADD);
authorizeService.authorizeAction(context, item, Constants.REMOVE);
authorizeService.authorizeAction(context, item, Constants.ADD, false);
authorizeService.authorizeAction(context, item, Constants.REMOVE, false);
} catch (AuthorizeException authex) {
if (AuthorizeConfiguration.canItemAdminManageCCLicense()) {
authorizeService
@@ -202,8 +217,10 @@ public class AuthorizeUtil {
authorizeService.authorizeAction(context, itemService
.getParentObject(context, item), Constants.ADMIN);
} else if (AuthorizeConfiguration.canCommunityAdminManageCCLicense()) {
authorizeService.authorizeAction(context, itemService
.getParentObject(context, item), Constants.ADMIN);
Collection collection = (Collection) itemService
.getParentObject(context, item);
authorizeService.authorizeAction(context, collectionService.getParentObject(context, collection),
Constants.ADMIN);
} else {
requireAdminRole(context);
}
@@ -522,4 +539,154 @@ public class AuthorizeUtil {
}
}
}
/**
* This method will check whether the current user is authorized to manage the default read group
* @param context The relevant DSpace context
* @param collection The collection for which this will be checked
* @throws AuthorizeException If something goes wrong
* @throws SQLException If something goes wrong
*/
public static void authorizeManageDefaultReadGroup(Context context,
Collection collection) throws AuthorizeException, SQLException {
AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
authorizeService.authorizeAction(context, collection, Constants.ADMIN);
}
/**
* This method checks whether the current user has sufficient rights to modify the group.
* Depending on the kind of group and due to delegated administration, separate checks need to be done to verify
* whether the user is allowed to modify the group.
*
* @param context the context of which the user will be checked
* @param group the group to be checked
* @throws SQLException
* @throws AuthorizeException
*/
public static void authorizeManageGroup(Context context, Group group) throws SQLException, AuthorizeException {
AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
CollectionRoleService collectionRoleService = XmlWorkflowServiceFactory.getInstance()
.getCollectionRoleService();
if (authorizeService.isAdmin(context)) {
return;
}
DSpaceObject parentObject = groupService.getParentObject(context, group);
if (parentObject == null) {
throw new AuthorizeException("not authorized to manage this group");
}
if (parentObject.getType() == Constants.COLLECTION) {
Collection collection = (Collection) parentObject;
if (group.equals(collection.getSubmitters())) {
authorizeManageSubmittersGroup(context, collection);
return;
}
List<CollectionRole> collectionRoles = collectionRoleService.findByCollection(context, collection);
for (CollectionRole role : collectionRoles) {
if (group.equals(role.getGroup())) {
authorizeManageWorkflowsGroup(context, collection);
return;
}
}
if (group.equals(collection.getAdministrators())) {
authorizeManageAdminGroup(context, collection);
return;
}
// if we reach this point, it means that the group is related
// to a collection but as it is not the submitters, nor the administrators,
// nor a workflow groups it must be a default item/bitstream groups
authorizeManageDefaultReadGroup(context, collection);
return;
}
if (parentObject.getType() == Constants.COMMUNITY) {
Community community = (Community) parentObject;
authorizeManageAdminGroup(context, community);
return;
}
throw new AuthorizeException("not authorized to manage this group");
}
/**
* This method will return a boolean indicating whether the current user is allowed to register a new
* account or not
* @param context The relevant DSpace context
* @param request The current request
* @return A boolean indicating whether the current user can register a new account or not
* @throws SQLException If something goes wrong
*/
public static boolean authorizeNewAccountRegistration(Context context, HttpServletRequest request)
throws SQLException {
if (DSpaceServicesFactory.getInstance().getConfigurationService()
.getBooleanProperty("user.registration", true)) {
// This allowSetPassword is currently the only mthod that would return true only when it's
// actually expected to be returning true.
// For example the LDAP canSelfRegister will return true due to auto-register, while that
// does not imply a new user can register explicitly
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
.allowSetPassword(context, request, null);
}
return false;
}
/**
* This method will return a boolean indicating whether it's allowed to update the password for the EPerson
* with the given email and canLogin property
* @param context The relevant DSpace context
* @param email The email to be checked
* @return A boolean indicating if the password can be updated or not
*/
public static boolean authorizeUpdatePassword(Context context, String email) {
try {
EPerson eperson = EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, email);
if (eperson != null && eperson.canLogIn()) {
HttpServletRequest request = new DSpace().getRequestService().getCurrentRequest()
.getHttpServletRequest();
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
.allowSetPassword(context, request, null);
}
} catch (SQLException e) {
log.error("Something went wrong trying to retrieve EPerson for email: " + email, e);
}
return false;
}
/**
* This method checks if the community Admin can manage accounts
*
* @return true if is able
*/
public static boolean canCommunityAdminManageAccounts() {
boolean isAble = false;
if (AuthorizeConfiguration.canCommunityAdminManagePolicies()
|| AuthorizeConfiguration.canCommunityAdminManageAdminGroup()
|| AuthorizeConfiguration.canCommunityAdminManageCollectionPolicies()
|| AuthorizeConfiguration.canCommunityAdminManageCollectionSubmitters()
|| AuthorizeConfiguration.canCommunityAdminManageCollectionWorkflows()
|| AuthorizeConfiguration.canCommunityAdminManageCollectionAdminGroup()) {
isAble = true;
}
return isAble;
}
/**
* This method checks if the Collection Admin can manage accounts
*
* @return true if is able
*/
public static boolean canCollectionAdminManageAccounts() {
boolean isAble = false;
if (AuthorizeConfiguration.canCollectionAdminManagePolicies()
|| AuthorizeConfiguration.canCollectionAdminManageSubmitters()
|| AuthorizeConfiguration.canCollectionAdminManageWorkflows()
|| AuthorizeConfiguration.canCollectionAdminManageAdminGroup()) {
isAble = true;
}
return isAble;
}
}

View File

@@ -10,6 +10,7 @@ package org.dspace.app.util;
import java.util.List;
import java.util.Map;
import org.dspace.core.Utils;
/**
* Class representing all DC inputs required for a submission, organized into pages
*
@@ -107,9 +108,21 @@ public class DCInputSet {
for (int i = 0; i < inputs.length; i++) {
for (int j = 0; j < inputs[i].length; j++) {
DCInput field = inputs[i][j];
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
return true;
// If this is a "qualdrop_value" field, then the full field name is the field + dropdown qualifier
if (field.getInputType().equals("qualdrop_value")) {
List<String> pairs = field.getPairs();
for (int k = 0; k < pairs.size(); k += 2) {
String qualifier = pairs.get(k + 1);
String fullName = Utils.standardize(field.getSchema(), field.getElement(), qualifier, ".");
if (fullName.equals(fieldName)) {
return true;
}
}
} else {
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
return true;
}
}
}
}

View File

@@ -250,12 +250,8 @@ public class IndexVersion {
} else if (firstMinor > secondMinor) {
// If we get here, major versions must be EQUAL. Now, time to check our minor versions
return GREATER_THAN;
} else if (firstMinor < secondMinor) {
return LESS_THAN;
} else {
// This is an impossible scenario.
// This 'else' should never be triggered since we've checked for equality above already
return EQUAL;
return LESS_THAN;
}
}

View File

@@ -20,7 +20,6 @@ import com.sun.syndication.feed.module.opensearch.OpenSearchModule;
import com.sun.syndication.feed.module.opensearch.entity.OSQuery;
import com.sun.syndication.feed.module.opensearch.impl.OpenSearchModuleImpl;
import com.sun.syndication.io.FeedException;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.service.OpenSearchService;
import org.dspace.content.DSpaceObject;

View File

@@ -87,13 +87,16 @@ public class IPMatcher {
+ ipSpec);
}
int maskBytes = maskBits / 8;
for (int i = 0; i < maskBytes; i++) {
netmask[i] = (byte) 0Xff;
}
netmask[maskBytes] = (byte) ((byte) 0Xff << 8 - (maskBits % 8)); // FIXME test!
for (int i = maskBytes + 1; i < (128 / 8); i++) {
netmask[i] = 0;
for (int i = 0; i < netmask.length; i++) {
if (maskBits <= 0) {
netmask[i] = 0;
} else if (maskBits > 8) {
netmask[i] = (byte) 0Xff;
} else {
netmask[i] = (byte) ((byte) 0Xff << 8 - maskBits);
}
maskBits = maskBits - 8;
}
break;
case 1: // No explicit mask: fill the mask with 1s

View File

@@ -34,6 +34,7 @@ import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.core.Context;
import org.dspace.core.Utils;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
@@ -492,35 +493,22 @@ public class ShibAuthentication implements AuthenticationMethod {
boolean lazySession = configurationService.getBooleanProperty("authentication-shibboleth.lazysession", false);
if ( lazySession ) {
String shibURL = configurationService.getProperty("authentication-shibboleth.lazysession.loginurl");
boolean forceHTTPS =
configurationService.getBooleanProperty("authentication-shibboleth.lazysession.secure",true);
String shibURL = getShibURL(request);
// Shibboleth authentication initiator
if (shibURL == null || shibURL.length() == 0) {
shibURL = "/Shibboleth.sso/Login";
// Determine the client redirect URL, where to redirect after authenticating.
String redirectUrl = null;
if (request.getHeader("Referer") != null && StringUtils.isNotBlank(request.getHeader("Referer"))) {
redirectUrl = request.getHeader("Referer");
} else if (request.getHeader("X-Requested-With") != null
&& StringUtils.isNotBlank(request.getHeader("X-Requested-With"))) {
redirectUrl = request.getHeader("X-Requested-With");
}
shibURL = shibURL.trim();
// Determine the return URL, where shib will send the user after authenticating. We need it to go back
// to DSpace's shibboleth-login url so the we will extract the user's information and locally
// authenticate them.
String host = request.getServerName();
int port = request.getServerPort();
String contextPath = request.getContextPath();
String returnURL = request.getHeader("Referer");
if (returnURL == null) {
if (request.isSecure() || forceHTTPS) {
returnURL = "https://";
} else {
returnURL = "http://";
}
returnURL += host;
if (!(port == 443 || port == 80)) {
returnURL += ":" + port;
}
}
// Determine the server return URL, where shib will send the user after authenticating.
// We need it to go back to DSpace's shibboleth-login url so we will extract the user's information
// and locally authenticate them.
String returnURL = configurationService.getProperty("dspace.server.url") + "/api/authn/shibboleth"
+ ((redirectUrl != null) ? "?redirectUrl=" + redirectUrl : "");
try {
shibURL += "?target=" + URLEncoder.encode(returnURL, "UTF-8");
@@ -1258,6 +1246,23 @@ public class ShibAuthentication implements AuthenticationMethod {
return valueList;
}
private String getShibURL(HttpServletRequest request) {
String shibURL = configurationService.getProperty("authentication-shibboleth.lazysession.loginurl",
"/Shibboleth.sso/Login");
boolean forceHTTPS =
configurationService.getBooleanProperty("authentication-shibboleth.lazysession.secure", true);
// Shibboleth url must be absolute
if (shibURL.startsWith("/")) {
String serverUrl = Utils.getBaseUrl(configurationService.getProperty("dspace.server.url"));
shibURL = serverUrl + shibURL;
if ((request.isSecure() || forceHTTPS) && shibURL.startsWith("http://")) {
shibURL = shibURL.replace("http://", "https://");
}
}
return shibURL;
}
}

View File

@@ -7,7 +7,8 @@
*/
package org.dspace.authorize;
import org.dspace.core.ConfigurationManager;
import org.dspace.services.ConfigurationService;
import org.dspace.utils.DSpace;
/**
* This class is responsible to provide access to the configuration of the
@@ -16,164 +17,26 @@ import org.dspace.core.ConfigurationManager;
* @author bollini
*/
public class AuthorizeConfiguration {
private static boolean can_communityAdmin_group = ConfigurationManager
.getBooleanProperty("core.authorization.community-admin.group",
true);
// subcommunities and collections
private static boolean can_communityAdmin_createSubelement = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.create-subelement",
true);
private static boolean can_communityAdmin_deleteSubelement = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.delete-subelement",
true);
private static boolean can_communityAdmin_policies = ConfigurationManager
.getBooleanProperty("core.authorization.community-admin.policies",
true);
private static boolean can_communityAdmin_adminGroup = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.admin-group", true);
private static boolean can_communityAdmin_collectionPolicies = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.collection.policies",
true);
private static boolean can_communityAdmin_collectionTemplateItem = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.collection.template-item",
true);
private static boolean can_communityAdmin_collectionSubmitters = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.collection.submitters",
true);
private static boolean can_communityAdmin_collectionWorkflows = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.collection.workflows",
true);
private static boolean can_communityAdmin_collectionAdminGroup = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.collection.admin-group",
true);
private static boolean can_communityAdmin_itemDelete = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item.delete", true);
private static boolean can_communityAdmin_itemWithdraw = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item.withdraw", true);
private static boolean can_communityAdmin_itemReinstatiate = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item.reinstatiate",
true);
private static boolean can_communityAdmin_itemPolicies = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item.policies", true);
// # also bundle
private static boolean can_communityAdmin_itemCreateBitstream = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item.create-bitstream",
true);
private static boolean can_communityAdmin_itemDeleteBitstream = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item.delete-bitstream",
true);
private static boolean can_communityAdmin_itemAdminccLicense = ConfigurationManager
.getBooleanProperty(
"core.authorization.community-admin.item-admin.cc-license",
true);
// # COLLECTION ADMIN
private static boolean can_collectionAdmin_policies = ConfigurationManager
.getBooleanProperty("core.authorization.collection-admin.policies",
true);
private static boolean can_collectionAdmin_templateItem = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.template-item", true);
private static boolean can_collectionAdmin_submitters = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.submitters", true);
private static boolean can_collectionAdmin_workflows = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.workflows", true);
private static boolean can_collectionAdmin_adminGroup = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.admin-group", true);
private static boolean can_collectionAdmin_itemDelete = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item.delete", true);
private static boolean can_collectionAdmin_itemWithdraw = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item.withdraw", true);
private static boolean can_collectionAdmin_itemReinstatiate = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item.reinstatiate",
true);
private static boolean can_collectionAdmin_itemPolicies = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item.policies", true);
// # also bundle
private static boolean can_collectionAdmin_itemCreateBitstream = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item.create-bitstream",
true);
private static boolean can_collectionAdmin_itemDeleteBitstream = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item.delete-bitstream",
true);
private static boolean can_collectionAdmin_itemAdminccLicense = ConfigurationManager
.getBooleanProperty(
"core.authorization.collection-admin.item-admin.cc-license",
true);
// # ITEM ADMIN
private static boolean can_itemAdmin_policies = ConfigurationManager
.getBooleanProperty("core.authorization.item-admin.policies", true);
// # also bundle
private static boolean can_itemAdmin_createBitstream = ConfigurationManager
.getBooleanProperty(
"core.authorization.item-admin.create-bitstream", true);
private static boolean can_itemAdmin_deleteBitstream = ConfigurationManager
.getBooleanProperty(
"core.authorization.item-admin.delete-bitstream", true);
private static boolean can_itemAdmin_ccLicense = ConfigurationManager
.getBooleanProperty("core.authorization.item-admin.cc-license",
true);
/**
* A static reference to the {@link ConfigurationService} see the init method for initialization
*/
private static ConfigurationService configurationService;
/**
* Default constructor
*/
private AuthorizeConfiguration() { }
/**
* Complete the initialization of the class retrieving a reference to the {@link ConfigurationService}. MUST be
* called at the start of each method
*/
private synchronized static void init() {
if (configurationService != null) {
return;
}
configurationService = new DSpace().getConfigurationService();
}
/**
* Are community admins allowed to create new, not strictly community
* related, group?
@@ -181,7 +44,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformGroupCreation() {
return can_communityAdmin_group;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.group", true);
}
/**
@@ -190,7 +54,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformSubelementCreation() {
return can_communityAdmin_createSubelement;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.create-subelement", true);
}
/**
@@ -199,7 +64,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformSubelementDeletion() {
return can_communityAdmin_deleteSubelement;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.delete-subelement", true);
}
/**
@@ -209,7 +75,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManagePolicies() {
return can_communityAdmin_policies;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.policies", true);
}
/**
@@ -219,7 +86,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageAdminGroup() {
return can_communityAdmin_adminGroup;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.admin-group", true);
}
/**
@@ -229,7 +97,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionPolicies() {
return can_communityAdmin_collectionPolicies;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.collection.policies", true);
}
/**
@@ -239,7 +108,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionTemplateItem() {
return can_communityAdmin_collectionTemplateItem;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.collection.template-item",
true);
}
/**
@@ -249,7 +120,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionSubmitters() {
return can_communityAdmin_collectionSubmitters;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.collection.submitters",
true);
}
/**
@@ -259,7 +132,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionWorkflows() {
return can_communityAdmin_collectionWorkflows;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.collection.workflows", true);
}
/**
@@ -269,7 +143,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionAdminGroup() {
return can_communityAdmin_collectionAdminGroup;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.collection.admin-group",
true);
}
/**
@@ -278,7 +154,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformItemDeletion() {
return can_communityAdmin_itemDelete;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item.delete", true);
}
/**
@@ -287,7 +164,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformItemWithdrawn() {
return can_communityAdmin_itemWithdraw;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item.withdraw", true);
}
/**
@@ -297,7 +175,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformItemReinstatiate() {
return can_communityAdmin_itemReinstatiate;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item.reinstatiate", true);
}
/**
@@ -307,7 +186,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageItemPolicies() {
return can_communityAdmin_itemPolicies;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item.policies", true);
}
/**
@@ -317,7 +197,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformBitstreamCreation() {
return can_communityAdmin_itemCreateBitstream;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item.create-bitstream",
true);
}
/**
@@ -327,7 +209,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformBitstreamDeletion() {
return can_communityAdmin_itemDeleteBitstream;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item.delete-bitstream",
true);
}
/**
@@ -337,7 +221,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCCLicense() {
return can_communityAdmin_itemAdminccLicense;
init();
return configurationService.getBooleanProperty("core.authorization.community-admin.item-admin.cc-license",
true);
}
/**
@@ -346,7 +232,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManagePolicies() {
return can_collectionAdmin_policies;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.policies", true);
}
/**
@@ -356,7 +243,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageTemplateItem() {
return can_collectionAdmin_templateItem;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.template-item", true);
}
/**
@@ -366,7 +254,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageSubmitters() {
return can_collectionAdmin_submitters;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.submitters", true);
}
/**
@@ -376,7 +265,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageWorkflows() {
return can_collectionAdmin_workflows;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.workflows", true);
}
/**
@@ -386,7 +276,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageAdminGroup() {
return can_collectionAdmin_adminGroup;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.admin-group", true);
}
/**
@@ -395,7 +286,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformItemDeletion() {
return can_collectionAdmin_itemDelete;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item.delete", true);
}
/**
@@ -404,7 +296,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformItemWithdrawn() {
return can_collectionAdmin_itemWithdraw;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item.withdraw", true);
}
/**
@@ -414,7 +307,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformItemReinstatiate() {
return can_collectionAdmin_itemReinstatiate;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item.reinstatiate", true);
}
/**
@@ -424,7 +318,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageItemPolicies() {
return can_collectionAdmin_itemPolicies;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item.policies", true);
}
/**
@@ -434,7 +329,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformBitstreamCreation() {
return can_collectionAdmin_itemCreateBitstream;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item.create-bitstream",
true);
}
/**
@@ -444,7 +341,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformBitstreamDeletion() {
return can_collectionAdmin_itemDeleteBitstream;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item.delete-bitstream",
true);
}
/**
@@ -454,7 +353,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageCCLicense() {
return can_collectionAdmin_itemAdminccLicense;
init();
return configurationService.getBooleanProperty("core.authorization.collection-admin.item-admin.cc-license",
true);
}
/**
@@ -463,7 +364,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminManagePolicies() {
return can_itemAdmin_policies;
init();
return configurationService.getBooleanProperty("core.authorization.item-admin.policies", true);
}
/**
@@ -472,7 +374,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminPerformBitstreamCreation() {
return can_itemAdmin_createBitstream;
init();
return configurationService.getBooleanProperty("core.authorization.item-admin.create-bitstream", true);
}
/**
@@ -481,7 +384,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminPerformBitstreamDeletion() {
return can_itemAdmin_deleteBitstream;
init();
return configurationService.getBooleanProperty("core.authorization.item-admin.delete-bitstream", true);
}
/**
@@ -490,7 +394,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminManageCCLicense() {
return can_itemAdmin_ccLicense;
init();
return configurationService.getBooleanProperty("core.authorization.item-admin.cc-license", true);
}
}

View File

@@ -430,7 +430,11 @@ public class AuthorizeServiceImpl implements AuthorizeService {
public boolean isCommunityAdmin(Context c) throws SQLException {
EPerson e = c.getCurrentUser();
return isCommunityAdmin(c, e);
}
@Override
public boolean isCommunityAdmin(Context c, EPerson e) throws SQLException {
if (e != null) {
List<ResourcePolicy> policies = resourcePolicyService.find(c, e,
groupService.allMemberGroups(c, e),
@@ -446,7 +450,11 @@ public class AuthorizeServiceImpl implements AuthorizeService {
public boolean isCollectionAdmin(Context c) throws SQLException {
EPerson e = c.getCurrentUser();
return isCollectionAdmin(c, e);
}
@Override
public boolean isCollectionAdmin(Context c, EPerson e) throws SQLException {
if (e != null) {
List<ResourcePolicy> policies = resourcePolicyService.find(c, e,
groupService.allMemberGroups(c, e),

View File

@@ -8,10 +8,8 @@
package org.dspace.authorize.dao.impl;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;

View File

@@ -213,6 +213,26 @@ public interface AuthorizeService {
public boolean isCollectionAdmin(Context c) throws SQLException;
/**
* Check to see if a specific user is Community admin
*
* @param c current context
* @param e the user to check
* @return true if user is an admin of some community
* @throws SQLException
*/
public boolean isCommunityAdmin(Context c, EPerson e) throws SQLException;
/**
* Check to see if a specific user is Collection admin
*
* @param c current context
* @param e the user to check
* @return true if user is an admin of some collection
* @throws SQLException if database error
*/
public boolean isCollectionAdmin(Context c, EPerson e) throws SQLException;
///////////////////////////////////////////////
// policy manipulation methods
///////////////////////////////////////////////

View File

@@ -11,7 +11,6 @@ import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;

View File

@@ -13,7 +13,6 @@ import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import org.apache.commons.collections4.CollectionUtils;

View File

@@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;

View File

@@ -17,11 +17,13 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.dspace.app.util.AuthorizeUtil;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
@@ -40,6 +42,13 @@ import org.dspace.core.Context;
import org.dspace.core.I18nUtil;
import org.dspace.core.LogManager;
import org.dspace.core.service.LicenseService;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
import org.dspace.eperson.service.SubscribeService;
@@ -100,6 +109,9 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
@Autowired(required = true)
protected CollectionRoleService collectionRoleService;
@Autowired(required = true)
protected SearchService searchService;
protected CollectionServiceImpl() {
super();
}
@@ -889,4 +901,95 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
throws SQLException {
return collectionDAO.getCollectionsWithBitstreamSizesTotal(context);
}
@Override
public Group createDefaultReadGroup(Context context, Collection collection, String typeOfGroupString,
int defaultRead)
throws SQLException, AuthorizeException {
Group role = groupService.create(context);
groupService.setName(role, "COLLECTION_" + collection.getID().toString() + "_" + typeOfGroupString +
"_DEFAULT_READ");
// Remove existing privileges from the anonymous group.
authorizeService.removePoliciesActionFilter(context, collection, defaultRead);
// Grant our new role the default privileges.
authorizeService.addPolicy(context, collection, defaultRead, role);
groupService.update(context, role);
return role;
}
@Override
public List<Collection> findCollectionsWithSubmit(String q, Context context, Community community,
int offset, int limit) throws SQLException, SearchServiceException {
List<Collection> collections = new ArrayList<Collection>();
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
discoverQuery.setStart(offset);
discoverQuery.setMaxResults(limit);
DiscoverResult resp = retrieveCollectionsWithSubmit(context, discoverQuery,community, q);
for (IndexableObject solrCollections : resp.getIndexableObjects()) {
Collection c = ((IndexableCollection) solrCollections).getIndexedObject();
collections.add(c);
}
return collections;
}
@Override
public int countCollectionsWithSubmit(String q, Context context, Community community)
throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
DiscoverResult resp = retrieveCollectionsWithSubmit(context, discoverQuery,community,q);
return (int)resp.getTotalSearchResults();
}
/**
* Finds all Indexed Collections where the current user has submit rights. If the user is an Admin,
* this is all Indexed Collections. Otherwise, it includes those collections where
* an indexed "submit" policy lists either the eperson or one of the eperson's groups
*
* @param context DSpace context
* @param discoverQuery
* @param community parent community, could be null
* @param q limit the returned collection to those with metadata values matching the query
* terms. The terms are used to make also a prefix query on SOLR
* so it can be used to implement an autosuggest feature over the collection name
* @return discovery search result objects
* @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
private DiscoverResult retrieveCollectionsWithSubmit(Context context, DiscoverQuery discoverQuery,
Community community, String q) throws SQLException, SearchServiceException {
StringBuilder query = new StringBuilder();
EPerson currentUser = context.getCurrentUser();
if (!authorizeService.isAdmin(context)) {
String userId = "";
if (currentUser != null) {
userId = currentUser.getID().toString();
}
query.append("submit:(e").append(userId);
Set<Group> groups = groupService.allMemberGroupsSet(context, currentUser);
for (Group group : groups) {
query.append(" OR g").append(group.getID());
}
query.append(")");
discoverQuery.addFilterQueries(query.toString());
}
if (community != null) {
discoverQuery.addFilterQueries("location.comm:" + community.getID().toString());
}
if (StringUtils.isNotBlank(q)) {
StringBuilder buildQuery = new StringBuilder();
String escapedQuery = ClientUtils.escapeQueryChars(q);
buildQuery.append(escapedQuery).append(" OR ").append(escapedQuery).append("*");
discoverQuery.setQuery(buildQuery.toString());
}
DiscoverResult resp = searchService.search(context, discoverQuery);
return resp;
}
}

View File

@@ -11,7 +11,6 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -271,4 +270,4 @@ public class Community extends DSpaceObject implements DSpaceObjectLegacySupport
return communityService;
}
}
}

View File

@@ -11,7 +11,6 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;

View File

@@ -13,7 +13,6 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;

View File

@@ -0,0 +1,129 @@
/**
* 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.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.google.common.collect.Iterators;
import org.dspace.app.bulkedit.DSpaceCSV;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataDSpaceCsvExportService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implementation of {@link MetadataDSpaceCsvExportService}
*/
public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExportService {
@Autowired
private ItemService itemService;
@Override
public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String handle,
DSpaceRunnableHandler handler) throws Exception {
Iterator<Item> toExport = null;
if (exportAllItems) {
handler.logInfo("Exporting whole repository WARNING: May take some time!");
toExport = itemService.findAll(context);
} else {
DSpaceObject dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, handle);
if (dso == null) {
throw new IllegalArgumentException(
"Item '" + handle + "' does not resolve to an item in your repository!");
}
if (dso.getType() == Constants.ITEM) {
handler.logInfo("Exporting item '" + dso.getName() + "' (" + handle + ")");
List<Item> item = new ArrayList<>();
item.add((Item) dso);
toExport = item.iterator();
} else if (dso.getType() == Constants.COLLECTION) {
handler.logInfo("Exporting collection '" + dso.getName() + "' (" + handle + ")");
Collection collection = (Collection) dso;
toExport = itemService.findByCollection(context, collection);
} else if (dso.getType() == Constants.COMMUNITY) {
handler.logInfo("Exporting community '" + dso.getName() + "' (" + handle + ")");
toExport = buildFromCommunity(context, (Community) dso);
} else {
throw new IllegalArgumentException("Error identifying '" + handle + "'");
}
}
DSpaceCSV csv = this.export(context, toExport, exportAllMetadata);
return csv;
}
@Override
public DSpaceCSV export(Context context, Iterator<Item> toExport, boolean exportAll) throws Exception {
Context.Mode originalMode = context.getCurrentMode();
context.setMode(Context.Mode.READ_ONLY);
// Process each item
DSpaceCSV csv = new DSpaceCSV(exportAll);
while (toExport.hasNext()) {
Item item = toExport.next();
csv.addItem(item);
context.uncacheEntity(item);
}
context.setMode(originalMode);
// Return the results
return csv;
}
@Override
public DSpaceCSV export(Context context, Community community, boolean exportAll) throws Exception {
return export(context, buildFromCommunity(context, community), exportAll);
}
/**
* Build an array list of item ids that are in a community (include sub-communities and collections)
*
* @param context DSpace context
* @param community The community to build from
* @return The list of item ids
* @throws SQLException if database error
*/
private Iterator<Item> buildFromCommunity(Context context, Community community)
throws SQLException {
// Add all the collections
List<Collection> collections = community.getCollections();
Iterator<Item> result = null;
for (Collection collection : collections) {
Iterator<Item> items = itemService.findByCollection(context, collection);
result = addItemsToResult(result, items);
}
// Add all the sub-communities
List<Community> communities = community.getSubcommunities();
for (Community subCommunity : communities) {
Iterator<Item> items = buildFromCommunity(context, subCommunity);
result = addItemsToResult(result, items);
}
return result;
}
private Iterator<Item> addItemsToResult(Iterator<Item> result, Iterator<Item> items) {
if (result == null) {
result = items;
} else {
result = Iterators.concat(result, items);
}
return result;
}
}

View File

@@ -10,7 +10,6 @@ package org.dspace.content;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;

View File

@@ -8,7 +8,6 @@
package org.dspace.content.dao.impl;
import java.sql.SQLException;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@@ -21,7 +20,7 @@ import org.dspace.scripts.Process;
import org.dspace.scripts.Process_;
/**
*
*
* Implementation class for {@link ProcessDAO}
*/
public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements ProcessDAO {

View File

@@ -13,7 +13,6 @@ import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import org.dspace.authorize.AuthorizeException;
@@ -46,7 +45,7 @@ public interface BitstreamService extends DSpaceObjectService<Bitstream>, DSpace
* checksum algorithm as same as the given bitstream.
* This allows multiple bitstreams to share the same internal identifier of assets .
* An example of such a use case scenario is versioning.
*
*
* @param context
* DSpace context object
* @param bitstream

View File

@@ -20,8 +20,10 @@ import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.dspace.eperson.Group;
/**
* Service interface class for the Collection object.
* The implementation of this class is responsible for all business logic calls for the Collection object and is
@@ -339,4 +341,57 @@ public interface CollectionService
* @throws SQLException if database error
*/
List<Map.Entry<Collection, Long>> getCollectionsWithBitstreamSizesTotal(Context context) throws SQLException;
/**
* This method will create a default read group for the given Collection. It'll create either a defaultItemRead or
* a defaultBitstreamRead group depending on the given parameters
*
* @param context The relevant DSpace context
* @param collection The collection for which it'll be created
* @param typeOfGroupString The type of group to be made, item or bitstream
* @param defaultRead The defaultRead int, item or bitstream
* @return The created Group
* @throws SQLException If something goes wrong
* @throws AuthorizeException If something goes wrong
*/
Group createDefaultReadGroup(Context context, Collection collection, String typeOfGroupString, int defaultRead)
throws SQLException, AuthorizeException;
/**
* Returns Collections for which the current user has 'submit' privileges.
* NOTE: for better performance, this method retrieves its results from an
* index (cache) and does not query the database directly.
* This means that results may be stale or outdated until DS-4524 is resolved"
*
* @param q limit the returned collection to those with metadata values matching the query terms.
* The terms are used to make also a prefix query on SOLR so it can be used to implement
* an autosuggest feature over the collection name
* @param context DSpace Context
* @param community parent community
* @param offset the position of the first result to return
* @param limit paging limit
* @return discovery search result objects
* @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
public List<Collection> findCollectionsWithSubmit(String q, Context context, Community community,
int offset, int limit) throws SQLException, SearchServiceException;
/**
* Counts the number of Collection for which the current user has 'submit' privileges.
* NOTE: for better performance, this method retrieves its results from an index (cache)
* and does not query the database directly.
* This means that results may be stale or outdated until DS-4524 is resolved."
*
* @param q limit the returned collection to those with metadata values matching the query terms.
* The terms are used to make also a prefix query on SOLR so it can be used to implement
* an autosuggest feature over the collection name
* @param context DSpace Context
* @param community parent community
* @return total collections found
* @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
public int countCollectionsWithSubmit(String q, Context context, Community community)
throws SQLException, SearchServiceException;
}

View File

@@ -0,0 +1,58 @@
/**
* 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.util.Iterator;
import org.dspace.app.bulkedit.DSpaceCSV;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
/**
* This is the interface to be implemented by a Service that deals with the exporting of Metadata
*/
public interface MetadataDSpaceCsvExportService {
/**
* This method will export DSpaceObject objects depending on the parameters it gets. It can export all the items
* in the repository, all the items in a community, all the items in a collection or a specific item. The latter
* three are specified by the handle parameter. The entire repository can be exported by defining the
* exportAllItems parameter as true
* @param context The relevant DSpace context
* @param exportAllItems A boolean indicating whether or not the entire repository should be exported
* @param exportAllMetadata Defines if all metadata should be exported or only the allowed ones
* @param handle The handle for the DSpaceObject to be exported, can be a Community, Collection or Item
* @return A DSpaceCSV object containing the exported information
* @throws Exception If something goes wrong
*/
public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata,
String handle, DSpaceRunnableHandler dSpaceRunnableHandler) throws Exception;
/**
* This method will export all the Items in the given toExport iterator to a DSpaceCSV
* @param context The relevant DSpace context
* @param toExport The iterator containing the items to export
* @param exportAll Defines if all metadata should be exported or only the allowed ones
* @return A DSpaceCSV object containing the exported information
* @throws Exception If something goes wrong
*/
public DSpaceCSV export(Context context, Iterator<Item> toExport, boolean exportAll) throws Exception;
/**
* This method will export all the Items within the given Community to a DSpaceCSV
* @param context The relevant DSpace context
* @param community The Community that contains the Items to be exported
* @param exportAll Defines if all metadata should be exported or only the allowed ones
* @return A DSpaceCSV object containing the exported information
* @throws Exception If something goes wrong
*/
public DSpaceCSV export(Context context, Community community, boolean exportAll) throws Exception;
}

View File

@@ -58,6 +58,11 @@ public class Context implements AutoCloseable {
*/
private EPerson currentUser;
/**
* Temporary store when the current user is temporary switched
*/
private EPerson currentUserPreviousState;
/**
* Current Locale
*/
@@ -89,6 +94,11 @@ public class Context implements AutoCloseable {
*/
private List<UUID> specialGroups;
/**
* Temporary store for the specialGroups when the current user is temporary switched
*/
private List<UUID> specialGroupsPreviousState;
/**
* Content events
*/
@@ -169,7 +179,7 @@ public class Context implements AutoCloseable {
}
currentUser = null;
currentLocale = I18nUtil.DEFAULTLOCALE;
currentLocale = I18nUtil.getDefaultLocale();
extraLogInfo = "";
ignoreAuth = false;
@@ -628,6 +638,42 @@ public class Context implements AutoCloseable {
return myGroups;
}
/**
* Temporary change the user bound to the context, empty the special groups that
* are retained to allow subsequent restore
*
* @param newUser the EPerson to bound to the context
*
* @throws IllegalStateException if the switch was already performed without be
* restored
*/
public void switchContextUser(EPerson newUser) {
if (currentUserPreviousState != null) {
throw new IllegalStateException(
"A previous user is already set, you can only switch back and foreward one time");
}
currentUserPreviousState = currentUser;
specialGroupsPreviousState = specialGroups;
specialGroups = new ArrayList<UUID>();
currentUser = newUser;
}
/**
* Restore the user bound to the context and his special groups
*
* @throws IllegalStateException if no switch was performed before
*/
public void restoreContextUser() {
if (specialGroupsPreviousState == null) {
throw new IllegalStateException("No previous state found");
}
currentUser = currentUserPreviousState;
specialGroups = specialGroupsPreviousState;
specialGroupsPreviousState = null;
currentUserPreviousState = null;
}
/**
* Close the context, aborting any open transactions (if any).
* @throws Throwable
@@ -830,4 +876,5 @@ public class Context implements AutoCloseable {
private void reloadContextBoundEntities() throws SQLException {
currentUser = reloadEntity(currentUser);
}
}

View File

@@ -37,9 +37,6 @@ import org.dspace.services.factory.DSpaceServicesFactory;
public class I18nUtil {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(I18nUtil.class);
// the default Locale of this DSpace Instance
public static final Locale DEFAULTLOCALE = getDefaultLocale();
// delimiters between elements of UNIX/POSIX locale spec, e.g. en_US.UTF-8
private static final String LOCALE_DELIMITERS = " _.";
@@ -127,7 +124,7 @@ public class I18nUtil {
return parseLocales(locales);
} else {
Locale[] availableLocales = new Locale[1];
availableLocales[0] = DEFAULTLOCALE;
availableLocales[0] = getDefaultLocale();
return availableLocales;
}
}
@@ -148,7 +145,7 @@ public class I18nUtil {
Locale supportedLocale = null;
String testLocale = "";
if (availableLocales == null) {
supportedLocale = DEFAULTLOCALE;
supportedLocale = getDefaultLocale();
} else {
if (!locale.getVariant().equals("")) {
testLocale = locale.toString();
@@ -188,7 +185,7 @@ public class I18nUtil {
}
}
if (!isSupported) {
supportedLocale = DEFAULTLOCALE;
supportedLocale = getDefaultLocale();
}
}
return supportedLocale;
@@ -220,7 +217,7 @@ public class I18nUtil {
* String of the message
*/
public static String getMessage(String key) {
return getMessage(key.trim(), DEFAULTLOCALE);
return getMessage(key.trim(), getDefaultLocale());
}
/**
@@ -233,7 +230,7 @@ public class I18nUtil {
*/
public static String getMessage(String key, Locale locale) {
if (locale == null) {
locale = DEFAULTLOCALE;
locale = getDefaultLocale();
}
ResourceBundle.Control control =
ResourceBundle.Control.getNoFallbackControl(
@@ -384,4 +381,23 @@ public class I18nUtil {
}
return resultList.toArray(new Locale[resultList.size()]);
}
/**
* Check if the input locale is in the list of supported locales
* @param locale
* @return true if locale is supported, false otherwise
*/
public static boolean isSupportedLocale(Locale locale) {
boolean isSupported = false;
Locale[] supportedLocales = getSupportedLocales();
if (supportedLocales != null) {
for (Locale sLocale: supportedLocales) {
if (locale.getLanguage().equals(sLocale.getLanguage()) ) {
isSupported = true;
break;
}
}
}
return isSupported;
}
}

View File

@@ -13,8 +13,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.rmi.dgc.VMID;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -414,6 +416,23 @@ public final class Utils {
}
}
/**
* Retrieve the baseurl from a given URL string
* @param urlString URL string
* @return baseurl (without any context path) or null (if URL was invalid)
*/
public static String getBaseUrl(String urlString) {
try {
URL url = new URL(urlString);
String baseUrl = url.getProtocol() + "://" + url.getHost();
if (url.getPort() != -1) {
baseUrl += (":" + url.getPort());
}
return baseUrl;
} catch (MalformedURLException e) {
return null;
}
}
/**
* Retrieve the hostname from a given URI string

View File

@@ -14,7 +14,6 @@ import java.util.Optional;
import java.util.UUID;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
@@ -30,17 +29,18 @@ import org.dspace.discovery.indexobject.factory.IndexFactory;
import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.scripts.DSpaceRunnable;
import org.springframework.beans.factory.annotation.Autowired;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.utils.DSpace;
/**
* Class used to reindex dspace communities/collections/items into discovery
*/
public class IndexClient extends DSpaceRunnable {
public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguration> {
private Context context;
@Autowired
private IndexingService indexer;
private IndexingService indexer = DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName(IndexingService.class.getName(),
IndexingService.class);
private IndexClientOptions indexClientOptions;
@@ -144,6 +144,12 @@ public class IndexClient extends DSpaceRunnable {
handler.logInfo("Done with indexing");
}
@Override
public IndexDiscoveryScriptConfiguration getScriptConfiguration() {
return new DSpace().getServiceManager().getServiceByName("index-discovery",
IndexDiscoveryScriptConfiguration.class);
}
public void setup() throws ParseException {
try {
context = new Context(Context.Mode.READ_ONLY);
@@ -151,18 +157,8 @@ public class IndexClient extends DSpaceRunnable {
} catch (Exception e) {
throw new ParseException("Unable to create a new DSpace Context: " + e.getMessage());
}
indexClientOptions = IndexClientOptions.getIndexClientOption(commandLine);
}
/**
* Constructor for this class. This will ensure that the Options are created and set appropriately.
*/
private IndexClient() {
Options options = IndexClientOptions.constructOptions();
this.options = options;
}
/**
* Indexes the given object and all children, if applicable.
*

View File

@@ -0,0 +1,58 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import java.sql.SQLException;
import org.apache.commons.cli.Options;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The {@link ScriptConfiguration} for the {@link IndexClient} script
*/
public class IndexDiscoveryScriptConfiguration<T extends IndexClient> extends ScriptConfiguration<T> {
@Autowired
private AuthorizeService authorizeService;
private Class<T> dspaceRunnableClass;
@Override
public Class<T> getDspaceRunnableClass() {
return dspaceRunnableClass;
}
@Override
public boolean isAllowedToExecute(Context context) {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e);
}
}
@Override
public Options getOptions() {
if (options == null) {
super.options = IndexClientOptions.constructOptions();
}
return options;
}
/**
* Generic setter for the dspaceRunnableClass
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this IndexDiscoveryScriptConfiguration
*/
@Override
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
this.dspaceRunnableClass = dspaceRunnableClass;
}
}

View File

@@ -21,7 +21,7 @@ import org.dspace.core.ReloadableEntity;
* @param <PK>
* the Class of the primary key
*/
public interface IndexableObject<T extends ReloadableEntity, PK extends Serializable> {
public interface IndexableObject<T extends ReloadableEntity<PK>, PK extends Serializable> {
/**
*

View File

@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
@@ -69,7 +68,6 @@ import org.dspace.discovery.indexobject.IndexableCommunity;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.discovery.indexobject.factory.IndexFactory;
import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.GroupService;
@@ -100,16 +98,6 @@ import org.springframework.stereotype.Service;
@Service
public class SolrServiceImpl implements SearchService, IndexingService {
/**
* The name of the discover configuration used to search for workflow tasks in the mydspace
*/
public static final String DISCOVER_WORKFLOW_CONFIGURATION_NAME = "workflow";
/**
* The name of the discover configuration used to search for inprogress submission in the mydspace
*/
public static final String DISCOVER_WORKSPACE_CONFIGURATION_NAME = "workspace";
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SolrServiceImpl.class);
@Autowired
@@ -848,46 +836,9 @@ public class SolrServiceImpl implements SearchService, IndexingService {
}
boolean isWorkspace = StringUtils.startsWith(discoveryQuery.getDiscoveryConfigurationName(),
DISCOVER_WORKSPACE_CONFIGURATION_NAME);
boolean isWorkflow = StringUtils.startsWith(discoveryQuery.getDiscoveryConfigurationName(),
DISCOVER_WORKFLOW_CONFIGURATION_NAME);
EPerson currentUser = context.getCurrentUser();
// extra security check to avoid the possibility that an anonymous user
// get access to workspace or workflow
if (currentUser == null && (isWorkflow || isWorkspace)) {
throw new IllegalStateException("An anonymous user cannot perform a workspace or workflow search");
}
if (isWorkspace) {
// insert filter by submitter
solrQuery
.addFilterQuery("submitter_authority:(" + currentUser.getID() + ")");
} else if (isWorkflow) {
// Retrieve all the groups the current user is a member of !
Set<Group> groups;
try {
groups = groupService.allMemberGroupsSet(context, currentUser);
} catch (SQLException e) {
throw new org.dspace.discovery.SearchServiceException(e.getMessage(), e);
}
// insert filter by controllers
StringBuilder controllerQuery = new StringBuilder();
controllerQuery.append("taskfor:(e" + currentUser.getID());
for (Group group : groups) {
controllerQuery.append(" OR g").append(group.getID());
}
controllerQuery.append(")");
solrQuery.addFilterQuery(controllerQuery.toString());
}
//Add any configured search plugins !
List<SolrServiceSearchPlugin> solrServiceSearchPlugins = DSpaceServicesFactory.getInstance().getServiceManager()
.getServicesByType(
SolrServiceSearchPlugin
.class);
List<SolrServiceSearchPlugin> solrServiceSearchPlugins = DSpaceServicesFactory.getInstance()
.getServiceManager().getServicesByType(SolrServiceSearchPlugin.class);
for (SolrServiceSearchPlugin searchPlugin : solrServiceSearchPlugins) {
searchPlugin.additionalSearchParameters(context, discoveryQuery, solrQuery);
}

View File

@@ -0,0 +1,76 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import java.sql.SQLException;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The purpose of this plugin is to index all ADD type resource policies related to collections.
*
* @author Mykhaylo Boychuk (at 4science.it)
*/
public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIndexPlugin {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(SolrServiceIndexCollectionSubmittersPlugin.class);
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Override
public void additionalIndex(Context context, IndexableObject idxObj, SolrInputDocument document) {
if (idxObj instanceof IndexableCollection) {
Collection col = ((IndexableCollection) idxObj).getIndexedObject();
if (col != null) {
try {
String fieldValue = null;
Community parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(col)
.getParentObject(context, col);
while (parent != null) {
if (parent.getAdministrators() != null) {
fieldValue = "g" + parent.getAdministrators().getID();
document.addField("submit", fieldValue);
}
parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(parent)
.getParentObject(context, parent);
}
List<ResourcePolicy> policies = authorizeService.getPoliciesActionFilter(context, col,
Constants.ADD);
for (ResourcePolicy resourcePolicy : policies) {
if (resourcePolicy.getGroup() != null) {
fieldValue = "g" + resourcePolicy.getGroup().getID();
} else {
fieldValue = "e" + resourcePolicy.getEPerson().getID();
}
document.addField("submit", fieldValue);
context.uncacheEntity(resourcePolicy);
}
} catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while indexing resource policies",
"Collection: (id " + col.getID() + " type " + col.getName() + ")" ));
}
}
}
}
}

View File

@@ -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.discovery;
import java.sql.SQLException;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Context;
import org.dspace.discovery.indexobject.IndexableDSpaceObject;
/**
* Indexes the UUID of the parent object for any Community, Collection and Item
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
public class SolrServiceParentObjectIndexingPlugin implements SolrServiceIndexPlugin {
@Override
public void additionalIndex(Context context, IndexableObject idxObj, SolrInputDocument document) {
try {
if (idxObj instanceof IndexableDSpaceObject) {
DSpaceObject dso = ((IndexableDSpaceObject) idxObj).getIndexedObject();
if (dso instanceof Community || dso instanceof Collection || dso instanceof Item) {
DSpaceObject parent = ContentServiceFactory.getInstance().getDSpaceObjectService(dso)
.getParentObject(context, dso);
if (parent != null) {
document.addField("location.parent", parent.getID().toString());
}
}
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -11,24 +11,33 @@ import java.sql.SQLException;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
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.Context;
import org.dspace.core.LogManager;
import org.dspace.discovery.indexobject.IndexableClaimedTask;
import org.dspace.discovery.indexobject.IndexableDSpaceObject;
import org.dspace.discovery.indexobject.IndexableInProgressSubmission;
import org.dspace.discovery.indexobject.IndexablePoolTask;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -57,8 +66,21 @@ public class SolrServiceResourceRestrictionPlugin implements SolrServiceIndexPlu
@Override
public void additionalIndex(Context context, IndexableObject idxObj, SolrInputDocument document) {
DSpaceObject dso = null;
if (idxObj instanceof IndexableDSpaceObject) {
DSpaceObject dso = ((IndexableDSpaceObject) idxObj).getIndexedObject();
dso = ((IndexableDSpaceObject) idxObj).getIndexedObject();
} else if (idxObj instanceof IndexableInProgressSubmission) {
final InProgressSubmission inProgressSubmission
= ((IndexableInProgressSubmission) idxObj).getIndexedObject();
dso = inProgressSubmission.getItem();
} else if (idxObj instanceof IndexablePoolTask) {
final PoolTask poolTask = ((IndexablePoolTask) idxObj).getIndexedObject();
dso = poolTask.getWorkflowItem().getItem();
} else if (idxObj instanceof IndexableClaimedTask) {
final ClaimedTask claimedTask = ((IndexableClaimedTask) idxObj).getIndexedObject();
dso = claimedTask.getWorkflowItem().getItem();
}
if (dso != null) {
try {
List<ResourcePolicy> policies = authorizeService.getPoliciesActionFilter(context, dso, Constants.READ);
for (ResourcePolicy resourcePolicy : policies) {
@@ -77,9 +99,33 @@ public class SolrServiceResourceRestrictionPlugin implements SolrServiceIndexPlu
//remove the policy from the cache to save memory
context.uncacheEntity(resourcePolicy);
}
// also index ADMIN policies as ADMIN permissions provides READ access
// going up through the hierarchy for communities, collections and items
while (dso != null) {
if (dso instanceof Community || dso instanceof Collection || dso instanceof Item) {
List<ResourcePolicy> policiesAdmin = authorizeService
.getPoliciesActionFilter(context, dso, Constants.ADMIN);
for (ResourcePolicy resourcePolicy : policiesAdmin) {
String fieldValue;
if (resourcePolicy.getGroup() != null) {
// We have a group add it to the value
fieldValue = "g" + resourcePolicy.getGroup().getID();
} else {
// We have an eperson add it to the value
fieldValue = "e" + resourcePolicy.getEPerson().getID();
}
document.addField("read", fieldValue);
// remove the policy from the cache to save memory
context.uncacheEntity(resourcePolicy);
}
}
dso = ContentServiceFactory.getInstance().getDSpaceObjectService(dso).getParentObject(context, dso);
}
} catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while indexing resource policies",
"DSpace object: (id " + dso.getID() + " type " + dso.getType() + ")"));
"DSpace object: (id " + dso.getID() + " type " + dso.getType() + ")"
));
}
}
}
@@ -87,13 +133,6 @@ public class SolrServiceResourceRestrictionPlugin implements SolrServiceIndexPlu
@Override
public void additionalSearchParameters(Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery) {
try {
// skip workspace and workflow queries as security for it them is builtin in the SolrServiceImpl
if (StringUtils.startsWith(discoveryQuery.getDiscoveryConfigurationName(),
SolrServiceImpl.DISCOVER_WORKSPACE_CONFIGURATION_NAME)
|| StringUtils.startsWith(discoveryQuery.getDiscoveryConfigurationName(),
SolrServiceImpl.DISCOVER_WORKFLOW_CONFIGURATION_NAME)) {
return;
}
if (!authorizeService.isAdmin(context)) {
StringBuilder resourceQuery = new StringBuilder();
//Always add the anonymous group id to the query

View File

@@ -19,5 +19,14 @@ import org.dspace.core.Context;
*/
public interface SolrServiceSearchPlugin {
public void additionalSearchParameters(Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery);
/**
* Edits the solr query before it is sent to solr by adding additional parameters to it.
*
* @param context The DSpace Context object.
* @param discoveryQuery The discovery query object on which the solr query is based.
* @param solrQuery The query that will be sent to solr and which may be edited by this plugin.
* @throws SearchServiceException Any checked exception that might happen in this plugin
*/
public void additionalSearchParameters(Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery)
throws SearchServiceException;
}

View File

@@ -0,0 +1,101 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import java.sql.SQLException;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Plugin to restrict or grant access to workspace and workflow items
* based on the discovery configuration used.
*/
public class SolrServiceWorkspaceWorkflowRestrictionPlugin implements SolrServiceSearchPlugin {
/**
* The name of the discover configuration used to search for inprogress submission in the mydspace
*/
public static final String DISCOVER_WORKSPACE_CONFIGURATION_NAME = "workspace";
/**
* The name of the discover configuration used to search for workflow tasks in the mydspace
*/
public static final String DISCOVER_WORKFLOW_CONFIGURATION_NAME = "workflow";
/**
* The name of the discover configuration used by administrators to search for workflow tasks
*/
public static final String DISCOVER_WORKFLOW_ADMIN_CONFIGURATION_NAME = "workflowAdmin";
@Autowired(required = true)
protected GroupService groupService;
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Override
public void additionalSearchParameters(
Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery
) throws SearchServiceException {
boolean isWorkspace = StringUtils.startsWith(
discoveryQuery.getDiscoveryConfigurationName(),
DISCOVER_WORKSPACE_CONFIGURATION_NAME
);
boolean isWorkflow = StringUtils.startsWith(
discoveryQuery.getDiscoveryConfigurationName(),
DISCOVER_WORKFLOW_CONFIGURATION_NAME
);
boolean isWorkflowAdmin = isAdmin(context)
&& DISCOVER_WORKFLOW_ADMIN_CONFIGURATION_NAME.equals(discoveryQuery.getDiscoveryConfigurationName());
EPerson currentUser = context.getCurrentUser();
// extra security check to avoid the possibility that an anonymous user
// get access to workspace or workflow
if (currentUser == null && (isWorkflow || isWorkspace)) {
throw new IllegalStateException(
"An anonymous user cannot perform a workspace or workflow search");
}
if (isWorkspace) {
// insert filter by submitter
solrQuery.addFilterQuery("submitter_authority:(" + currentUser.getID() + ")");
} else if (isWorkflow && !isWorkflowAdmin) {
// Retrieve all the groups the current user is a member of !
Set<Group> groups;
try {
groups = groupService.allMemberGroupsSet(context, currentUser);
} catch (SQLException e) {
throw new SearchServiceException(e.getMessage(), e);
}
// insert filter by controllers
StringBuilder controllerQuery = new StringBuilder();
controllerQuery.append("taskfor:(e").append(currentUser.getID());
for (Group group : groups) {
controllerQuery.append(" OR g").append(group.getID());
}
controllerQuery.append(")");
solrQuery.addFilterQuery(controllerQuery.toString());
}
}
private boolean isAdmin(Context context) throws SearchServiceException {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
throw new SearchServiceException(e.getMessage(), e);
}
}
}

View File

@@ -38,7 +38,7 @@ public interface CitationDocumentService {
* Citation enabled globally (all citable bitstreams will get "watermarked") modules/disseminate-citation:
* enable_globally
* OR
* The container is this object is whitelist enabled.
* The container is this object is "allow list" enabled.
* - community: modules/disseminate-citation: enabled_communities
* - collection: modules/disseminate-citation: enabled_collections
* AND

View File

@@ -12,6 +12,7 @@ import java.sql.SQLException;
import java.util.Locale;
import javax.mail.MessagingException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.ConfigurationManager;
@@ -22,6 +23,7 @@ import org.dspace.core.Utils;
import org.dspace.eperson.service.AccountService;
import org.dspace.eperson.service.EPersonService;
import org.dspace.eperson.service.RegistrationDataService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -47,6 +49,8 @@ public class AccountServiceImpl implements AccountService {
protected EPersonService ePersonService;
@Autowired(required = true)
protected RegistrationDataService registrationDataService;
@Autowired
private ConfigurationService configurationService;
protected AccountServiceImpl() {
@@ -67,6 +71,9 @@ public class AccountServiceImpl implements AccountService {
public void sendRegistrationInfo(Context context, String email)
throws SQLException, IOException, MessagingException,
AuthorizeException {
if (!configurationService.getBooleanProperty("user.registration", true)) {
throw new IllegalStateException("The user.registration parameter was set to false");
}
sendInfo(context, email, true, true);
}
@@ -155,6 +162,14 @@ public class AccountServiceImpl implements AccountService {
registrationDataService.deleteByToken(context, token);
}
@Override
public boolean verifyPasswordStructure(String password) {
if (StringUtils.length(password) < 6) {
return false;
}
return true;
}
/**
* THIS IS AN INTERNAL METHOD. THE SEND PARAMETER ALLOWS IT TO BE USED FOR
* TESTING PURPOSES.
@@ -233,8 +248,8 @@ public class AccountServiceImpl implements AccountService {
// Note change from "key=" to "token="
String specialLink = new StringBuffer().append(base).append(
base.endsWith("/") ? "" : "/").append(
isRegister ? "register" : "forgot").append("?")
.append("token=").append(rd.getToken())
isRegister ? "register" : "forgot").append("/")
.append(rd.getToken())
.toString();
Locale locale = context.getCurrentLocale();
Email bean = Email.getEmail(I18nUtil.getEmailFilename(locale, isRegister ? "register"

View File

@@ -23,7 +23,9 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.DSpaceObject;
import org.dspace.content.DSpaceObjectServiceImpl;
@@ -76,6 +78,8 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Autowired(required = true)
protected ResourcePolicyService resourcePolicyService;
protected GroupServiceImpl() {
super();
@@ -185,7 +189,8 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
return false;
// special, everyone is member of group 0 (anonymous)
} else if (StringUtils.equals(group.getName(), Group.ANONYMOUS)) {
} else if (StringUtils.equals(group.getName(), Group.ANONYMOUS) ||
isParentOf(context, group, findByName(context, Group.ANONYMOUS))) {
return true;
} else {
@@ -654,6 +659,23 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
return collectionService.getParentObject(context, collection);
}
}
} else {
if (AuthorizeConfiguration.canCollectionAdminManagePolicies()
|| AuthorizeConfiguration.canCommunityAdminManagePolicies()
|| AuthorizeConfiguration.canCommunityAdminManageCollectionWorkflows()) {
List<Group> groups = new ArrayList<Group>();
groups.add(group);
List<ResourcePolicy> policies = resourcePolicyService.find(context, null, groups,
Constants.DEFAULT_ITEM_READ, Constants.COLLECTION);
if (policies.size() > 0) {
return policies.get(0).getdSpaceObject();
}
policies = resourcePolicyService.find(context, null, groups,
Constants.DEFAULT_BITSTREAM_READ, Constants.COLLECTION);
if (policies.size() > 0) {
return policies.get(0).getdSpaceObject();
}
}
}
}
if (AuthorizeConfiguration.canCommunityAdminManageAdminGroup()) {

View File

@@ -46,4 +46,11 @@ public interface AccountService {
public void deleteToken(Context context, String token)
throws SQLException;
/**
* This method verifies that a certain String adheres to the password rules for DSpace
* @param password The String to be checked
* @return A boolean indicating whether or not the given String adheres to the password rules
*/
public boolean verifyPasswordStructure(String password);
}

View File

@@ -21,7 +21,6 @@ import net.handle.hdllib.HandleStorage;
import net.handle.hdllib.HandleValue;
import net.handle.hdllib.ScanCallback;
import net.handle.hdllib.Util;
import org.apache.logging.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory;

View File

@@ -79,7 +79,7 @@ public class SimpleXpathMetadatumContributor implements MetadataContributor<OMEl
* @param query query string
* @param prefixToNamespaceMapping metadata prefix to namespace mapping
* @param field
* <a href="https://github.com/DSpace/DSpace/tree/master/dspace-api/src/main/java/org/dspace/importer/external#metadata-mapping-">MetadataFieldConfig</a>
* <a href="https://github.com/DSpace/DSpace/tree/main/dspace-api/src/main/java/org/dspace/importer/external#metadata-mapping-">MetadataFieldConfig</a>
*/
public SimpleXpathMetadatumContributor(String query, Map<String, String> prefixToNamespaceMapping,
MetadataFieldConfig field) {

View File

@@ -8,6 +8,8 @@
package org.dspace.license;
import java.util.List;
/**
* @author wbossons
*/
@@ -15,17 +17,17 @@ public class CCLicense {
private String licenseName;
private String licenseId;
private int order = 0;
private List<CCLicenseField> ccLicenseFieldList;
public CCLicense() {
super();
}
public CCLicense(String licenseId, String licenseName, int order) {
public CCLicense(String licenseId, String licenseName, List<CCLicenseField> ccLicenseFieldList) {
super();
this.licenseId = licenseId;
this.licenseName = licenseName;
this.order = order;
this.ccLicenseFieldList = ccLicenseFieldList;
}
public String getLicenseName() {
@@ -44,13 +46,19 @@ public class CCLicense {
this.licenseId = licenseId;
}
public int getOrder() {
return this.order;
/**
* Gets the list of CC License Fields
* @return the list of CC License Fields
*/
public List<CCLicenseField> getCcLicenseFieldList() {
return ccLicenseFieldList;
}
public void setOrder(int order) {
this.order = order;
/**
* Sets the list of CC License Fields
* @param ccLicenseFieldList
*/
public void setCcLicenseFieldList(final List<CCLicenseField> ccLicenseFieldList) {
this.ccLicenseFieldList = ccLicenseFieldList;
}
}

View File

@@ -0,0 +1,60 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.license;
import java.io.IOException;
import java.util.Map;
import org.jdom.Document;
/**
* Service interface class for the Creative commons license connector service.
* The implementation of this class is responsible for all the calls to the CC license API and parsing the response
* The service is autowired by spring
*/
public interface CCLicenseConnectorService {
/**
* Retrieves the CC Licenses for the provided language from the CC License API
*
* @param language - the language to retrieve the licenses for
* @return a map of licenses with the id and the license for the provided language
*/
public Map<String, CCLicense> retrieveLicenses(String language);
/**
* Retrieve the CC License URI based on the provided license id, language and answers to the field questions from
* the CC License API
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the CC License URI
*/
public String retrieveRightsByQuestion(String licenseId,
String language,
Map<String, String> answerMap);
/**
* Retrieve the license RDF document based on the license URI
*
* @param licenseURI - The license URI for which to retrieve the license RDF document
* @return the license RDF document
* @throws IOException
*/
public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException;
/**
* Retrieve the license Name from the license document
*
* @param doc - The license document from which to retrieve the license name
* @return the license name
*/
public String retrieveLicenseName(final Document doc);
}

View File

@@ -0,0 +1,371 @@
/**
* 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.license;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.services.ConfigurationService;
import org.jaxen.JaxenException;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.xml.sax.InputSource;
/**
* Implementation for the Creative commons license connector service.
* This class is responsible for all the calls to the CC license API and parsing the response
*/
public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, InitializingBean {
private Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLicenseConnectorServiceImpl.class);
private CloseableHttpClient client;
protected SAXBuilder parser = new SAXBuilder();
private String postArgument = "answers";
private String postAnswerFormat =
"<answers> " +
"<locale>{1}</locale>" +
"<license-{0}>" +
"{2}" +
"</license-{0}>" +
"</answers>";
@Autowired
private ConfigurationService configurationService;
@Override
public void afterPropertiesSet() throws Exception {
HttpClientBuilder builder = HttpClientBuilder.create();
client = builder
.disableAutomaticRetries()
.setMaxConnTotal(5)
.build();
}
/**
* Retrieves the CC Licenses for the provided language from the CC License API
*
* @param language - the language to retrieve the licenses for
* @return a map of licenses with the id and the license for the provided language
*/
public Map<String, CCLicense> retrieveLicenses(String language) {
String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl");
String uri = ccLicenseUrl + "/?locale=" + language;
HttpGet httpGet = new HttpGet(uri);
List<String> licenses;
try (CloseableHttpResponse response = client.execute(httpGet)) {
licenses = retrieveLicenses(response);
} catch (JDOMException | JaxenException | IOException e) {
log.error("Error while retrieving the license details using url: " + uri, e);
licenses = Collections.emptyList();
}
Map<String, CCLicense> ccLicenses = new HashMap<>();
for (String license : licenses) {
String licenseUri = ccLicenseUrl + "/license/" + license;
HttpGet licenseHttpGet = new HttpGet(licenseUri);
try (CloseableHttpResponse response = client.execute(licenseHttpGet)) {
CCLicense ccLicense = retrieveLicenseObject(license, response);
ccLicenses.put(ccLicense.getLicenseId(), ccLicense);
} catch (JaxenException | JDOMException | IOException e) {
log.error("Error while retrieving the license details using url: " + licenseUri, e);
}
}
return ccLicenses;
}
/**
* Retrieve the list of licenses from the response from the CC License API and remove the licenses configured
* to be excluded
*
* @param response The response from the API
* @return a list of license identifiers for which details need to be retrieved
* @throws IOException
* @throws JaxenException
* @throws JDOMException
*/
private List<String> retrieveLicenses(CloseableHttpResponse response)
throws IOException, JaxenException, JDOMException {
List<String> domains = new LinkedList<>();
String[] excludedLicenses = configurationService.getArrayProperty("cc.license.classfilter");
String responseString = EntityUtils.toString(response.getEntity());
JDOMXPath licenseClassXpath = new JDOMXPath("//licenses/license");
try (StringReader stringReader = new StringReader(responseString)) {
InputSource is = new InputSource(stringReader);
org.jdom.Document classDoc = this.parser.build(is);
List<Element> elements = licenseClassXpath.selectNodes(classDoc);
for (Element element : elements) {
String licenseId = getSingleNodeValue(element, "@id");
if (StringUtils.isNotBlank(licenseId) && !ArrayUtils.contains(excludedLicenses, licenseId)) {
domains.add(licenseId);
}
}
}
return domains;
}
/**
* Parse the response for a single CC License and return the corresponding CC License Object
*
* @param licenseId the license id of the CC License to retrieve
* @param response for a specific CC License response
* @return the corresponding CC License Object
* @throws IOException
* @throws JaxenException
* @throws JDOMException
*/
private CCLicense retrieveLicenseObject(final String licenseId, CloseableHttpResponse response)
throws IOException, JaxenException, JDOMException {
String responseString = EntityUtils.toString(response.getEntity());
JDOMXPath licenseClassXpath = new JDOMXPath("//licenseclass");
JDOMXPath licenseFieldXpath = new JDOMXPath("field");
try (StringReader stringReader = new StringReader(responseString)) {
InputSource is = new InputSource(stringReader);
org.jdom.Document classDoc = this.parser.build(is);
Object element = licenseClassXpath.selectSingleNode(classDoc);
String licenseLabel = getSingleNodeValue(element, "label");
List<CCLicenseField> ccLicenseFields = new LinkedList<>();
List<Element> licenseFields = licenseFieldXpath.selectNodes(element);
for (Element licenseField : licenseFields) {
CCLicenseField ccLicenseField = parseLicenseField(licenseField);
ccLicenseFields.add(ccLicenseField);
}
return new CCLicense(licenseId, licenseLabel, ccLicenseFields);
}
}
private CCLicenseField parseLicenseField(final Element licenseField) throws JaxenException {
String id = getSingleNodeValue(licenseField, "@id");
String label = getSingleNodeValue(licenseField, "label");
String description = getSingleNodeValue(licenseField, "description");
JDOMXPath enumXpath = new JDOMXPath("enum");
List<Element> enums = enumXpath.selectNodes(licenseField);
List<CCLicenseFieldEnum> ccLicenseFieldEnumList = new LinkedList<>();
for (Element enumElement : enums) {
CCLicenseFieldEnum ccLicenseFieldEnum = parseEnum(enumElement);
ccLicenseFieldEnumList.add(ccLicenseFieldEnum);
}
return new CCLicenseField(id, label, description, ccLicenseFieldEnumList);
}
private CCLicenseFieldEnum parseEnum(final Element enumElement) throws JaxenException {
String id = getSingleNodeValue(enumElement, "@id");
String label = getSingleNodeValue(enumElement, "label");
String description = getSingleNodeValue(enumElement, "description");
return new CCLicenseFieldEnum(id, label, description);
}
private String getNodeValue(final Object el) {
if (el instanceof Element) {
return ((Element) el).getValue();
} else if (el instanceof Attribute) {
return ((Attribute) el).getValue();
} else if (el instanceof String) {
return (String) el;
} else {
return null;
}
}
private String getSingleNodeValue(final Object t, String query) throws JaxenException {
JDOMXPath xpath = new JDOMXPath(query);
Object singleNode = xpath.selectSingleNode(t);
return getNodeValue(singleNode);
}
/**
* Retrieve the CC License URI based on the provided license id, language and answers to the field questions from
* the CC License API
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the CC License URI
*/
public String retrieveRightsByQuestion(String licenseId,
String language,
Map<String, String> answerMap) {
String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl");
HttpPost httpPost = new HttpPost(ccLicenseUrl + "/license/" + licenseId + "/issue");
String answers = createAnswerString(answerMap);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
String text = MessageFormat.format(postAnswerFormat, licenseId, language, answers);
builder.addTextBody(postArgument, text);
HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
try (CloseableHttpResponse response = client.execute(httpPost)) {
return retrieveLicenseUri(response);
} catch (JDOMException | JaxenException | IOException e) {
log.error("Error while retrieving the license uri for license : " + licenseId + " with answers "
+ answerMap.toString(), e);
}
return null;
}
/**
* Parse the response for the CC License URI request and return the corresponding CC License URI
*
* @param response for a specific CC License URI response
* @return the corresponding CC License URI as a string
* @throws IOException
* @throws JaxenException
* @throws JDOMException
*/
private String retrieveLicenseUri(final CloseableHttpResponse response)
throws IOException, JaxenException, JDOMException {
String responseString = EntityUtils.toString(response.getEntity());
JDOMXPath licenseClassXpath = new JDOMXPath("//result/license-uri");
try (StringReader stringReader = new StringReader(responseString)) {
InputSource is = new InputSource(stringReader);
org.jdom.Document classDoc = this.parser.build(is);
Object node = licenseClassXpath.selectSingleNode(classDoc);
String nodeValue = getNodeValue(node);
if (StringUtils.isNotBlank(nodeValue)) {
return nodeValue;
}
}
return null;
}
private String createAnswerString(final Map<String, String> parameterMap) {
StringBuilder sb = new StringBuilder();
for (String key : parameterMap.keySet()) {
sb.append("<");
sb.append(key);
sb.append(">");
sb.append(parameterMap.get(key));
sb.append("</");
sb.append(key);
sb.append(">");
}
return sb.toString();
}
/**
* Retrieve the license RDF document based on the license URI
*
* @param licenseURI - The license URI for which to retrieve the license RDF document
* @return the license RDF document
* @throws IOException
*/
@Override
public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException {
String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl");
String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI;
URL request_url;
try {
request_url = new URL(issueUrl);
} catch (MalformedURLException e) {
return null;
}
URLConnection connection = request_url.openConnection();
connection.setDoOutput(true);
try {
// parsing document from input stream
InputStream stream = connection.getInputStream();
Document doc = parser.build(stream);
return doc;
} catch (Exception e) {
log.error("Error while retrieving the license document for URI: " + licenseURI, e);
}
return null;
}
/**
* Retrieve the license Name from the license document
*
* @param doc - The license document from which to retrieve the license name
* @return the license name
*/
public String retrieveLicenseName(final Document doc) {
try {
return getSingleNodeValue(doc, "//result/license-name");
} catch (JaxenException e) {
log.error("Error while retrieving the license name from the license document", e);
}
return null;
}
}

View File

@@ -7,8 +7,7 @@
*/
package org.dspace.license;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
/**
* Wrapper class for representation of a license field declaration.
@@ -22,7 +21,7 @@ public class CCLicenseField {
private String description = "";
private String type = "";
private HashMap fieldEnum = null;
private List<CCLicenseFieldEnum> fieldEnum = null;
/**
* Construct a new LicenseField class. Note that after construction,
@@ -31,13 +30,11 @@ public class CCLicenseField {
* @param id The unique identifier for this field; this value will be used in constructing the answers XML.
* @param label The label to use when generating the user interface.
*/
public CCLicenseField(String id, String label) {
super();
this.fieldEnum = new HashMap();
public CCLicenseField(String id, String label, String description, List<CCLicenseFieldEnum> fieldEnum) {
this.id = id;
this.label = label;
this.description = description;
this.fieldEnum = fieldEnum;
}
/**
@@ -90,16 +87,12 @@ public class CCLicenseField {
}
/**
* @return Returns an instance implementing the Map interface;
* the instance contains a mapping from identifiers to
* labels for the enumeration values.
* @see Map
* Returns the list of enums of this field
* @return the list of enums of this field
*/
public Map<String, String> getEnum() {
return this.fieldEnum;
public List<CCLicenseFieldEnum> getFieldEnum() {
return fieldEnum;
}
}

View File

@@ -0,0 +1,82 @@
/**
* 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.license;
import org.apache.commons.lang3.StringUtils;
/**
* Wrapper class for representation of a license field enum declaration.
* A field enum is a single "answer" to the field question
*/
public class CCLicenseFieldEnum {
private String id = "";
private String label = "";
private String description = "";
public CCLicenseFieldEnum(String id, String label, String description) {
if (StringUtils.isNotBlank(id)) {
this.id = id;
}
if (StringUtils.isNotBlank(label)) {
this.label = label;
}
if (StringUtils.isNotBlank(description)) {
this.description = description;
}
}
/**
* Get the id of this enum
* @return the id of this enum
*/
public String getId() {
return id;
}
/**
* Set the id of this enum
* @param id
*/
public void setId(final String id) {
this.id = id;
}
/**
* Get the label of this enum
* @return the label of this enum
*/
public String getLabel() {
return label;
}
/**
* Set the label of this enum
* @param label
*/
public void setLabel(final String label) {
this.label = label;
}
/**
* Get the description of this enum
* @return the description of this enum
*/
public String getDescription() {
return description;
}
/**
* Set the description of this enum
* @param description
*/
public void setDescription(final String description) {
this.description = description;
}
}

View File

@@ -1,435 +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.license;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.logging.log4j.Logger;
import org.dspace.license.factory.LicenseServiceFactory;
import org.dspace.license.service.CreativeCommonsService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.jaxen.JaxenException;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
/**
* A wrapper around Creative Commons REST web services.
*
* @author Wendy Bossons
*/
public class CCLookup {
/**
* log4j logger
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLookup.class);
private String cc_root;
private String jurisdiction;
private List<String> lcFilter = new ArrayList<String>();
private Document license_doc = null;
private String rdfString = null;
private String errorMessage = null;
private boolean success = false;
private SAXBuilder parser = new SAXBuilder();
private List<CCLicense> licenses = new ArrayList<CCLicense>();
private List<CCLicenseField> licenseFields = new ArrayList<CCLicenseField>();
protected CreativeCommonsService creativeCommonsService = LicenseServiceFactory.getInstance()
.getCreativeCommonsService();
/**
* Constructs a new instance with the default web services root.
*/
public CCLookup() {
super();
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
cc_root = configurationService.getProperty("cc.api.rooturl");
String jurisProp = configurationService.getProperty("cc.license.jurisdiction");
jurisdiction = (jurisProp != null) ? jurisProp : "";
String[] filters = configurationService.getArrayProperty("cc.license.classfilter");
if (filters != null) {
for (String name : filters) {
lcFilter.add(name.trim());
}
}
}
/**
* Returns the id for a particular CCLicense label. Returns an
* empty string if no match is found.
*
* @param class_label The CCLicense label to find.
* @return Returns a String containing the License class ID if the label
* is found; if not found, returns an empty string.
* @see CCLicense
*/
public String getLicenseId(String class_label) {
for (int i = 0; i < this.licenses.size(); i++) {
if (((CCLicense) this.licenses.get(i)).getLicenseName().equals(class_label)) {
return ((CCLicense) this.licenses.get(i)).getLicenseId();
}
}
return "";
}
/**
* Queries the web service for the available licenses.
*
* @param language The language to request labels and description strings in.
* @return Returns a Map of CCLicense objects.
* @see Map
* @see CCLicense
*/
public Collection<CCLicense> getLicenses(String language) {
// create XPath expressions
try {
JDOMXPath xp_Licenses = new JDOMXPath("//licenses/license");
JDOMXPath xp_LicenseID = new JDOMXPath("@id");
URL classUrl = new URL(this.cc_root + "/?locale=" + language);
Document classDoc = this.parser.build(classUrl);
// extract the identifiers and labels using XPath
List<Element> results = xp_Licenses.selectNodes(classDoc);
// populate licenses container
this.licenses.clear();
for (int i = 0; i < results.size(); i++) {
Element license = results.get(i);
// add if not filtered
String liD = ((Attribute) xp_LicenseID.selectSingleNode(license)).getValue();
if (!lcFilter.contains(liD)) {
this.licenses.add(new CCLicense(liD, license.getText(), i));
}
}
} catch (JaxenException jaxen_e) {
return null;
} catch (JDOMException jdom_e) {
return null;
} catch (IOException io_e) {
return null;
} catch (Exception e) {
// do nothing... but we should
return null;
}
return licenses;
}
/**
* Queries the web service for a set of licenseFields for a particular license class.
*
* @param license A String specifying the CCLicense identifier to
* retrieve fields for.
* @param language the locale string
* @return A Collection of LicenseField objects.
* @see CCLicense
*/
public Collection<CCLicenseField> getLicenseFields(String license, String language) {
JDOMXPath xp_LicenseField;
JDOMXPath xp_LicenseID;
JDOMXPath xp_FieldType;
JDOMXPath xp_Description;
JDOMXPath xp_Label;
JDOMXPath xp_Enum;
Document fieldDoc;
URL classUrl;
List results = null;
List enumOptions = null;
// create XPath expressions
try {
xp_LicenseField = new JDOMXPath("//field");
xp_LicenseID = new JDOMXPath("@id");
xp_Description = new JDOMXPath("description");
xp_Label = new JDOMXPath("label");
xp_FieldType = new JDOMXPath("type");
xp_Enum = new JDOMXPath("enum");
} catch (JaxenException e) {
return null;
}
// retrieve and parse the license class document
try {
classUrl = new URL(this.cc_root + "/license/" + license + "?locale=" + language);
} catch (Exception err) {
// do nothing... but we should
return null;
}
// parse the licenses document
try {
fieldDoc = this.parser.build(classUrl);
} catch (JDOMException e) {
return null;
} catch (IOException e) {
return null;
}
// reset the field definition container
this.licenseFields.clear();
// extract the identifiers and labels using XPath
try {
results = xp_LicenseField.selectNodes(fieldDoc);
} catch (JaxenException e) {
return null;
}
for (int i = 0; i < results.size(); i++) {
Element field = (Element) results.get(i);
try {
// create the field object
CCLicenseField cclicensefield = new CCLicenseField(
((Attribute) xp_LicenseID.selectSingleNode(field)).getValue(),
((Element) xp_Label.selectSingleNode(field)).getText());
// extract additional properties
cclicensefield.setDescription(((Element) xp_Description.selectSingleNode(field)).getText());
cclicensefield.setType(((Element) xp_FieldType.selectSingleNode(field)).getText());
enumOptions = xp_Enum.selectNodes(field);
for (int j = 0; j < enumOptions.size(); j++) {
String id = ((Attribute) xp_LicenseID.selectSingleNode(enumOptions.get(j))).getValue();
String label = ((Element) xp_Label.selectSingleNode(enumOptions.get(j))).getText();
cclicensefield.getEnum().put(id, label);
} // for each enum option
this.licenseFields.add(cclicensefield);
} catch (JaxenException e) {
return null;
}
}
return licenseFields;
} // licenseFields
/**
* Passes a set of "answers" to the web service and retrieves a license.
*
* @param licenseId The identifier of the license class being requested.
* @param answers A Map containing the answers to the license fields;
* each key is the identifier of a LicenseField, with the value
* containing the user-supplied answer.
* @param lang The language to request localized elements in.
* @throws IOException if IO error
* @see CCLicense
* @see Map
*/
public void issue(String licenseId, Map answers, String lang)
throws IOException {
// Determine the issue URL
String issueUrl = this.cc_root + "/license/" + licenseId + "/issue";
// Assemble the "answers" document
String answer_doc = "<answers>\n<locale>" + lang + "</locale>\n" + "<license-" + licenseId + ">\n";
Iterator keys = answers.keySet().iterator();
try {
String current = (String) keys.next();
while (true) {
answer_doc += "<" + current + ">" + (String) answers.get(current) + "</" + current + ">\n";
current = (String) keys.next();
}
} catch (NoSuchElementException e) {
// exception indicates we've iterated through the
// entire collection; just swallow and continue
}
// answer_doc += "<jurisdiction></jurisidiction>\n"; FAILS with jurisdiction argument
answer_doc += "</license-" + licenseId + ">\n</answers>\n";
String post_data;
try {
post_data = URLEncoder.encode("answers", "UTF-8") + "=" + URLEncoder.encode(answer_doc, "UTF-8");
} catch (UnsupportedEncodingException e) {
return;
}
URL post_url;
try {
post_url = new URL(issueUrl);
} catch (MalformedURLException e) {
return;
}
URLConnection connection = post_url.openConnection();
// this will not be needed after I'm done TODO: remove
connection.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(post_data);
writer.flush();
// end TODO
try {
// parsing document from input stream
java.io.InputStream stream = connection.getInputStream();
this.license_doc = this.parser.build(stream);
} catch (JDOMException jde) {
log.warn(jde.getMessage());
} catch (Exception e) {
log.warn(e.getCause());
}
return;
} // issue
/**
* Passes a set of "answers" to the web service and retrieves a license.
*
* @param licenseURI The uri of the license.
*
* Note: does not support localization in 1.5 -- not yet
* @throws IOException if IO error
* @see CCLicense
* @see Map
*/
public void issue(String licenseURI)
throws IOException {
// Determine the issue URL
// Example: http://api.creativecommons.org/rest/1.5/details?
// license-uri=http://creativecommons.org/licenses/by-nc-sa/3.0/
String issueUrl = cc_root + "/details?license-uri=" + licenseURI;
URL request_url;
try {
request_url = new URL(issueUrl);
} catch (MalformedURLException e) {
return;
}
URLConnection connection = request_url.openConnection();
// this will not be needed after I'm done TODO: remove
connection.setDoOutput(true);
try {
// parsing document from input stream
java.io.InputStream stream = connection.getInputStream();
license_doc = this.parser.build(stream);
} catch (JDOMException jde) {
log.warn(jde.getMessage());
} catch (Exception e) {
log.warn(e.getCause());
}
return;
} // issue
/**
* Retrieves the URI for the license issued.
*
* @return A String containing the URI for the license issued.
*/
public String getLicenseUrl() {
String text = null;
try {
JDOMXPath xp_LicenseName = new JDOMXPath("//result/license-uri");
text = ((Element) xp_LicenseName.selectSingleNode(this.license_doc)).getText();
} catch (Exception e) {
log.warn(e.getMessage());
setSuccess(false);
text = "An error occurred getting the license - uri.";
} finally {
return text;
}
} // getLicenseUrl
/**
* Retrieves the human readable name for the license issued.
*
* @return A String containing the license name.
*/
public String getLicenseName() {
String text = null;
try {
JDOMXPath xp_LicenseName = new JDOMXPath("//result/license-name");
text = ((Element) xp_LicenseName.selectSingleNode(this.license_doc)).getText();
} catch (Exception e) {
log.warn(e.getMessage());
setSuccess(false);
text = "An error occurred on the license name.";
} finally {
return text;
}
} // getLicenseName
public org.jdom.Document getLicenseDocument() {
return this.license_doc;
}
public String getRdf()
throws IOException {
String result = "";
try {
result = creativeCommonsService.fetchLicenseRDF(license_doc);
} catch (Exception e) {
log.warn("An error occurred getting the rdf . . ." + e.getMessage());
setSuccess(false);
}
return result;
}
public boolean isSuccess() {
setSuccess(false);
JDOMXPath xp_Success;
String text = null;
try {
xp_Success = new JDOMXPath("//message");
text = ((Element) xp_Success.selectSingleNode(this.license_doc)).getText();
setErrorMessage(text);
} catch (Exception e) {
log.warn("There was an issue . . . " + text);
setSuccess(true);
}
return this.success;
}
private void setSuccess(boolean success) {
this.success = success;
}
public String getErrorMessage() {
return this.errorMessage;
}
private void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}

View File

@@ -13,7 +13,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
@@ -82,9 +85,18 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
protected BundleService bundleService;
@Autowired(required = true)
protected ItemService itemService;
@Autowired
protected CCLicenseConnectorService ccLicenseConnectorService;
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private String defaultLanguage;
private String jurisdiction;
private static final String JURISDICTION_KEY = "jurisdiction";
private Map<String, Map<String, CCLicense>> ccLicenses;
protected CreativeCommonsServiceImpl() {
}
@@ -101,10 +113,14 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
System.setProperty("http.proxyPort", proxyPort);
}
ccLicenses = new HashMap<>();
defaultLanguage = configurationService.getProperty("cc.license.locale", "en");
jurisdiction = configurationService.getProperty("cc.license.jurisdiction", "");
try {
templates = TransformerFactory.newInstance().newTemplates(
new StreamSource(CreativeCommonsServiceImpl.class
.getResourceAsStream("CreativeCommons.xsl")));
new StreamSource(CreativeCommonsServiceImpl.class
.getResourceAsStream("CreativeCommons.xsl")));
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
}
@@ -112,15 +128,10 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
}
@Override
public boolean isEnabled() {
return true;
}
// create the CC bundle if it doesn't exist
// If it does, remove it and create a new one.
protected Bundle getCcBundle(Context context, Item item)
throws SQLException, AuthorizeException, IOException {
throws SQLException, AuthorizeException, IOException {
List<Bundle> bundles = itemService.getBundles(item, CC_BUNDLE_NAME);
if ((bundles.size() > 0) && (bundles.get(0) != null)) {
@@ -131,8 +142,8 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
@Override
public void setLicenseRDF(Context context, Item item, String licenseRdf)
throws SQLException, IOException,
AuthorizeException {
throws SQLException, IOException,
AuthorizeException {
Bundle bundle = getCcBundle(context, item);
// set the format
BitstreamFormat bs_rdf_format = bitstreamFormatService.findByShortDescription(context, "RDF XML");
@@ -144,7 +155,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
@Override
public void setLicense(Context context, Item item,
InputStream licenseStm, String mimeType)
throws SQLException, IOException, AuthorizeException {
throws SQLException, IOException, AuthorizeException {
Bundle bundle = getCcBundle(context, item);
// set the format
@@ -160,17 +171,26 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
Bitstream bs = bitstreamService.create(context, bundle, licenseStm);
bs.setSource(context, CC_BS_SOURCE);
bs.setName(context, (mimeType != null &&
(mimeType.equalsIgnoreCase("text/xml") ||
mimeType.equalsIgnoreCase("text/rdf"))) ?
BSN_LICENSE_RDF : BSN_LICENSE_TEXT);
(mimeType.equalsIgnoreCase("text/xml") ||
mimeType.equalsIgnoreCase("text/rdf"))) ?
BSN_LICENSE_RDF : BSN_LICENSE_TEXT);
bs.setFormat(context, bs_format);
bitstreamService.update(context, bs);
}
/**
* Removes the license file from the item
*
* @param context - The relevant DSpace Context
* @param item - The item from which the license file needs to be removed
* @throws SQLException
* @throws IOException
* @throws AuthorizeException
*/
@Override
public void removeLicense(Context context, Item item)
throws SQLException, IOException, AuthorizeException {
public void removeLicenseFile(Context context, Item item)
throws SQLException, IOException, AuthorizeException {
// remove CC license bundle if one exists
List<Bundle> bundles = itemService.getBundles(item, CC_BUNDLE_NAME);
@@ -179,66 +199,74 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
}
}
@Override
public boolean hasLicense(Context context, Item item)
throws SQLException, IOException {
// try to find CC license bundle
List<Bundle> bundles = itemService.getBundles(item, CC_BUNDLE_NAME);
if (bundles.size() == 0) {
return false;
}
// verify it has correct contents
try {
if ((getLicenseURL(context, item) == null)) {
return false;
}
} catch (AuthorizeException ae) {
return false;
}
return true;
}
@Override
public String getLicenseRDF(Context context, Item item) throws SQLException,
IOException, AuthorizeException {
return getStringFromBitstream(context, item, BSN_LICENSE_RDF);
}
@Override
public Bitstream getLicenseRdfBitstream(Item item) throws SQLException,
IOException, AuthorizeException {
IOException, AuthorizeException {
return getBitstream(item, BSN_LICENSE_RDF);
}
@Deprecated
@Override
public Bitstream getLicenseTextBitstream(Item item) throws SQLException,
IOException, AuthorizeException {
IOException, AuthorizeException {
return getBitstream(item, BSN_LICENSE_TEXT);
}
@Override
public String getLicenseURL(Context context, Item item) throws SQLException, IOException, AuthorizeException {
String licenseUri = getCCField("uri").ccItemValue(item);
String licenseUri = getCCField("uri");
if (StringUtils.isNotBlank(licenseUri)) {
return licenseUri;
return getLicenseURI(item);
}
// JSPUI backward compatibility see https://jira.duraspace.org/browse/DS-2604
return getStringFromBitstream(context, item, BSN_LICENSE_URL);
}
/**
* Returns the stored license uri of the item
*
* @param item - The item for which to retrieve the stored license uri
* @return the stored license uri of the item
*/
@Override
public String getLicenseURI(Item item) {
String licenseUriField = getCCField("uri");
if (StringUtils.isNotBlank(licenseUriField)) {
String metadata = itemService.getMetadata(item, licenseUriField);
if (StringUtils.isNotBlank(metadata)) {
return metadata;
}
}
return null;
}
/**
* Returns the stored license name of the item
*
* @param item - The item for which to retrieve the stored license name
* @return the stored license name of the item
*/
@Override
public String getLicenseName( Item item) {
String licenseNameField = getCCField("name");
if (StringUtils.isNotBlank(licenseNameField)) {
String metadata = itemService.getMetadata(item, licenseNameField);
if (StringUtils.isNotBlank(metadata)) {
return metadata;
}
}
return null;
}
@Override
public String fetchLicenseRDF(Document license) {
StringWriter result = new StringWriter();
try {
templates.newTransformer().transform(
new JDOMSource(license),
new StreamResult(result)
new JDOMSource(license),
new StreamResult(result)
);
} catch (TransformerException e) {
throw new IllegalStateException(e.getMessage(), e);
@@ -267,7 +295,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
*/
protected void setBitstreamFromBytes(Context context, Item item, Bundle bundle,
String bitstream_name, BitstreamFormat format, byte[] bytes)
throws SQLException, IOException, AuthorizeException {
throws SQLException, IOException, AuthorizeException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Bitstream bs = bitstreamService.create(context, bundle, bais);
@@ -297,7 +325,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
*/
protected String getStringFromBitstream(Context context, Item item,
String bitstream_name) throws SQLException, IOException,
AuthorizeException {
AuthorizeException {
byte[] bytes = getBytesFromBitstream(context, item, bitstream_name);
if (bytes == null) {
@@ -320,7 +348,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
* to perform a particular action.
*/
protected Bitstream getBitstream(Item item, String bitstream_name)
throws SQLException, IOException, AuthorizeException {
throws SQLException, IOException, AuthorizeException {
Bundle cc_bundle = null;
// look for the CC bundle
@@ -342,7 +370,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
}
protected byte[] getBytesFromBitstream(Context context, Item item, String bitstream_name)
throws SQLException, IOException, AuthorizeException {
throws SQLException, IOException, AuthorizeException {
Bitstream bs = getBitstream(item, bitstream_name);
// no such bitstream
@@ -361,26 +389,322 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
* Returns a metadata field handle for given field Id
*/
@Override
public LicenseMetadataValue getCCField(String fieldId) {
return new LicenseMetadataValue(configurationService.getProperty("cc.license." + fieldId));
public String getCCField(String fieldId) {
return configurationService.getProperty("cc.license." + fieldId);
}
/**
* Remove license information, delete also the bitstream
*
* @param context - DSpace Context
* @param item - the item
* @throws AuthorizeException Exception indicating the current user of the context does not have permission
* to perform a particular action.
* @throws IOException A general class of exceptions produced by failed or interrupted I/O operations.
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
@Override
public void removeLicense(Context context, LicenseMetadataValue uriField,
LicenseMetadataValue nameField, Item item)
throws AuthorizeException, IOException, SQLException {
public void removeLicense(Context context, Item item)
throws AuthorizeException, IOException, SQLException {
String uriField = getCCField("uri");
String nameField = getCCField("name");
String licenseUri = itemService.getMetadata(item, uriField);
// only remove any previous licenses
String licenseUri = uriField.ccItemValue(item);
if (licenseUri != null) {
uriField.removeItemValue(context, item, licenseUri);
removeLicenseField(context, item, uriField);
if (configurationService.getBooleanProperty("cc.submit.setname")) {
String licenseName = nameField.keyedItemValue(item, licenseUri);
nameField.removeItemValue(context, item, licenseName);
removeLicenseField(context, item, nameField);
}
if (configurationService.getBooleanProperty("cc.submit.addbitstream")) {
removeLicense(context, item);
removeLicenseFile(context, item);
}
}
}
private void removeLicenseField(Context context, Item item, String field) throws SQLException {
String[] params = splitField(field);
itemService.clearMetadata(context, item, params[0], params[1], params[2], params[3]);
}
private void addLicenseField(Context context, Item item, String field, String value) throws SQLException {
String[] params = splitField(field);
itemService.addMetadata(context, item, params[0], params[1], params[2], params[3], value);
}
/**
* Find all CC Licenses using the default language found in the configuration
*
* @return A list of available CC Licenses
*/
@Override
public List<CCLicense> findAllCCLicenses() {
return findAllCCLicenses(defaultLanguage);
}
/**
* Find all CC Licenses for the provided language
*
* @param language - the language for which to find the CC Licenses
* @return A list of available CC Licenses for the provided language
*/
@Override
public List<CCLicense> findAllCCLicenses(String language) {
if (!ccLicenses.containsKey(language)) {
initLicenses(language);
}
return new LinkedList<>(ccLicenses.get(language).values());
}
/**
* Find the CC License corresponding to the provided ID using the default language found in the configuration
*
* @param id - the ID of the license to be found
* @return the corresponding license if found or null when not found
*/
@Override
public CCLicense findOne(String id) {
return findOne(id, defaultLanguage);
}
/**
* Find the CC License corresponding to the provided ID and provided language
*
* @param id - the ID of the license to be found
* @param language - the language for which to find the CC License
* @return the corresponding license if found or null when not found
*/
@Override
public CCLicense findOne(String id, String language) {
if (!ccLicenses.containsKey(language)) {
initLicenses(language);
}
Map<String, CCLicense> licenseMap = ccLicenses.get(language);
if (licenseMap.containsKey(id)) {
return licenseMap.get(id);
}
return null;
}
/**
* Retrieves the licenses for a specific language and cache them in this service
*
* @param language - the language for which to find the CC Licenses
*/
private void initLicenses(final String language) {
Map<String, CCLicense> licenseMap = ccLicenseConnectorService.retrieveLicenses(language);
ccLicenses.put(language, licenseMap);
}
/**
* Retrieve the CC License URI for the provided license ID, based on the provided answers, using the default
* language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
@Override
public String retrieveLicenseUri(String licenseId, Map<String, String> answerMap) {
return retrieveLicenseUri(licenseId, defaultLanguage, answerMap);
}
/**
* Retrieve the CC License URI for the provided license ID and language based on the provided answers
*
* @param licenseId - the ID of the license
* @param language - the language for which to find the CC License URI
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
@Override
public String retrieveLicenseUri(String licenseId, String language, Map<String, String> answerMap) {
return ccLicenseConnectorService.retrieveRightsByQuestion(licenseId, language, answerMap);
}
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the default language found in the config to check the license
*
* @param licenseId - the ID of the license
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
@Override
public boolean verifyLicenseInformation(String licenseId, Map<String, String> fullAnswerMap) {
return verifyLicenseInformation(licenseId, defaultLanguage, fullAnswerMap);
}
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the provided language to check the license
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
@Override
public boolean verifyLicenseInformation(String licenseId, String language, Map<String, String> fullAnswerMap) {
CCLicense ccLicense = findOne(licenseId, language);
List<CCLicenseField> ccLicenseFieldList = ccLicense.getCcLicenseFieldList();
for (String field : fullAnswerMap.keySet()) {
CCLicenseField ccLicenseField = findCCLicenseField(field, ccLicenseFieldList);
if (ccLicenseField == null) {
return false;
}
if (!containsAnswerEnum(fullAnswerMap.get(field), ccLicenseField)) {
return false;
}
}
return true;
}
/**
* Retrieve the full answer map containing empty values when an answer for a field was not provided in the
* answerMap, using the default language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer
*/
@Override
public Map<String, String> retrieveFullAnswerMap(String licenseId, Map<String, String> answerMap) {
return retrieveFullAnswerMap(licenseId, defaultLanguage, answerMap);
}
/**
* Retrieve the full answer map for a provided language, containing empty values when an answer for a field was not
* provided in the answerMap.
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer for the provided language
*/
@Override
public Map<String, String> retrieveFullAnswerMap(String licenseId, String language, Map<String, String> answerMap) {
CCLicense ccLicense = findOne(licenseId, language);
if (ccLicense == null) {
return null;
}
Map<String, String> fullParamMap = new HashMap<>(answerMap);
List<CCLicenseField> ccLicenseFieldList = ccLicense.getCcLicenseFieldList();
for (CCLicenseField ccLicenseField : ccLicenseFieldList) {
if (!fullParamMap.containsKey(ccLicenseField.getId())) {
fullParamMap.put(ccLicenseField.getId(), "");
}
}
updateJurisdiction(fullParamMap);
return fullParamMap;
}
private void updateJurisdiction(final Map<String, String> fullParamMap) {
if (fullParamMap.containsKey(JURISDICTION_KEY)) {
fullParamMap.put(JURISDICTION_KEY, jurisdiction);
}
}
private boolean containsAnswerEnum(final String enumAnswer, final CCLicenseField ccLicenseField) {
List<CCLicenseFieldEnum> fieldEnums = ccLicenseField.getFieldEnum();
for (CCLicenseFieldEnum fieldEnum : fieldEnums) {
if (StringUtils.equals(fieldEnum.getId(), enumAnswer)) {
return true;
}
}
return false;
}
private CCLicenseField findCCLicenseField(final String field, final List<CCLicenseField> ccLicenseFieldList) {
for (CCLicenseField ccLicenseField : ccLicenseFieldList) {
if (StringUtils.equals(ccLicenseField.getId(), field)) {
return ccLicenseField;
}
}
return null;
}
/**
* Update the license of the item with a new one based on the provided license URI
*
* @param context - The relevant DSpace context
* @param licenseUri - The license URI to be used in the update
* @param item - The item for which to update the license
* @return true when the update was successful, false when not
* @throws AuthorizeException
* @throws SQLException
*/
@Override
public boolean updateLicense(final Context context, final String licenseUri, final Item item)
throws AuthorizeException, SQLException {
try {
Document doc = ccLicenseConnectorService.retrieveLicenseRDFDoc(licenseUri);
if (doc == null) {
return false;
}
String licenseName = ccLicenseConnectorService.retrieveLicenseName(doc);
if (StringUtils.isBlank(licenseName)) {
return false;
}
removeLicense(context, item);
addLicense(context, item, licenseUri, licenseName, doc);
return true;
} catch (IOException e) {
log.error("Error while updating the license of item: " + item.getID(), e);
}
return false;
}
/**
* Add a new license to the item
*
* @param context - The relevant Dspace context
* @param item - The item to which the license will be added
* @param licenseUri - The license URI to add
* @param licenseName - The license name to add
* @param doc - The license to document to add
* @throws SQLException
* @throws IOException
* @throws AuthorizeException
*/
@Override
public void addLicense(Context context, Item item, String licenseUri, String licenseName, Document doc)
throws SQLException, IOException, AuthorizeException {
String uriField = getCCField("uri");
String nameField = getCCField("name");
addLicenseField(context, item, uriField, licenseUri);
if (configurationService.getBooleanProperty("cc.submit.addbitstream")) {
setLicenseRDF(context, item, fetchLicenseRDF(doc));
}
if (configurationService.getBooleanProperty("cc.submit.setname")) {
addLicenseField(context, item, nameField, licenseName);
}
}
private String[] splitField(String fieldName) {
String[] params = new String[4];
String[] fParams = fieldName.split("\\.");
for (int i = 0; i < fParams.length; i++) {
params[i] = fParams[i];
}
params[3] = Item.ANY;
return params;
}
}

View File

@@ -1,129 +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.license;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
/**
* Helper class for using CC-related Metadata fields
*
* @author kevinvandevelde at atmire.com
*/
public class LicenseMetadataValue {
protected final ItemService itemService;
// Shibboleth for Creative Commons license data - i.e. characters that reliably indicate CC in a URI
protected static final String ccShib = "creativecommons";
private String[] params = new String[4];
public LicenseMetadataValue(String fieldName) {
if (fieldName != null && fieldName.length() > 0) {
String[] fParams = fieldName.split("\\.");
for (int i = 0; i < fParams.length; i++) {
params[i] = fParams[i];
}
params[3] = Item.ANY;
}
itemService = ContentServiceFactory.getInstance().getItemService();
}
/**
* Returns first value that matches Creative Commons 'shibboleth',
* or null if no matching values.
* NB: this method will succeed only for metadata fields holding CC URIs
*
* @param item - the item to read
* @return value - the first CC-matched value, or null if no such value
*/
public String ccItemValue(Item item) {
List<MetadataValue> dcvalues = itemService.getMetadata(item, params[0], params[1], params[2], params[3]);
for (MetadataValue dcvalue : dcvalues) {
if ((dcvalue.getValue()).indexOf(ccShib) != -1) {
// return first value that matches the shib
return dcvalue.getValue();
}
}
return null;
}
/**
* Returns the value that matches the value mapped to the passed key if any.
* NB: this only delivers a license name (if present in field) given a license URI
*
* @param item - the item to read
* @param key - the key for desired value
* @return value - the value associated with key or null if no such value
* @throws IOException A general class of exceptions produced by failed or interrupted I/O operations.
* @throws SQLException An exception that provides information on a database access error or other errors.
* @throws AuthorizeException Exception indicating the current user of the context does not have permission
* to perform a particular action.
*/
public String keyedItemValue(Item item, String key)
throws AuthorizeException, IOException, SQLException {
CCLookup ccLookup = new CCLookup();
ccLookup.issue(key);
String matchValue = ccLookup.getLicenseName();
List<MetadataValue> dcvalues = itemService.getMetadata(item, params[0], params[1], params[2], params[3]);
for (MetadataValue dcvalue : dcvalues) {
if (dcvalue.getValue().equals(matchValue)) {
return dcvalue.getValue();
}
}
return null;
}
/**
* Removes the passed value from the set of values for the field in passed item.
*
* @param context The relevant DSpace Context.
* @param item - the item to update
* @param value - the value to remove
* @throws IOException A general class of exceptions produced by failed or interrupted I/O operations.
* @throws SQLException An exception that provides information on a database access error or other errors.
* @throws AuthorizeException Exception indicating the current user of the context does not have permission
* to perform a particular action.
*/
public void removeItemValue(Context context, Item item, String value)
throws AuthorizeException, IOException, SQLException {
if (value != null) {
List<MetadataValue> dcvalues = itemService.getMetadata(item, params[0], params[1], params[2], params[3]);
ArrayList<String> arrayList = new ArrayList<String>();
for (MetadataValue dcvalue : dcvalues) {
if (!dcvalue.getValue().equals(value)) {
arrayList.add(dcvalue.getValue());
}
}
itemService.clearMetadata(context, item, params[0], params[1], params[2], params[3]);
itemService.addMetadata(context, item, params[0], params[1], params[2], params[3], arrayList);
}
}
/**
* Adds passed value to the set of values for the field in passed item.
*
* @param context The relevant DSpace Context.
* @param item - the item to update
* @param value - the value to add in this field
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
public void addItemValue(Context context, Item item, String value) throws SQLException {
itemService.addMetadata(context, item, params[0], params[1], params[2], params[3], value);
}
}

View File

@@ -10,12 +10,14 @@ package org.dspace.license.service;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.license.LicenseMetadataValue;
import org.dspace.license.CCLicense;
import org.jdom.Document;
/**
@@ -29,13 +31,6 @@ public interface CreativeCommonsService {
public static final String CC_BUNDLE_NAME = "CC-LICENSE";
/**
* Simple accessor for enabling of CC
*
* @return is CC enabled?
*/
public boolean isEnabled();
/**
* setLicenseRDF
*
@@ -50,7 +45,7 @@ public interface CreativeCommonsService {
* to perform a particular action.
*/
public void setLicenseRDF(Context context, Item item, String licenseRdf)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
@@ -72,19 +67,40 @@ public interface CreativeCommonsService {
*/
public void setLicense(Context context, Item item,
InputStream licenseStm, String mimeType)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
public void removeLicense(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
/**
* Removes the license file from the item
*
* @param context - The relevant DSpace Context
* @param item - The item from which the license file needs to be removed
* @throws SQLException
* @throws IOException
* @throws AuthorizeException
*/
public void removeLicenseFile(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
public boolean hasLicense(Context context, Item item)
throws SQLException, IOException;
public String getLicenseURL(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
public String getLicenseRDF(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
/**
* Returns the stored license uri of the item
*
* @param item - The item for which to retrieve the stored license uri
* @return the stored license uri of the item
*/
public String getLicenseURI(Item item);
/**
* Returns the stored license name of the item
*
* @param item - The item for which to retrieve the stored license name
* @return the stored license name of the item
*/
public String getLicenseName(Item item);
/**
* Get Creative Commons license RDF, returning Bitstream object.
@@ -97,7 +113,7 @@ public interface CreativeCommonsService {
* to perform a particular action.
*/
public Bitstream getLicenseRdfBitstream(Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
* Get Creative Commons license Text, returning Bitstream object.
@@ -112,7 +128,7 @@ public interface CreativeCommonsService {
* is no longer stored (see https://jira.duraspace.org/browse/DS-2604)
*/
public Bitstream getLicenseTextBitstream(Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
* Get a few license-specific properties. We expect these to be cached at
@@ -121,7 +137,7 @@ public interface CreativeCommonsService {
* @param fieldId name of the property.
* @return its value.
*/
public LicenseMetadataValue getCCField(String fieldId);
public String getCCField(String fieldId);
/**
* Apply same transformation on the document to retrieve only the most
@@ -138,15 +154,134 @@ public interface CreativeCommonsService {
* Remove license information, delete also the bitstream
*
* @param context - DSpace Context
* @param uriField - the metadata field for license uri
* @param nameField - the metadata field for license name
* @param item - the item
* @throws AuthorizeException Exception indicating the current user of the context does not have permission
* to perform a particular action.
* @throws IOException A general class of exceptions produced by failed or interrupted I/O operations.
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
public void removeLicense(Context context, LicenseMetadataValue uriField,
LicenseMetadataValue nameField, Item item)
throws AuthorizeException, IOException, SQLException;
public void removeLicense(Context context, Item item)
throws AuthorizeException, IOException, SQLException;
/**
* Find all CC Licenses using the default language found in the configuration
*
* @return A list of available CC Licenses
*/
public List<CCLicense> findAllCCLicenses();
/**
* Find all CC Licenses for the provided language
*
* @param language - the language for which to find the CC Licenses
* @return A list of available CC Licenses for the provided language
*/
public List<CCLicense> findAllCCLicenses(String language);
/**
* Find the CC License corresponding to the provided ID using the default language found in the configuration
*
* @param id - the ID of the license to be found
* @return the corresponding license if found or null when not found
*/
public CCLicense findOne(String id);
/**
* Find the CC License corresponding to the provided ID and provided language
*
* @param id - the ID of the license to be found
* @param language - the language for which to find the CC License
* @return the corresponding license if found or null when not found
*/
public CCLicense findOne(String id, String language);
/**
* Retrieve the CC License URI for the provided license ID, based on the provided answers, using the default
* language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
public String retrieveLicenseUri(String licenseId, Map<String, String> answerMap);
/**
* Retrieve the CC License URI for the provided license ID and language based on the provided answers
*
* @param licenseId - the ID of the license
* @param language - the language for which to find the CC License URI
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
public String retrieveLicenseUri(String licenseId, String language, Map<String, String> answerMap);
/**
* Retrieve the full answer map containing empty values when an answer for a field was not provided in the
* answerMap, using the default language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer
*/
public Map<String, String> retrieveFullAnswerMap(String licenseId, Map<String, String> answerMap);
/**
* Retrieve the full answer map for a provided language, containing empty values when an answer for a field was not
* provided in the answerMap.
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer for the provided language
*/
public Map<String, String> retrieveFullAnswerMap(String licenseId, String language, Map<String, String> answerMap);
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the default language found in the config to check the license
*
* @param licenseId - the ID of the license
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
public boolean verifyLicenseInformation(String licenseId, Map<String, String> fullAnswerMap);
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the provided language to check the license
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
public boolean verifyLicenseInformation(String licenseId, String language, Map<String, String> fullAnswerMap);
/**
* Update the license of the item with a new one based on the provided license URI
*
* @param context - The relevant DSpace context
* @param licenseUri - The license URI to be used in the update
* @param item - The item for which to update the license
* @return true when the update was successful, false when not
* @throws AuthorizeException
* @throws SQLException
*/
public boolean updateLicense(final Context context, String licenseUri, final Item item)
throws AuthorizeException, SQLException;
/**
* Add a new license to the item
*
* @param context - The relevant Dspace context
* @param item - The item to which the license will be added
* @param licenseUri - The license URI to add
* @param licenseName - The license name to add
* @param doc - The license to document to add
* @throws SQLException
* @throws IOException
* @throws AuthorizeException
*/
public void addLicense(Context context, Item item, String licenseUri, String licenseName, Document doc)
throws SQLException, IOException, AuthorizeException;
}

View File

@@ -7,70 +7,72 @@
*/
package org.dspace.scripts;
import java.sql.SQLException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.apache.commons.lang3.StringUtils;
import org.dspace.eperson.EPerson;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
* This abstract class is the class that should be extended by each script.
* it provides the basic variables to be hold by the script as well as the means to initialize, parse and run the script
* Every DSpaceRunnable that is implemented in this way should be defined in the scripts.xml config file as a bean
* This is the class that should be extended for each Script. This class will contain the logic needed to run and it'll
* fetch the information that it needs from the {@link ScriptConfiguration} provided through the diamond operators.
* This will be the dspaceRunnableClass for the {@link ScriptConfiguration} beans. Specifically created for each
* script
* @param <T>
*/
public abstract class DSpaceRunnable implements Runnable {
public abstract class DSpaceRunnable<T extends ScriptConfiguration> implements Runnable {
/**
* The name of the script
*/
private String name;
/**
* The description of the script
*/
private String description;
/**
* The CommandLine object for the script that'll hold the information
*/
protected CommandLine commandLine;
/**
* The possible options for this script
* This EPerson identifier variable is the uuid of the eperson that's running the script
*/
protected Options options;
private UUID epersonIdentifier;
/**
* The handler that deals with this script. This handler can currently either be a RestDSpaceRunnableHandler or
* a CommandlineDSpaceRunnableHandler depending from where the script is called
*/
protected DSpaceRunnableHandler handler;
@Autowired
private AuthorizeService authorizeService;
/**
* This method will return the Configuration that the implementing DSpaceRunnable uses
* @return The {@link ScriptConfiguration} that this implementing DspaceRunnable uses
*/
public abstract T getScriptConfiguration();
public String getName() {
return name;
private void setHandler(DSpaceRunnableHandler dSpaceRunnableHandler) {
this.handler = dSpaceRunnableHandler;
}
@Required
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
@Required
public void setDescription(String description) {
this.description = description;
}
public Options getOptions() {
return options;
/**
* This method sets the appropriate DSpaceRunnableHandler depending on where it was ran from and it parses
* the arguments given to the script
* @param args The arguments given to the script
* @param dSpaceRunnableHandler The DSpaceRunnableHandler object that defines from where the script was ran
* @param currentUser
* @throws ParseException If something goes wrong
*/
public void initialize(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler,
EPerson currentUser) throws ParseException {
if (currentUser != null) {
this.setEpersonIdentifier(currentUser.getID());
}
this.setHandler(dSpaceRunnableHandler);
this.parse(args);
}
/**
@@ -80,18 +82,16 @@ public abstract class DSpaceRunnable implements Runnable {
* @throws ParseException If something goes wrong
*/
private void parse(String[] args) throws ParseException {
commandLine = new DefaultParser().parse(getOptions(), args);
commandLine = new DefaultParser().parse(getScriptConfiguration().getOptions(), args);
setup();
}
/**
* This method will call upon the {@link DSpaceRunnableHandler#printHelp(Options, String)} method with the script's
* options and name
* This method has to be included in every script and handles the setup of the script by parsing the CommandLine
* and setting the variables
* @throws ParseException If something goes wrong
*/
public void printHelp() {
handler.printHelp(options, name);
}
public abstract void setup() throws ParseException;
/**
* This is the run() method from the Runnable interface that we implement. This method will handle the running
@@ -108,22 +108,6 @@ public abstract class DSpaceRunnable implements Runnable {
}
}
private void setHandler(DSpaceRunnableHandler dSpaceRunnableHandler) {
this.handler = dSpaceRunnableHandler;
}
/**
* This method sets the appropriate DSpaceRunnableHandler depending on where it was ran from and it parses
* the arguments given to the script
* @param args The arguments given to the script
* @param dSpaceRunnableHandler The DSpaceRunnableHandler object that defines from where the script was ran
* @throws ParseException If something goes wrong
*/
public void initialize(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler) throws ParseException {
this.setHandler(dSpaceRunnableHandler);
this.parse(args);
}
/**
* This method has to be included in every script and this will be the main execution block for the script that'll
* contain all the logic needed
@@ -132,25 +116,46 @@ public abstract class DSpaceRunnable implements Runnable {
public abstract void internalRun() throws Exception;
/**
* This method has to be included in every script and handles the setup of the script by parsing the CommandLine
* and setting the variables
* @throws ParseException If something goes wrong
* This method will call upon the {@link DSpaceRunnableHandler#printHelp(Options, String)} method with the script's
* options and name
*/
public abstract void setup() throws ParseException;
public void printHelp() {
handler.printHelp(getScriptConfiguration().getOptions(), getScriptConfiguration().getName());
}
/**
* This method will return if the script is allowed to execute in the given context. This is by default set
* to the currentUser in the context being an admin, however this can be overwritten by each script individually
* if different rules apply
* @param context The relevant DSpace context
* @return A boolean indicating whether the script is allowed to execute or not
* This method will traverse all the options and it'll grab options defined as an InputStream type to then save
* the filename specified by that option in a list of Strings that'll be returned in the end
* @return The list of Strings representing filenames from the options given to the script
*/
public boolean isAllowedToExecute(Context context) {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
handler.logError("Error occured when trying to verify permissions for script: " + name);
public List<String> getFileNamesFromInputStreamOptions() {
List<String> fileNames = new LinkedList<>();
for (Option option : getScriptConfiguration().getOptions().getOptions()) {
if (option.getType() == InputStream.class &&
StringUtils.isNotBlank(commandLine.getOptionValue(option.getOpt()))) {
fileNames.add(commandLine.getOptionValue(option.getOpt()));
}
}
return false;
return fileNames;
}
/**
* Generic getter for the epersonIdentifier
* This EPerson identifier variable is the uuid of the eperson that's running the script
* @return the epersonIdentifier value of this DSpaceRunnable
*/
public UUID getEpersonIdentifier() {
return epersonIdentifier;
}
/**
* Generic setter for the epersonIdentifier
* This EPerson identifier variable is the uuid of the eperson that's running the script
* @param epersonIdentifier The epersonIdentifier to be set on this DSpaceRunnable
*/
public void setEpersonIdentifier(UUID epersonIdentifier) {
this.epersonIdentifier = epersonIdentifier;
}
}

View File

@@ -8,6 +8,7 @@
package org.dspace.scripts;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -80,6 +81,8 @@ public class Process implements ReloadableEntity<Integer> {
@Temporal(TemporalType.TIMESTAMP)
private Date creationTime;
public static final String BITSTREAM_TYPE_METADATAFIELD = "dspace.process.filetype";
protected Process() {
}
@@ -174,6 +177,9 @@ public class Process implements ReloadableEntity<Integer> {
* @return The Bitstreams that are used or created by the process
*/
public List<Bitstream> getBitstreams() {
if (bitstreams == null) {
bitstreams = new LinkedList<>();
}
return bitstreams;
}

View File

@@ -7,18 +7,33 @@
*/
package org.dspace.scripts;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataValue;
import org.dspace.content.ProcessStatus;
import org.dspace.content.dao.ProcessDAO;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson;
@@ -35,6 +50,18 @@ public class ProcessServiceImpl implements ProcessService {
@Autowired
private ProcessDAO processDAO;
@Autowired
private BitstreamService bitstreamService;
@Autowired
private BitstreamFormatService bitstreamFormatService;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private MetadataFieldService metadataFieldService;
@Override
public Process create(Context context, EPerson ePerson, String scriptName,
List<DSpaceCommandLineParameter> parameters) throws SQLException {
@@ -113,11 +140,35 @@ public class ProcessServiceImpl implements ProcessService {
}
@Override
public void delete(Context context, Process process) throws SQLException {
public void appendFile(Context context, Process process, InputStream is, String type, String fileName)
throws IOException, SQLException, AuthorizeException {
Bitstream bitstream = bitstreamService.create(context, is);
if (getBitstream(context, process, type) != null) {
throw new IllegalArgumentException("Cannot create another file of type: " + type + " for this process" +
" with id: " + process.getID());
}
bitstream.setName(context, fileName);
bitstreamService.setFormat(context, bitstream, bitstreamFormatService.guessFormat(context, bitstream));
MetadataField dspaceProcessFileTypeField = metadataFieldService
.findByString(context, Process.BITSTREAM_TYPE_METADATAFIELD, '.');
bitstreamService.addMetadata(context, bitstream, dspaceProcessFileTypeField, null, type);
authorizeService.addPolicy(context, bitstream, Constants.READ, context.getCurrentUser());
authorizeService.addPolicy(context, bitstream, Constants.WRITE, context.getCurrentUser());
authorizeService.addPolicy(context, bitstream, Constants.DELETE, context.getCurrentUser());
bitstreamService.update(context, bitstream);
process.addBitstream(bitstream);
update(context, process);
}
@Override
public void delete(Context context, Process process) throws SQLException, IOException, AuthorizeException {
for (Bitstream bitstream : ListUtils.emptyIfNull(process.getBitstreams())) {
bitstreamService.delete(context, bitstream);
}
processDAO.delete(context, process);
log.info(LogManager.getHeader(context, "process_delete", "Process with ID " + process.getID()
+ " and name " + process.getName() + " has been deleted"));
}
@Override
@@ -141,8 +192,57 @@ public class ProcessServiceImpl implements ProcessService {
return parameterList;
}
@Override
public Bitstream getBitstreamByName(Context context, Process process, String bitstreamName) {
for (Bitstream bitstream : getBitstreams(context, process)) {
if (StringUtils.equals(bitstream.getName(), bitstreamName)) {
return bitstream;
}
}
return null;
}
@Override
public Bitstream getBitstream(Context context, Process process, String type) {
List<Bitstream> allBitstreams = process.getBitstreams();
if (type == null) {
return null;
} else {
if (allBitstreams != null) {
for (Bitstream bitstream : allBitstreams) {
if (StringUtils.equals(bitstreamService.getMetadata(bitstream,
Process.BITSTREAM_TYPE_METADATAFIELD), type)) {
return bitstream;
}
}
}
}
return null;
}
@Override
public List<Bitstream> getBitstreams(Context context, Process process) {
return process.getBitstreams();
}
public int countTotal(Context context) throws SQLException {
return processDAO.countRows(context);
}
@Override
public List<String> getFileTypesForProcessBitstreams(Context context, Process process) {
List<Bitstream> list = getBitstreams(context, process);
Set<String> fileTypesSet = new HashSet<>();
for (Bitstream bitstream : list) {
List<MetadataValue> metadata = bitstreamService.getMetadata(bitstream,
Process.BITSTREAM_TYPE_METADATAFIELD, Item.ANY);
if (metadata != null && !metadata.isEmpty()) {
fileTypesSet.add(metadata.get(0).getValue());
}
}
return new ArrayList<>(fileTypesSet);
}
}

View File

@@ -7,29 +7,46 @@
*/
package org.dspace.scripts;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.stream.Collectors;
import org.dspace.core.Context;
import org.dspace.kernel.ServiceManager;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.dspace.scripts.service.ScriptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The implementation for the {@link ScriptService}
*/
public class ScriptServiceImpl implements ScriptService {
private static final Logger log = LoggerFactory.getLogger(ScriptServiceImpl.class);
@Autowired
private ServiceManager serviceManager;
@Override
public DSpaceRunnable getScriptForName(String name) {
return serviceManager.getServiceByName(name, DSpaceRunnable.class);
public ScriptConfiguration getScriptConfiguration(String name) {
return serviceManager.getServiceByName(name, ScriptConfiguration.class);
}
@Override
public List<DSpaceRunnable> getDSpaceRunnables(Context context) {
return serviceManager.getServicesByType(DSpaceRunnable.class).stream().filter(
dSpaceRunnable -> dSpaceRunnable.isAllowedToExecute(context)).collect(Collectors.toList());
public List<ScriptConfiguration> getScriptConfigurations(Context context) {
return serviceManager.getServicesByType(ScriptConfiguration.class).stream().filter(
scriptConfiguration -> scriptConfiguration.isAllowedToExecute(context)).collect(Collectors.toList());
}
@Override
public DSpaceRunnable createDSpaceRunnableForScriptConfiguration(ScriptConfiguration scriptToExecute)
throws IllegalAccessException, InstantiationException {
try {
return (DSpaceRunnable) scriptToExecute.getDspaceRunnableClass().getDeclaredConstructor().newInstance();
} catch (InvocationTargetException | NoSuchMethodException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,92 @@
/**
* 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.scripts.configuration;
import org.apache.commons.cli.Options;
import org.dspace.core.Context;
import org.dspace.scripts.DSpaceRunnable;
import org.springframework.beans.factory.BeanNameAware;
/**
* This class represents an Abstract class that a ScriptConfiguration can inherit to further implement this
* and represent a script's configuration
*/
public abstract class ScriptConfiguration<T extends DSpaceRunnable> implements BeanNameAware {
/**
* The possible options for this script
*/
protected Options options;
private String description;
private String name;
/**
* Generic getter for the description
* @return the description value of this ScriptConfiguration
*/
public String getDescription() {
return description;
}
/**
* Generic setter for the description
* @param description The description to be set on this ScriptConfiguration
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Generic getter for the name
* @return the name value of this ScriptConfiguration
*/
public String getName() {
return name;
}
/**
* Generic setter for the name
* @param name The name to be set on this ScriptConfiguration
*/
public void setName(String name) {
this.name = name;
}
/**
* Generic getter for the dspaceRunnableClass
* @return the dspaceRunnableClass value of this ScriptConfiguration
*/
public abstract Class<T> getDspaceRunnableClass();
/**
* Generic setter for the dspaceRunnableClass
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this IndexDiscoveryScriptConfiguration
*/
public abstract void setDspaceRunnableClass(Class<T> dspaceRunnableClass);
/**
* This method will return if the script is allowed to execute in the given context. This is by default set
* to the currentUser in the context being an admin, however this can be overwritten by each script individually
* if different rules apply
* @param context The relevant DSpace context
* @return A boolean indicating whether the script is allowed to execute or not
*/
public abstract boolean isAllowedToExecute(Context context);
/**
* The getter for the options of the Script
* @return the options value of this ScriptConfiguration
*/
public abstract Options getOptions();
@Override
public void setBeanName(String beanName) {
this.name = beanName;
}
}

View File

@@ -7,9 +7,14 @@
*/
package org.dspace.scripts.handler;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Optional;
import org.apache.commons.cli.Options;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
/**
* This is an interface meant to be implemented by any DSpaceRunnableHandler to specify specific execution methods
@@ -78,4 +83,28 @@ public interface DSpaceRunnableHandler {
* @param name The name of the script
*/
public void printHelp(Options options, String name);
/**
* This method will grab the InputStream for the file defined by the given file name. The exact implementation will
* differ based on whether it's a REST call or CommandLine call. The REST Call will look for Bitstreams in the
* Database whereas the CommandLine call will look on the filesystem
* @param context The relevant DSpace context
* @param fileName The filename for the file that holds the InputStream
* @return The InputStream for the file defined by the given file name
* @throws IOException If something goes wrong
* @throws AuthorizeException If something goes wrong
*/
public Optional<InputStream> getFileStream(Context context, String fileName) throws IOException, AuthorizeException;
/**
* This method will write the InputStream to either a file on the filesystem or a bitstream in the database
* depending on whether it's coming from a CommandLine call or REST call respectively
* @param context The relevant DSpace context
* @param fileName The filename
* @param inputStream The inputstream to be written
* @param type The type of the file
* @throws IOException If something goes wrong
*/
public void writeFilestream(Context context, String fileName, InputStream inputStream, String type)
throws IOException, SQLException, AuthorizeException;
}

View File

@@ -7,9 +7,16 @@
*/
package org.dspace.scripts.handler.impl;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
/**
@@ -84,4 +91,20 @@ public class CommandLineDSpaceRunnableHandler implements DSpaceRunnableHandler {
formatter.printHelp(name, options);
}
}
@Override
public Optional<InputStream> getFileStream(Context context, String fileName) throws IOException {
File file = new File(fileName);
if (!(file.exists() && file.isFile())) {
return Optional.empty();
}
return Optional.of(FileUtils.openInputStream(file));
}
@Override
public void writeFilestream(Context context, String fileName, InputStream inputStream, String type)
throws IOException {
File file = new File(fileName);
FileUtils.copyInputStreamToFile(inputStream, file);
}
}

View File

@@ -7,9 +7,13 @@
*/
package org.dspace.scripts.service;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.scripts.DSpaceCommandLineParameter;
@@ -104,13 +108,28 @@ public interface ProcessService {
*/
public void complete(Context context, Process process) throws SQLException;
/**
* The method will create a bitstream from the given inputstream with the given type as metadata and given name
* as name and attach it to the given process
* @param context The relevant DSpace context
* @param process The process for which the bitstream will be made
* @param is The inputstream for the bitstream
* @param type The type of the bitstream
* @param fileName The name of the bitstream
* @throws IOException If something goes wrong
* @throws SQLException If something goes wrong
* @throws AuthorizeException If something goes wrong
*/
public void appendFile(Context context, Process process, InputStream is, String type, String fileName)
throws IOException, SQLException, AuthorizeException;
/**
* This method will delete the given Process object from the database
* @param context The relevant DSpace context
* @param process The Process object to be deleted
* @throws SQLException If something goes wrong
*/
public void delete(Context context, Process process) throws SQLException;
public void delete(Context context, Process process) throws SQLException, IOException, AuthorizeException;
/**
* This method will be used to update the given Process object in the database
@@ -128,6 +147,32 @@ public interface ProcessService {
*/
public List<DSpaceCommandLineParameter> getParameters(Process process);
/**
* This method will return the Bitstream that matches the given name for the given Process
* @param context The relevant DSpace context
* @param process The process that should hold the requested Bitstream
* @param bitstreamName The name of the requested Bitstream
* @return The Bitstream from the given Process that matches the given bitstream name
*/
public Bitstream getBitstreamByName(Context context, Process process, String bitstreamName);
/**
* This method will return the Bitstream for a given process with a given type
* @param context The relevant DSpace context
* @param process The process that holds the Bitstreams to be searched in
* @param type The type that the Bitstream must have
* @return The Bitstream of the given type for the given Process
*/
public Bitstream getBitstream(Context context, Process process, String type);
/**
* This method will return all the Bitstreams for a given process
* @param context The relevant DSpace context
* @param process The process that holds the Bitstreams to be searched in
* @return The list of Bitstreams
*/
public List<Bitstream> getBitstreams(Context context, Process process);
/**
* Returns the total amount of Process objects in the dataase
* @param context The relevant DSpace context
@@ -136,4 +181,12 @@ public interface ProcessService {
*/
int countTotal(Context context) throws SQLException;
/**
* This will return a list of Strings where each String represents the type of a Bitstream in the Process given
* @param context The DSpace context
* @param process The Process object that we'll use to find the bitstreams
* @return A list of Strings where each String represents a fileType that is in the Process
*/
public List<String> getFileTypesForProcessBitstreams(Context context, Process process);
}

View File

@@ -11,6 +11,7 @@ import java.util.List;
import org.dspace.core.Context;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.scripts.configuration.ScriptConfiguration;
/**
* This service will deal with logic to handle DSpaceRunnable objects
@@ -18,16 +19,29 @@ import org.dspace.scripts.DSpaceRunnable;
public interface ScriptService {
/**
* This method will return the DSpaceRunnable that has the name that's equal to the name given in the parameters
* This method will return the ScriptConfiguration that has the name that's equal to the name given in the
* parameters
* @param name The name that the script has to match
* @return The matching DSpaceRunnable script
* @return The matching ScriptConfiguration
*/
DSpaceRunnable getScriptForName(String name);
ScriptConfiguration getScriptConfiguration(String name);
/**
* This method will return a list of DSpaceRunnable objects for which the given Context is authorized to use them
* This method will return a list of ScriptConfiguration objects for which the given Context is authorized
* @param context The relevant DSpace context
* @return The list of accessible DSpaceRunnable scripts for this context
* @return The list of accessible ScriptConfiguration scripts for this context
*/
List<DSpaceRunnable> getDSpaceRunnables(Context context);
List<ScriptConfiguration> getScriptConfigurations(Context context);
/**
* This method will create a new instance of the DSpaceRunnable that's linked with this Scriptconfiguration
* It'll grab the DSpaceRunnable class from the ScriptConfiguration's variables and create a new instance of it
* to return
* @param scriptToExecute The relevant ScriptConfiguration
* @return The new instance of the DSpaceRunnable class
* @throws IllegalAccessException If something goes wrong
* @throws InstantiationException If something goes wrong
*/
DSpaceRunnable createDSpaceRunnableForScriptConfiguration(ScriptConfiguration scriptToExecute)
throws IllegalAccessException, InstantiationException;
}

View File

@@ -0,0 +1,116 @@
/**
* 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.statistics.export;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.dspace.core.Context;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.dspace.statistics.export.factory.OpenURLTrackerLoggerServiceFactory;
import org.dspace.statistics.export.service.OpenUrlService;
/**
* Script to retry the failed url transmissions to IRUS
* This script also has an option to add new failed urls for testing purposes
*/
public class RetryFailedOpenUrlTracker extends DSpaceRunnable {
private Context context = null;
private String lineToAdd = null;
private boolean help = false;
private boolean retryFailed = false;
private OpenUrlService openUrlService;
/**
* Run the script
* When the -a option is used, a new "failed" url will be added to the database
*
* @throws Exception
*/
public void internalRun() throws Exception {
if (help) {
printHelp();
return;
}
context.turnOffAuthorisationSystem();
if (StringUtils.isNotBlank(lineToAdd)) {
openUrlService.logfailed(context, lineToAdd);
handler.logInfo("Created dummy entry in OpenUrlTracker with URL: " + lineToAdd);
}
if (retryFailed) {
handler.logInfo("Reprocessing failed URLs stored in the db");
openUrlService.reprocessFailedQueue(context);
}
context.restoreAuthSystemState();
try {
context.complete();
} catch (Exception e) {
handler.logError(e.getMessage());
}
}
public ScriptConfiguration getScriptConfiguration() {
return null;
}
/**
* Setups the parameters
*
* @throws ParseException
*/
public void setup() throws ParseException {
context = new Context();
openUrlService = OpenURLTrackerLoggerServiceFactory.getInstance().getOpenUrlService();
if (!(commandLine.hasOption('a') || commandLine.hasOption('r') || commandLine.hasOption('h'))) {
throw new ParseException("At least one of the parameters (-a, -r, -h) is required!");
}
if (commandLine.hasOption('h')) {
help = true;
}
if (commandLine.hasOption('a')) {
lineToAdd = commandLine.getOptionValue('a');
}
if (commandLine.hasOption('r')) {
retryFailed = true;
}
}
private RetryFailedOpenUrlTracker() {
Options options = constructOptions();
this.options = options;
}
/**
* Constructs the script options
*
* @return the constructed options
*/
private Options constructOptions() {
Options options = new Options();
options.addOption("a", true, "Add a new \"failed\" row to the table with a url (test purposes only)");
options.getOption("a").setType(String.class);
options.addOption("r", false, "Retry sending requests to all urls stored in the table with failed requests. " +
"This includes the url that can be added through the -a option.");
options.getOption("r").setType(boolean.class);
options.addOption("h", "help", false, "print this help message");
options.getOption("h").setType(boolean.class);
return options;
}
}

View File

@@ -15,7 +15,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
import org.apache.commons.collections4.MapUtils;

View File

@@ -94,5 +94,19 @@ public interface WorkflowService<T extends WorkflowItem> {
public Group getWorkflowRoleGroup(Context context, Collection collection, String roleName, Group roleGroup)
throws SQLException, IOException, WorkflowConfigurationException, AuthorizeException, WorkflowException;
/**
* This method will create the workflowRoleGroup for a collection and the given rolename
* @param context The relevant DSpace context
* @param collection The collection
* @param roleName The rolename
* @return The created Group
* @throws AuthorizeException If something goes wrong
* @throws SQLException If something goes wrong
* @throws IOException If something goes wrong
* @throws WorkflowConfigurationException If something goes wrong
*/
public Group createWorkflowRoleGroup(Context context, Collection collection, String roleName)
throws AuthorizeException, SQLException, IOException, WorkflowConfigurationException;
public List<String> getFlywayMigrationLocations();
}

View File

@@ -1216,6 +1216,12 @@ public class BasicWorkflowServiceImpl implements BasicWorkflowService {
return roleGroup;
}
@Override
public Group createWorkflowRoleGroup(Context context, Collection collection, String roleName)
throws AuthorizeException, SQLException {
return getWorkflowRoleGroup(context, collection, roleName, null);
}
@Override
public List<String> getFlywayMigrationLocations() {
return Collections.emptyList();

View File

@@ -18,7 +18,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.UUID;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
@@ -158,22 +157,6 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService {
Role role = WorkflowUtils.getCollectionAndRepositoryRoles(collection).get(roleName);
if (role.getScope() == Role.Scope.COLLECTION || role.getScope() == Role.Scope.REPOSITORY) {
roleGroup = WorkflowUtils.getRoleGroup(context, collection, role);
if (roleGroup == null) {
authorizeService.authorizeAction(context, collection, Constants.WRITE);
roleGroup = groupService.create(context);
if (role.getScope() == Role.Scope.COLLECTION) {
groupService.setName(roleGroup,
"COLLECTION_" + collection.getID().toString()
+ "_WORKFLOW_ROLE_" + roleName);
} else {
groupService.setName(roleGroup, role.getName());
}
groupService.update(context, roleGroup);
authorizeService.addPolicy(context, collection, Constants.ADD, roleGroup);
if (role.getScope() == Role.Scope.COLLECTION) {
WorkflowUtils.createCollectionWorkflowRole(context, collection, roleName, roleGroup);
}
}
}
return roleGroup;
} catch (WorkflowConfigurationException e) {
@@ -181,6 +164,28 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService {
}
}
@Override
public Group createWorkflowRoleGroup(Context context, Collection collection, String roleName)
throws AuthorizeException, SQLException, IOException, WorkflowConfigurationException {
Group roleGroup;
authorizeService.authorizeAction(context, collection, Constants.WRITE);
roleGroup = groupService.create(context);
Role role = WorkflowUtils.getCollectionAndRepositoryRoles(collection).get(roleName);
if (role.getScope() == Role.Scope.COLLECTION) {
groupService.setName(roleGroup,
"COLLECTION_" + collection.getID().toString()
+ "_WORKFLOW_ROLE_" + roleName);
} else {
groupService.setName(roleGroup, role.getName());
}
groupService.update(context, roleGroup);
authorizeService.addPolicy(context, collection, Constants.ADD, roleGroup);
if (role.getScope() == Role.Scope.COLLECTION) {
WorkflowUtils.createCollectionWorkflowRole(context, collection, roleName, roleGroup);
}
return roleGroup;
}
@Override
public List<String> getFlywayMigrationLocations() {
return Collections.singletonList("classpath:org.dspace.storage.rdbms.xmlworkflow");
@@ -309,7 +314,7 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService {
@Override
public WorkflowActionConfig doState(Context c, EPerson user, HttpServletRequest request, int workflowItemId,
Workflow workflow, WorkflowActionConfig currentActionConfig)
throws SQLException, AuthorizeException, IOException, MessagingException, WorkflowException {
throws SQLException, AuthorizeException, IOException, WorkflowException {
try {
XmlWorkflowItem wi = xmlWorkflowItemService.find(c, workflowItemId);
Step currentStep = currentActionConfig.getStep();

View File

@@ -104,7 +104,7 @@ public class Workflow implements BeanNameAware {
Map<String, Role> roles = new HashMap<>();
for (Step step : steps) {
if (step.getRole() != null) {
roles.put(step.getRole().getName(), step.getRole());
roles.put(step.getRole().getId(), step.getRole());
}
}
return roles;

View File

@@ -50,6 +50,20 @@ public abstract class Action {
*/
public abstract List<String> getOptions();
/**
* Returns true if one of the options is a parameter of the request
* @param request Action request
* @return true if one of the options is a parameter of the request; false if none was found
*/
protected boolean isOptionInParam(HttpServletRequest request) {
for (String option: this.getOptions()) {
if (request.getParameter(option) != null) {
return true;
}
}
return false;
}
public WorkflowActionConfig getParent() {
return parent;
}

View File

@@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.util.Util;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCDate;
import org.dspace.content.MetadataSchemaEnum;
@@ -46,12 +47,14 @@ public class AcceptEditRejectAction extends ProcessingAction {
@Override
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
throws SQLException, AuthorizeException, IOException {
if (request.getParameter(SUBMIT_APPROVE) != null) {
return processAccept(c, wfi);
} else {
if (request.getParameter(SUBMIT_REJECT) != null) {
return processRejectPage(c, wfi, request);
if (super.isOptionInParam(request)) {
switch (Util.getSubmitButton(request, SUBMIT_CANCEL)) {
case SUBMIT_APPROVE:
return processAccept(c, wfi);
case SUBMIT_REJECT:
return processRejectPage(c, wfi, request);
default:
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
}
}
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);

View File

@@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.util.Util;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCDate;
import org.dspace.content.MetadataSchemaEnum;
@@ -42,20 +43,23 @@ public class FinalEditAction extends ProcessingAction {
@Override
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
throws SQLException, AuthorizeException {
return processMainPage(c, wfi, step, request);
return processMainPage(c, wfi, request);
}
public ActionResult processMainPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
public ActionResult processMainPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request)
throws SQLException, AuthorizeException {
if (request.getParameter(SUBMIT_APPROVE) != null) {
//Delete the tasks
addApprovedProvenance(c, wfi);
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
} else {
//We pressed the leave button so return to our submissions page
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
if (super.isOptionInParam(request)) {
switch (Util.getSubmitButton(request, SUBMIT_CANCEL)) {
case SUBMIT_APPROVE:
//Delete the tasks
addApprovedProvenance(c, wfi);
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
default:
//We pressed the leave button so return to our submissions page
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);
}
}
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
}
@Override

View File

@@ -34,7 +34,8 @@ public abstract class ProcessingAction extends Action {
@Autowired(required = true)
protected ItemService itemService;
protected static final String SUBMIT_EDIT_METADATA = "submit_edit_metadata";
public static final String SUBMIT_EDIT_METADATA = "submit_edit_metadata";
public static final String SUBMIT_CANCEL = "submit_cancel";
@Override
public boolean isAuthorized(Context context, HttpServletRequest request, XmlWorkflowItem wfi) throws SQLException {

View File

@@ -11,9 +11,9 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.util.Util;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCDate;
import org.dspace.content.MetadataSchemaEnum;
@@ -47,14 +47,16 @@ public class ReviewAction extends ProcessingAction {
@Override
public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
throws SQLException, AuthorizeException, IOException {
if (request.getParameter(SUBMIT_APPROVE) != null) {
return processAccept(c, wfi, step, request);
} else {
if (request.getParameter(SUBMIT_REJECT) != null) {
return processRejectPage(c, wfi, step, request);
if (super.isOptionInParam(request)) {
switch (Util.getSubmitButton(request, SUBMIT_CANCEL)) {
case SUBMIT_APPROVE:
return processAccept(c, wfi);
case SUBMIT_REJECT:
return processRejectPage(c, wfi, step, request);
default:
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
}
}
return new ActionResult(ActionResult.TYPE.TYPE_CANCEL);
}
@@ -66,11 +68,9 @@ public class ReviewAction extends ProcessingAction {
return options;
}
public ActionResult processAccept(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request)
throws SQLException, AuthorizeException {
public ActionResult processAccept(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException {
//Delete the tasks
addApprovedProvenance(c, wfi);
return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE);
}
@@ -80,14 +80,14 @@ public class ReviewAction extends ProcessingAction {
// Get user's name + email address
String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
.getEPersonName(c.getCurrentUser());
.getEPersonName(c.getCurrentUser());
String provDescription = getProvenanceStartId() + " Approved for entry into archive by "
+ usersName + " on " + now + " (GMT) ";
// Add to item as a DC field
itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en",
provDescription);
provDescription);
itemService.update(c, wfi.getItem());
}
@@ -102,8 +102,8 @@ public class ReviewAction extends ProcessingAction {
//We have pressed reject, so remove the task the user has & put it back to a workspace item
XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService()
.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(),
this.getProvenanceStartId(), reason);
.sendWorkflowItemBackSubmission(c, wfi, c.getCurrentUser(),
this.getProvenanceStartId(), reason);
return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE);

View File

@@ -130,6 +130,11 @@ public class PoolTaskServiceImpl implements PoolTaskService {
return poolTaskDAO.findByEPerson(context, ePerson);
}
@Override
public List<PoolTask> findByGroup(Context context, Group group) throws SQLException {
return poolTaskDAO.findByGroup(context, group);
}
@Override
public PoolTask create(Context context) throws SQLException, AuthorizeException {
return poolTaskDAO.create(context, new PoolTask());

View File

@@ -9,7 +9,6 @@ package org.dspace.xmlworkflow.storedcomponents.dao.impl;
import java.sql.SQLException;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;

View File

@@ -14,6 +14,7 @@ import java.util.List;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.service.DSpaceCRUDService;
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
@@ -41,4 +42,13 @@ public interface PoolTaskService extends DSpaceCRUDService<PoolTask> {
throws SQLException, AuthorizeException;
public List<PoolTask> findByEPerson(Context context, EPerson ePerson) throws SQLException;
/**
* This method will return a list of PoolTask for the given group
* @param context The relevant DSpace context
* @param group The Group to be searched on
* @return The list of PoolTask objects
* @throws SQLException If something goes wrong
*/
public List<PoolTask> findByGroup(Context context, Group group) throws SQLException;
}

View File

@@ -0,0 +1,2 @@
id,collection,dc.contributor.author
+,"123456789/2","Donald, SmithImported"
1 id collection dc.contributor.author
2 + 123456789/2 Donald, SmithImported

View File

@@ -18,6 +18,7 @@
<!-- for handle "default". -->
<submission-map>
<name-map collection-handle="default" submission-name="traditional"/>
<name-map collection-handle="123456789/language-test-1" submission-name="languagetestprocess"/>
</submission-map>
@@ -82,9 +83,9 @@
<!--Step will be to select a Creative Commons License -->
<!-- Uncomment this step to allow the user to select a Creative Commons
license -->
<!-- <step id="creative-commons"> <heading>submit.progressbar.CClicense</heading>
<processing-class>org.dspace.submit.step.CCLicenseStep</processing-class>
<type>cclicense</type> </step> -->
<step id="cclicense"> <heading>submit.progressbar.CClicense</heading>
<processing-class>org.dspace.app.rest.submit.step.CCLicenseStep</processing-class>
<type>cclicense</type> </step>
<!--Step will be to Check for potential duplicate -->
<!-- <step id="detect-duplicate"> <heading>submit.progressbar.detect.duplicate</heading>
@@ -108,6 +109,12 @@
<processing-class>org.dspace.submit.step.SampleStep</processing-class>
<type>sample</type>
</step>
<step id="languagetest" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type>
</step>
</step-definitions>
<!-- The submission-definitions map lays out the detailed definition of -->
@@ -145,10 +152,14 @@
<!--Step will be to Sign off on the License -->
<step id="license"/>
<!-- <step id="creative-commons"/> -->
<step id="cclicense"/>
<!-- <step id="verify"/> -->
</submission-process>
<submission-process name="languagetestprocess">
<step id="collection"/>
<step id="languagetest"/>
</submission-process>
</submission-definitions>
</item-submission>

View File

@@ -107,4 +107,20 @@ plugin.sequence.java.util.Collection = \
java.util.ArrayList, \
java.util.LinkedList, \
java.util.Stack, \
java.util.TreeSet
java.util.TreeSet
###########################################
# PROPERTIES USED TO TEST CONFIGURATION #
# PROPERTY EXPOSURE VIA REST #
###########################################
rest.properties.exposed = configuration.exposed.single.value
rest.properties.exposed = configuration.exposed.array.value
rest.properties.exposed = configuration.not.existing
configuration.not.exposed = secret_value
configuration.exposed.single.value = public_value
configuration.exposed.array.value = public_value_1, public_value_2
# Test config for the authentication ip functionality
authentication-ip.Staff = 5.5.5.5
authentication-ip.Student = 6.6.6.6

View File

@@ -4,19 +4,29 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="index-discovery" class="org.dspace.discovery.IndexClient" scope="prototype">
<property name="name" value="index-discovery"/>
<bean id="index-discovery" class="org.dspace.discovery.IndexDiscoveryScriptConfiguration">
<property name="description" value="Update Discovery Solr Search Index"/>
<property name="dspaceRunnableClass" value="org.dspace.discovery.IndexClient"/>
</bean>
<bean id="mock-script" class="org.dspace.scripts.impl.MockDSpaceRunnableScript" scope="prototype">
<property name="name" value="mock-script" />
<bean id="metadata-import" class="org.dspace.app.bulkedit.MetadataImportCliScriptConfiguration">
<property name="description" value="Import metadata after batch editing" />
<property name="dspaceRunnableClass" value="org.dspace.app.bulkedit.MetadataImportCLI"/>
</bean>
<bean id="metadata-export" class="org.dspace.app.bulkedit.MetadataExportScriptConfiguration">
<property name="description" value="Export metadata for batch editing"/>
<property name="dspaceRunnableClass" value="org.dspace.app.bulkedit.MetadataExport"/>
</bean>
<bean id="mock-script" class="org.dspace.scripts.MockDSpaceRunnableScriptConfiguration" scope="prototype">
<property name="description" value="Mocking a script for testing purposes" />
<property name="dspaceRunnableClass" value="org.dspace.scripts.impl.MockDSpaceRunnableScript"/>
</bean>
<bean id="retry-tracker" class="org.dspace.statistics.export.RetryFailedOpenUrlTracker" scope="prototype">
<property name="name" value="retry-tracker"/>
<property name="description" value="Retry all failed commits to the OpenURLTracker"/>
<property name="dspaceRunnableClass" value="org.dspace.statistics.export.RetryFailedOpenUrlTracker"/>
</bean>
</beans>

Some files were not shown because too many files have changed in this diff Show More