mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-17 15:03:18 +00:00
Merge branch 'master' into DS-4443_delete-solr-record-on-workspaceitem-deletion
This commit is contained in:
29
.codecov.yml
Normal file
29
.codecov.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
# DSpace configuration for Codecov.io coverage reports
|
||||
# These override the default YAML settings at
|
||||
# https://docs.codecov.io/docs/codecov-yaml#section-default-yaml
|
||||
# Can be validated via instructions at:
|
||||
# https://docs.codecov.io/docs/codecov-yaml#validate-your-repository-yaml
|
||||
|
||||
# Settings related to code coverage analysis
|
||||
coverage:
|
||||
status:
|
||||
# Configuration for project-level checks. This checks how the PR changes overall coverage.
|
||||
project:
|
||||
default:
|
||||
# For each PR, auto compare coverage to previous commit.
|
||||
# Require that overall (project) coverage does NOT drop more than 0.5%
|
||||
target: auto
|
||||
threshold: 0.5%
|
||||
# Configuration for patch-level checks. This checks the relative coverage of the new PR code ONLY.
|
||||
patch:
|
||||
default:
|
||||
# For each PR, make sure the coverage of the new code is within 1% of current overall coverage.
|
||||
# We let 'patch' be more lenient as we only require *project* coverage to not drop significantly.
|
||||
target: auto
|
||||
threshold: 1%
|
||||
|
||||
# Turn PR comments "off". This feature adds the code coverage summary as a
|
||||
# comment on each PR. See https://docs.codecov.io/docs/pull-request-comments
|
||||
# However, this same info is available from the Codecov checks in the PR's
|
||||
# "Checks" tab in GitHub. So, the comment is unnecessary.
|
||||
comment: false
|
25
.github/workflows/label_merge_conflicts.yml
vendored
Normal file
25
.github/workflows/label_merge_conflicts.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# This workflow checks open PRs for merge conflicts and labels them when conflicts are found
|
||||
name: Check for merge conflicts
|
||||
|
||||
# Run whenever the "main" branch is updated
|
||||
# NOTE: This means merge conflicts are only checked for when a PR is merged to main.
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# See: https://github.com/mschilde/auto-label-merge-conflicts/
|
||||
- name: Auto-label PRs with merge conflicts
|
||||
uses: mschilde/auto-label-merge-conflicts@v2.0
|
||||
# Add "merge conflict" label if a merge conflict is detected. Remove it when resolved.
|
||||
# Note, the authentication token is created automatically
|
||||
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
|
||||
with:
|
||||
CONFLICT_LABEL_NAME: 'merge conflict'
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Ignore errors
|
||||
continue-on-error: true
|
77
.travis.yml
77
.travis.yml
@@ -1,46 +1,55 @@
|
||||
# DSpace's Travis CI Configuration
|
||||
# Builds: https://travis-ci.com/github/DSpace/DSpace
|
||||
# Travis configuration guide/validation: https://config.travis-ci.com/explore
|
||||
language: java
|
||||
sudo: false
|
||||
# TODO: Upgrade to Bionic
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
# Give Maven 1GB of memory to work with
|
||||
- MAVEN_OPTS=-Xmx1024M
|
||||
os: linux
|
||||
|
||||
jdk:
|
||||
# DS-3384 Oracle JDK has DocLint enabled by default.
|
||||
# Let's use this to catch any newly introduced DocLint issues.
|
||||
- oraclejdk11
|
||||
|
||||
## Should we run into any problems with oraclejdk8 on Travis, we may try the following workaround.
|
||||
## https://docs.travis-ci.com/user/languages/java#Testing-Against-Multiple-JDKs
|
||||
## https://github.com/travis-ci/travis-ci/issues/3259#issuecomment-130860338
|
||||
#addons:
|
||||
# apt:
|
||||
# packages:
|
||||
# - oracle-java8-installer
|
||||
# Define global environment variables (shared across all jobs)
|
||||
env:
|
||||
global:
|
||||
# Suppress all Maven "downloading" messages in Travis logs (see https://stackoverflow.com/a/35653426)
|
||||
# This also slightly speeds builds in Travis, as there is less logging
|
||||
- HIDE_MAVEN_DOWNLOADS="-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn"
|
||||
# Give Maven 1GB of memory to work with
|
||||
- MAVEN_OPTS="-Xmx1024M $HIDE_MAVEN_DOWNLOADS"
|
||||
# Maven options which will skip ALL code validation checks. Includes skipping:
|
||||
# - enforcer.skip => Skip maven-enforcer-plugin rules
|
||||
# - checkstyle.skip => Skip all checkstyle checks by maven-checkstyle-plugin
|
||||
# - license.skip => Skip all license header checks by license-maven-plugin
|
||||
# - xml.skip => Skip all XML/XSLT validation by xml-maven-plugin
|
||||
# (Useful for builds which don't need to repeat code checks)
|
||||
- SKIP_CODE_CHECKS="-Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true"
|
||||
|
||||
before_install:
|
||||
# Remove outdated settings.xml from Travis builds. Workaround for https://github.com/travis-ci/travis-ci/issues/4629
|
||||
- rm ~/.m2/settings.xml
|
||||
# Create two jobs to run Unit & Integration tests in parallel.
|
||||
# These jobs only differ in the TEST_FLAGS defined below,
|
||||
# and otherwise share all the other configs in this file
|
||||
jobs:
|
||||
include:
|
||||
- name: "Run Unit Tests & Check Code"
|
||||
# NOTE: unit tests include deprecated REST API v6 (as it has unit tests)
|
||||
env: TEST_FLAGS="-DskipUnitTests=false -Pdspace-rest"
|
||||
- name: "Run Integration Tests"
|
||||
# NOTE: skips code checks, as they are already done by Unit Test job
|
||||
env: TEST_FLAGS="-DskipIntegrationTests=false $SKIP_CODE_CHECKS"
|
||||
|
||||
# Skip install stage, as we'll do it below
|
||||
install: "echo 'Skipping install stage, dependencies will be downloaded during build and test stages.'"
|
||||
# Skip 'install' process to save time. We build/install/test all at once in "script" below.
|
||||
install: skip
|
||||
|
||||
# Build DSpace and run both Unit and Integration Tests
|
||||
script:
|
||||
# Summary of flags used (below):
|
||||
# license:check => Validate all source code license headers
|
||||
# -DskipTests=false => Enable DSpace Unit Tests
|
||||
# -DskipITs=false => Enable DSpace Integration Tests
|
||||
# -Pdspace-rest => Enable optional dspace-rest module as part of build
|
||||
# -P !assembly => Skip assembly of "dspace-installer" directory (as it can be memory intensive)
|
||||
# -B => Maven batch/non-interactive mode (recommended for CI)
|
||||
# -V => Display Maven version info before build
|
||||
# -Dsurefire.rerunFailingTestsCount=2 => try again for flakey tests, and keep track of/report on number of retries
|
||||
- "mvn clean install license:check -DskipTests=false -DskipITs=false -Pdspace-rest -P !assembly -B -V -Dsurefire.rerunFailingTestsCount=2"
|
||||
# Build DSpace and run configured tests (see 'jobs' above)
|
||||
# Notes on flags used:
|
||||
# -B => Maven batch/non-interactive mode (recommended for CI)
|
||||
# -V => Display Maven version info before build
|
||||
# -P-assembly => Disable build of dspace-installer in [src]/dspace/, as it can be memory intensive
|
||||
# -Pcoverage-report => Enable aggregate code coverage report (across all modules) via JaCoCo
|
||||
script: mvn install -B -V -P-assembly -Pcoverage-report $TEST_FLAGS
|
||||
|
||||
# After a successful build and test (see 'script'), send code coverage reports to coveralls.io
|
||||
# These code coverage reports are generated by jacoco-maven-plugin (during test process above).
|
||||
after_success:
|
||||
# Run "verify", enabling the "coveralls" profile. This sends our reports to coveralls.io (see coveralls-maven-plugin)
|
||||
- "cd dspace && mvn verify -P coveralls"
|
||||
# After a successful build and test (see 'script'), send aggregate code coverage reports
|
||||
# (generated by -Pcoverage-report above) to CodeCov.io
|
||||
after_success: bash <(curl -s https://codecov.io/bash)
|
||||
|
20
README.md
20
README.md
@@ -90,33 +90,33 @@ run automatically by [Travis CI](https://travis-ci.com/DSpace/DSpace/) for all P
|
||||
|
||||
* How to run both Unit Tests (via `maven-surefire-plugin`) and Integration Tests (via `maven-failsafe-plugin`):
|
||||
```
|
||||
mvn clean test -DskipTests=false -DskipITs=false
|
||||
mvn install -DskipUnitTests=false -DskipIntegrationTests=false
|
||||
```
|
||||
* How to run just Unit Tests:
|
||||
* How to run _only_ Unit Tests:
|
||||
```
|
||||
mvn test -DskipTests=false
|
||||
mvn test -DskipUnitTests=false
|
||||
```
|
||||
* How to run a *single* Unit Test
|
||||
```
|
||||
# Run all tests in a specific test class
|
||||
# NOTE: failIfNoTests=false is required to skip tests in other modules
|
||||
mvn test -DskipTests=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
|
||||
mvn test -DskipUnitTests=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
|
||||
|
||||
# Run one test method in a specific test class
|
||||
mvn test -DskipTests=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
|
||||
mvn test -DskipUnitTests=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
|
||||
```
|
||||
* How to run Integration Tests (requires enabling Unit tests too)
|
||||
* How to run _only_ Integration Tests
|
||||
```
|
||||
mvn verify -DskipTests=false -DskipITs=false
|
||||
mvn install -DskipIntegrationTests=false
|
||||
```
|
||||
* How to run a *single* Integration Test (requires enabling Unit tests too)
|
||||
* How to run a *single* Integration Test
|
||||
```
|
||||
# Run all integration tests in a specific test class
|
||||
# NOTE: failIfNoTests=false is required to skip tests in other modules
|
||||
mvn test -DskipTests=false -DskipITs=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
|
||||
mvn install -DskipIntegrationTests=false -Dit.test=[full.package.testClassName] -DfailIfNoTests=false
|
||||
|
||||
# Run one test method in a specific test class
|
||||
mvn test -DskipTests=false -DskipITs=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
|
||||
mvn install -DskipIntegrationTests=false -Dit.test=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
|
||||
```
|
||||
* How to run only tests of a specific DSpace module
|
||||
```
|
||||
|
@@ -127,81 +127,8 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>findbugs</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
<!-- property>
|
||||
<name>skipTests</name>
|
||||
<value>false</value>
|
||||
</property -->
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- If Unit Testing is enabled, then setup the Unit Test Environment.
|
||||
See also the 'skiptests' profile in Parent POM. -->
|
||||
<profile>
|
||||
<id>test-environment</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
<property>
|
||||
<name>skipTests</name>
|
||||
<value>false</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Unit/Integration Testing setup: This plugin unzips the
|
||||
'testEnvironment.zip' file (created by dspace-parent POM), into
|
||||
the 'target/testing/' folder, to essentially create a test
|
||||
install of DSpace, against which Tests can be run. -->
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/testing</outputDirectory>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<classifier>testEnvironment</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>setupTestEnvironment</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>setupIntegrationTestEnvironment</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- This plugin allows us to run a Groovy script in our Maven POM
|
||||
(see: http://gmaven.codehaus.org/Executing+Groovy+Code )
|
||||
(see: https://groovy.github.io/gmaven/groovy-maven-plugin/execute.html )
|
||||
We are generating a OS-agnostic version (agnostic.build.dir) of
|
||||
the ${project.build.directory} property (full path of target dir).
|
||||
This is needed by the Surefire & Failsafe plugins (see below)
|
||||
@@ -230,7 +157,81 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Run Unit Testing! This plugin just kicks off the tests (when enabled). -->
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>src/test/resources/**</exclude>
|
||||
<exclude>src/test/data/**</exclude>
|
||||
<!-- Ignore license header requirements on Flyway upgrade scripts -->
|
||||
<exclude>src/main/resources/org/dspace/storage/rdbms/flywayupgrade/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>spotbugs</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Setup the Unit Test Environment (when -DskipUnitTests=false) -->
|
||||
<profile>
|
||||
<id>unit-test-environment</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
<property>
|
||||
<name>skipUnitTests</name>
|
||||
<value>false</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Unit Testing setup: This plugin unzips the
|
||||
'testEnvironment.zip' file (created by dspace-parent POM), into
|
||||
the 'target/testing/' folder, to essentially create a test
|
||||
install of DSpace, against which Tests can be run. -->
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/testing</outputDirectory>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<classifier>testEnvironment</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>setupUnitTestEnvironment</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Run Unit Testing! This plugin just kicks off the tests. -->
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
@@ -245,8 +246,52 @@
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Run Integration Testing! This plugin just kicks off the tests (when enabled). -->
|
||||
<!-- Setup the Integration Test Environment (when -DskipIntegrationTests=false) -->
|
||||
<profile>
|
||||
<id>integration-test-environment</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
<property>
|
||||
<name>skipIntegrationTests</name>
|
||||
<value>false</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Integration Testing setup: This plugin unzips the
|
||||
'testEnvironment.zip' file (created by dspace-parent POM), into
|
||||
the 'target/testing/' folder, to essentially create a test
|
||||
install of DSpace, against which Tests can be run. -->
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/testing</outputDirectory>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<classifier>testEnvironment</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>setupIntegrationTestEnvironment</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Run Integration Testing! This plugin just kicks off the tests. -->
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
@@ -262,7 +307,6 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
@@ -325,6 +369,14 @@
|
||||
<artifactId>apache-jena-libs</artifactId>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<!-- Required to support PubMed API call in "PubmedImportMetadataSourceServiceImpl.GetRecord" -->
|
||||
<!-- Makes runtime operations in Jersey Dependency Injection -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.inject</groupId>
|
||||
<artifactId>jersey-hk2</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
@@ -700,7 +752,7 @@
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<version>6.5.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Analytics -->
|
||||
@@ -724,6 +776,7 @@
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FindBugs -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
@@ -733,6 +786,7 @@
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
|
@@ -10,10 +10,14 @@ package org.dspace.app.bulkedit;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.handle.factory.HandleServiceFactory;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
@@ -41,8 +45,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
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");
|
||||
logHelpInfo();
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
@@ -61,6 +64,11 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
context.complete();
|
||||
}
|
||||
|
||||
protected void logHelpInfo() {
|
||||
handler.logInfo("\nfull export: metadata-export");
|
||||
handler.logInfo("partial export: metadata-export -i handle");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataExportScriptConfiguration getScriptConfiguration() {
|
||||
return new DSpace().getServiceManager().getServiceByName("metadata-export",
|
||||
@@ -75,17 +83,32 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
return;
|
||||
}
|
||||
|
||||
// Check a filename is given
|
||||
if (!commandLine.hasOption('f')) {
|
||||
throw new ParseException("Required parameter -f missing!");
|
||||
}
|
||||
filename = commandLine.getOptionValue('f');
|
||||
|
||||
exportAllMetadata = commandLine.hasOption('a');
|
||||
|
||||
if (!commandLine.hasOption('i')) {
|
||||
exportAllItems = true;
|
||||
}
|
||||
handle = commandLine.getOptionValue('i');
|
||||
filename = getFileNameForExportFile();
|
||||
|
||||
exportAllMetadata = commandLine.hasOption('a');
|
||||
|
||||
}
|
||||
|
||||
protected String getFileNameForExportFile() throws ParseException {
|
||||
Context context = new Context();
|
||||
try {
|
||||
DSpaceObject dso = null;
|
||||
if (StringUtils.isNotBlank(handle)) {
|
||||
dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, handle);
|
||||
} else {
|
||||
dso = ContentServiceFactory.getInstance().getSiteService().findSite(context);
|
||||
}
|
||||
if (dso == null) {
|
||||
throw new ParseException("A handle got given that wasn't able to be parsed to a DSpaceObject");
|
||||
}
|
||||
return dso.getID().toString() + ".csv";
|
||||
} catch (SQLException e) {
|
||||
handler.handleException("Something went wrong trying to retrieve DSO for handle: " + handle, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -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 org.apache.commons.cli.ParseException;
|
||||
|
||||
public class MetadataExportCli extends MetadataExport {
|
||||
|
||||
@Override
|
||||
protected String getFileNameForExportFile() {
|
||||
return commandLine.getOptionValue('f');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws ParseException {
|
||||
super.setup();
|
||||
// Check a filename is given
|
||||
if (!commandLine.hasOption('f')) {
|
||||
throw new ParseException("Required parameter -f missing!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logHelpInfo() {
|
||||
handler.logInfo("\nfull export: metadata-export -f filename");
|
||||
handler.logInfo("partial export: metadata-export -i handle -f filename");
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 org.apache.commons.cli.Options;
|
||||
|
||||
public class MetadataExportCliScriptConfiguration extends MetadataExportScriptConfiguration<MetadataExportCli> {
|
||||
|
||||
|
||||
@Override
|
||||
public Options getOptions() {
|
||||
Options options = super.getOptions();
|
||||
options.addOption("f", "file", true, "destination where you want file written");
|
||||
options.getOption("f").setType(OutputStream .class);
|
||||
options.getOption("f").setRequired(true);
|
||||
super.options = options;
|
||||
return options;
|
||||
}
|
||||
}
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
package org.dspace.app.bulkedit;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
@@ -56,9 +55,6 @@ public class MetadataExportScriptConfiguration<T extends MetadataExport> extends
|
||||
|
||||
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);
|
||||
|
@@ -182,24 +182,7 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
|
||||
c.turnOffAuthorisationSystem();
|
||||
|
||||
// Find the EPerson, assign to context
|
||||
try {
|
||||
if (commandLine.hasOption('e')) {
|
||||
EPerson eperson;
|
||||
String e = commandLine.getOptionValue('e');
|
||||
if (e.indexOf('@') != -1) {
|
||||
eperson = EPersonServiceFactory.getInstance().getEPersonService().findByEmail(c, e);
|
||||
} else {
|
||||
eperson = EPersonServiceFactory.getInstance().getEPersonService().find(c, UUID.fromString(e));
|
||||
}
|
||||
|
||||
if (eperson == null) {
|
||||
throw new ParseException("Error, eperson cannot be found: " + e);
|
||||
}
|
||||
c.setCurrentUser(eperson);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ParseException("Unable to find DSpace user: " + e.getMessage());
|
||||
}
|
||||
assignCurrentUserInContext(c);
|
||||
|
||||
if (authorityControlled == null) {
|
||||
setAuthorizedMetadataFields();
|
||||
@@ -277,6 +260,18 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
|
||||
|
||||
}
|
||||
|
||||
protected void assignCurrentUserInContext(Context context) throws ParseException {
|
||||
UUID uuid = getEpersonIdentifier();
|
||||
if (uuid != null) {
|
||||
try {
|
||||
EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid);
|
||||
context.setCurrentUser(ePerson);
|
||||
} catch (SQLException e) {
|
||||
log.error("Something went wrong trying to fetch the eperson for uuid: " + uuid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines whether the changes should be applied or not. This is default set to true for the REST
|
||||
* script as we don't want to interact with the caller. This will be overwritten in the CLI script to ask for
|
||||
@@ -312,9 +307,6 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
|
||||
throw new ParseException("Required parameter -f missing!");
|
||||
}
|
||||
filename = commandLine.getOptionValue('f');
|
||||
if (!commandLine.hasOption('e')) {
|
||||
throw new ParseException("Required parameter -e missing!");
|
||||
}
|
||||
|
||||
// Option to apply template to new items
|
||||
if (commandLine.hasOption('t')) {
|
||||
|
@@ -10,7 +10,12 @@ package org.dspace.app.bulkedit;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.scripts.handler.DSpaceRunnableHandler;
|
||||
|
||||
/**
|
||||
@@ -30,4 +35,34 @@ public class MetadataImportCLI extends MetadataImport {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assignCurrentUserInContext(Context context) throws ParseException {
|
||||
try {
|
||||
if (commandLine.hasOption('e')) {
|
||||
EPerson eperson;
|
||||
String e = commandLine.getOptionValue('e');
|
||||
if (e.indexOf('@') != -1) {
|
||||
eperson = EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, e);
|
||||
} else {
|
||||
eperson = EPersonServiceFactory.getInstance().getEPersonService().find(context, UUID.fromString(e));
|
||||
}
|
||||
|
||||
if (eperson == null) {
|
||||
throw new ParseException("Error, eperson cannot be found: " + e);
|
||||
}
|
||||
context.setCurrentUser(eperson);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ParseException("Unable to find DSpace user: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws ParseException {
|
||||
super.setup();
|
||||
if (!commandLine.hasOption('e')) {
|
||||
throw new ParseException("Required parameter -e missing!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,10 +7,21 @@
|
||||
*/
|
||||
package org.dspace.app.bulkedit;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
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> {
|
||||
|
||||
@Override
|
||||
public Options getOptions() {
|
||||
Options options = super.getOptions();
|
||||
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);
|
||||
super.options = options;
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
@@ -57,9 +57,6 @@ public class MetadataImportScriptConfiguration<T extends MetadataImport> extends
|
||||
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);
|
||||
|
@@ -19,6 +19,15 @@ import org.dspace.core.Context;
|
||||
* @author Andrea Bollini
|
||||
*/
|
||||
public interface RequestItemAuthorExtractor {
|
||||
public RequestItemAuthor getRequestItemAuthor(Context context, Item item)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Retrieve the auhtor to contact for a request copy of the give item.
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param item item to request
|
||||
* @return An object containing name an email address to send the request to
|
||||
* or null if no valid email address was found.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) throws SQLException;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.I18nUtil;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ public class RequestItemMetadataStrategy extends RequestItemSubmitterStrategy {
|
||||
@Override
|
||||
public RequestItemAuthor getRequestItemAuthor(Context context, Item item)
|
||||
throws SQLException {
|
||||
RequestItemAuthor author = null;
|
||||
if (emailMetadata != null) {
|
||||
List<MetadataValue> vals = itemService.getMetadataByMetadataString(item, emailMetadata);
|
||||
if (vals.size() > 0) {
|
||||
@@ -49,19 +51,38 @@ public class RequestItemMetadataStrategy extends RequestItemSubmitterStrategy {
|
||||
fullname = nameVals.iterator().next().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(fullname)) {
|
||||
fullname = I18nUtil
|
||||
.getMessage(
|
||||
"org.dspace.app.requestitem.RequestItemMetadataStrategy.unnamed",
|
||||
context);
|
||||
}
|
||||
RequestItemAuthor author = new RequestItemAuthor(
|
||||
fullname, email);
|
||||
author = new RequestItemAuthor(fullname, email);
|
||||
return author;
|
||||
}
|
||||
} else {
|
||||
// Uses the basic strategy to look for the original submitter
|
||||
author = super.getRequestItemAuthor(context, item);
|
||||
// Is the author or his email null, so get the help desk or admin name and email
|
||||
if (null == author || null == author.getEmail()) {
|
||||
String email = null;
|
||||
String name = null;
|
||||
//First get help desk name and email
|
||||
email = DSpaceServicesFactory.getInstance()
|
||||
.getConfigurationService().getProperty("mail.helpdesk");
|
||||
name = DSpaceServicesFactory.getInstance()
|
||||
.getConfigurationService().getProperty("mail.helpdesk.name");
|
||||
// If help desk mail is null get the mail and name of admin
|
||||
if (email == null) {
|
||||
email = DSpaceServicesFactory.getInstance()
|
||||
.getConfigurationService().getProperty("mail.admin");
|
||||
name = DSpaceServicesFactory.getInstance()
|
||||
.getConfigurationService().getProperty("mail.admin.name");
|
||||
}
|
||||
return super.getRequestItemAuthor(context, item);
|
||||
author = new RequestItemAuthor(name, email);
|
||||
}
|
||||
}
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setEmailMetadata(String emailMetadata) {
|
||||
|
@@ -23,13 +23,22 @@ public class RequestItemSubmitterStrategy implements RequestItemAuthorExtractor
|
||||
public RequestItemSubmitterStrategy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submitter of an Item as RequestItemAuthor or null if the
|
||||
* Submitter is deleted.
|
||||
*
|
||||
* @return The submitter of the item or null if the submitter is deleted
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
@Override
|
||||
public RequestItemAuthor getRequestItemAuthor(Context context, Item item)
|
||||
throws SQLException {
|
||||
EPerson submitter = item.getSubmitter();
|
||||
RequestItemAuthor author = new RequestItemAuthor(
|
||||
RequestItemAuthor author = null;
|
||||
if (null != submitter) {
|
||||
author = new RequestItemAuthor(
|
||||
submitter.getFullName(), submitter.getEmail());
|
||||
}
|
||||
return author;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ 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.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -84,6 +85,9 @@ public class GenerateSitemaps {
|
||||
options
|
||||
.addOption("p", "ping", true,
|
||||
"ping specified search engine URL");
|
||||
options
|
||||
.addOption("d", "delete", false,
|
||||
"delete sitemaps dir and its contents");
|
||||
|
||||
CommandLine line = null;
|
||||
|
||||
@@ -105,10 +109,9 @@ public class GenerateSitemaps {
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check -- if no sitemap generation or pinging to do, print
|
||||
* usage
|
||||
* Sanity check -- if no sitemap generation or pinging to do, or deletion, print usage
|
||||
*/
|
||||
if (line.getArgs().length != 0 || line.hasOption('b')
|
||||
if (line.getArgs().length != 0 || line.hasOption('d') || line.hasOption('b')
|
||||
&& line.hasOption('s') && !line.hasOption('g')
|
||||
&& !line.hasOption('m') && !line.hasOption('y')
|
||||
&& !line.hasOption('p')) {
|
||||
@@ -123,6 +126,10 @@ public class GenerateSitemaps {
|
||||
generateSitemaps(!line.hasOption('b'), !line.hasOption('s'));
|
||||
}
|
||||
|
||||
if (line.hasOption('d')) {
|
||||
deleteSitemaps();
|
||||
}
|
||||
|
||||
if (line.hasOption('a')) {
|
||||
pingConfiguredSearchEngines();
|
||||
}
|
||||
@@ -140,6 +147,29 @@ public class GenerateSitemaps {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs generate-sitemaps without any params for the scheduler (task-scheduler.xml).
|
||||
*
|
||||
* @throws SQLException if a database error occurs.
|
||||
* @throws IOException if IO error occurs.
|
||||
*/
|
||||
public static void generateSitemapsScheduled() throws IOException, SQLException {
|
||||
generateSitemaps(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the sitemaps directory and its contents if it exists
|
||||
* @throws IOException if IO error occurs
|
||||
*/
|
||||
public static void deleteSitemaps() throws IOException {
|
||||
File outputDir = new File(configurationService.getProperty("sitemap.dir"));
|
||||
if (!outputDir.exists() && !outputDir.isDirectory()) {
|
||||
log.error("Unable to delete sitemaps directory, doesn't exist or isn't a directort");
|
||||
} else {
|
||||
FileUtils.deleteDirectory(outputDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sitemap.org protocol and/or basic HTML sitemaps.
|
||||
*
|
||||
@@ -150,14 +180,9 @@ public class GenerateSitemaps {
|
||||
* @throws IOException if IO error
|
||||
* if IO error occurs.
|
||||
*/
|
||||
public static void generateSitemaps(boolean makeHTMLMap,
|
||||
boolean makeSitemapOrg) throws SQLException, IOException {
|
||||
String sitemapStem = configurationService.getProperty("dspace.ui.url")
|
||||
+ "/sitemap";
|
||||
String htmlMapStem = configurationService.getProperty("dspace.ui.url")
|
||||
+ "/htmlmap";
|
||||
String handleURLStem = configurationService.getProperty("dspace.ui.url")
|
||||
+ "/handle/";
|
||||
public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) throws SQLException, IOException {
|
||||
String uiURLStem = configurationService.getProperty("dspace.ui.url");
|
||||
String sitemapStem = uiURLStem + "/sitemap";
|
||||
|
||||
File outputDir = new File(configurationService.getProperty("sitemap.dir"));
|
||||
if (!outputDir.exists() && !outputDir.mkdir()) {
|
||||
@@ -168,13 +193,11 @@ public class GenerateSitemaps {
|
||||
AbstractGenerator sitemapsOrg = null;
|
||||
|
||||
if (makeHTMLMap) {
|
||||
html = new HTMLSitemapGenerator(outputDir, htmlMapStem + "?map=",
|
||||
null);
|
||||
html = new HTMLSitemapGenerator(outputDir, sitemapStem, ".html");
|
||||
}
|
||||
|
||||
if (makeSitemapOrg) {
|
||||
sitemapsOrg = new SitemapsOrgGenerator(outputDir, sitemapStem
|
||||
+ "?map=", null);
|
||||
sitemapsOrg = new SitemapsOrgGenerator(outputDir, sitemapStem, ".xml");
|
||||
}
|
||||
|
||||
Context c = new Context(Context.Mode.READ_ONLY);
|
||||
@@ -182,7 +205,7 @@ public class GenerateSitemaps {
|
||||
List<Community> comms = communityService.findAll(c);
|
||||
|
||||
for (Community comm : comms) {
|
||||
String url = handleURLStem + comm.getHandle();
|
||||
String url = uiURLStem + "/communities/" + comm.getID();
|
||||
|
||||
if (makeHTMLMap) {
|
||||
html.addURL(url, null);
|
||||
@@ -197,7 +220,7 @@ public class GenerateSitemaps {
|
||||
List<Collection> colls = collectionService.findAll(c);
|
||||
|
||||
for (Collection coll : colls) {
|
||||
String url = handleURLStem + coll.getHandle();
|
||||
String url = uiURLStem + "/collections/" + coll.getID();
|
||||
|
||||
if (makeHTMLMap) {
|
||||
html.addURL(url, null);
|
||||
@@ -214,7 +237,7 @@ public class GenerateSitemaps {
|
||||
|
||||
while (allItems.hasNext()) {
|
||||
Item i = allItems.next();
|
||||
String url = handleURLStem + i.getHandle();
|
||||
String url = uiURLStem + "/items/" + i.getID();
|
||||
Date lastMod = i.getLastModified();
|
||||
|
||||
if (makeHTMLMap) {
|
||||
|
@@ -59,7 +59,7 @@ public class SitemapsOrgGenerator extends AbstractGenerator {
|
||||
|
||||
@Override
|
||||
public String getFilename(int number) {
|
||||
return "sitemap" + number + ".xml.gz";
|
||||
return "sitemap" + number + ".xml";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,12 +100,12 @@ public class SitemapsOrgGenerator extends AbstractGenerator {
|
||||
|
||||
@Override
|
||||
public boolean useCompression() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIndexFilename() {
|
||||
return "sitemap_index.xml.gz";
|
||||
return "sitemap_index.xml";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -12,6 +12,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.MetadataSchemaEnum;
|
||||
@@ -291,7 +292,7 @@ public class DCInput {
|
||||
*
|
||||
* @return the input type
|
||||
*/
|
||||
public String getInputType() {
|
||||
public @Nullable String getInputType() {
|
||||
return inputType;
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,7 @@ package org.dspace.app.util;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.core.Utils;
|
||||
/**
|
||||
* Class representing all DC inputs required for a submission, organized into pages
|
||||
@@ -109,7 +110,7 @@ public class DCInputSet {
|
||||
for (int j = 0; j < inputs[i].length; j++) {
|
||||
DCInput field = inputs[i][j];
|
||||
// If this is a "qualdrop_value" field, then the full field name is the field + dropdown qualifier
|
||||
if (field.getInputType().equals("qualdrop_value")) {
|
||||
if (StringUtils.equals(field.getInputType(), "qualdrop_value")) {
|
||||
List<String> pairs = field.getPairs();
|
||||
for (int k = 0; k < pairs.size(); k += 2) {
|
||||
String qualifier = pairs.get(k + 1);
|
||||
|
@@ -614,6 +614,12 @@ public class AuthorizeServiceImpl implements AuthorizeService {
|
||||
resourcePolicyService.removeDsoEPersonPolicies(c, o, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEPersonPolicies(Context c, EPerson e)
|
||||
throws SQLException, AuthorizeException {
|
||||
resourcePolicyService.removeAllEPersonPolicies(c, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> getAuthorizedGroups(Context c, DSpaceObject o,
|
||||
int actionID) throws java.sql.SQLException {
|
||||
|
@@ -114,6 +114,11 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService {
|
||||
return resourcePolicyDAO.findByEPersonGroupTypeIdAction(c, e, groups, action, type_id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourcePolicy> find(Context context, EPerson ePerson) throws SQLException {
|
||||
return resourcePolicyDAO.findByEPerson(context, ePerson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourcePolicy> findByTypeGroupActionExceptId(Context context, DSpaceObject dso, Group group,
|
||||
int action, int notPolicyID)
|
||||
@@ -246,6 +251,11 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEPersonPolicies(Context context, EPerson ePerson) throws SQLException, AuthorizeException {
|
||||
resourcePolicyDAO.deleteByEPerson(context, ePerson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeGroupPolicies(Context c, Group group) throws SQLException {
|
||||
resourcePolicyDAO.deleteByGroup(c, group);
|
||||
|
@@ -33,6 +33,8 @@ public interface ResourcePolicyDAO extends GenericDAO<ResourcePolicy> {
|
||||
public List<ResourcePolicy> findByDsoAndType(Context context, DSpaceObject dSpaceObject, String type)
|
||||
throws SQLException;
|
||||
|
||||
public List<ResourcePolicy> findByEPerson(Context context, EPerson ePerson) throws SQLException;
|
||||
|
||||
public List<ResourcePolicy> findByGroup(Context context, Group group) throws SQLException;
|
||||
|
||||
public List<ResourcePolicy> findByDSoAndAction(Context context, DSpaceObject dso, int actionId) throws SQLException;
|
||||
@@ -66,6 +68,15 @@ public interface ResourcePolicyDAO extends GenericDAO<ResourcePolicy> {
|
||||
|
||||
public void deleteByDsoEPersonPolicies(Context context, DSpaceObject dso, EPerson ePerson) throws SQLException;
|
||||
|
||||
/**
|
||||
* Deletes all policies that belong to an EPerson
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param ePerson ePerson whose policies to delete
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void deleteByEPerson(Context context, EPerson ePerson) throws SQLException;
|
||||
|
||||
public void deleteByDsoAndTypeNotEqualsTo(Context c, DSpaceObject o, String type) throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -101,7 +112,7 @@ public interface ResourcePolicyDAO extends GenericDAO<ResourcePolicy> {
|
||||
* @return total resource policies of the ePerson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countByEPerson(Context context, EPerson eperson) throws SQLException;
|
||||
public int countByEPerson(Context context, EPerson ePerson) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return a paginated list of policies related to a resourceUuid belong to an ePerson
|
||||
|
@@ -63,6 +63,16 @@ public class ResourcePolicyDAOImpl extends AbstractHibernateDAO<ResourcePolicy>
|
||||
return list(context, criteriaQuery, false, ResourcePolicy.class, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourcePolicy> findByEPerson(Context context, EPerson ePerson) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, ResourcePolicy.class);
|
||||
Root<ResourcePolicy> resourcePolicyRoot = criteriaQuery.from(ResourcePolicy.class);
|
||||
criteriaQuery.select(resourcePolicyRoot);
|
||||
criteriaQuery.where(criteriaBuilder.equal(resourcePolicyRoot.get(ResourcePolicy_.eperson), ePerson));
|
||||
return list(context, criteriaQuery, false, ResourcePolicy.class, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourcePolicy> findByGroup(Context context, Group group) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
@@ -194,6 +204,15 @@ public class ResourcePolicyDAOImpl extends AbstractHibernateDAO<ResourcePolicy>
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByEPerson(Context context, EPerson ePerson) throws SQLException {
|
||||
String queryString = "delete from ResourcePolicy where eperson= :eperson";
|
||||
Query query = createQuery(context, queryString);
|
||||
query.setParameter("eperson", ePerson);
|
||||
query.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByDsoAndTypeNotEqualsTo(Context context, DSpaceObject dso, String type) throws SQLException {
|
||||
|
||||
@@ -247,10 +266,10 @@ public class ResourcePolicyDAOImpl extends AbstractHibernateDAO<ResourcePolicy>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countByEPerson(Context context, EPerson eperson) throws SQLException {
|
||||
public int countByEPerson(Context context, EPerson ePerson) throws SQLException {
|
||||
Query query = createQuery(context,
|
||||
"SELECT count(*) FROM " + ResourcePolicy.class.getSimpleName() + " WHERE eperson_id = (:epersonUuid) ");
|
||||
query.setParameter("epersonUuid", eperson.getID());
|
||||
query.setParameter("epersonUuid", ePerson.getID());
|
||||
return count(query);
|
||||
}
|
||||
|
||||
|
@@ -449,6 +449,16 @@ public interface AuthorizeService {
|
||||
*/
|
||||
public void removeEPersonPolicies(Context c, DSpaceObject o, EPerson e) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Removes all policies from an eperson that belong to an EPerson.
|
||||
*
|
||||
* @param c current context
|
||||
* @param e the eperson
|
||||
* @throws SQLException if there's a database problem
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void removeAllEPersonPolicies(Context c, EPerson e) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Returns all groups authorized to perform an action on an object. Returns
|
||||
* empty array if no matches.
|
||||
|
@@ -39,6 +39,16 @@ public interface ResourcePolicyService extends DSpaceCRUDService<ResourcePolicy>
|
||||
|
||||
public List<ResourcePolicy> find(Context context, Group group) throws SQLException;
|
||||
|
||||
/**
|
||||
* Retrieve a list of ResourcePolicies by EPerson
|
||||
*
|
||||
* @param c context
|
||||
* @param ePerson the EPerson for which to look up the resource policies
|
||||
* @return a list of ResourcePolicies for the provided EPerson
|
||||
* @throws SQLException if there's a database problem
|
||||
*/
|
||||
public List<ResourcePolicy> find(Context c, EPerson ePerson) throws SQLException;
|
||||
|
||||
public List<ResourcePolicy> find(Context c, EPerson e, List<Group> groups, int action, int type_id)
|
||||
throws SQLException;
|
||||
|
||||
@@ -72,6 +82,16 @@ public interface ResourcePolicyService extends DSpaceCRUDService<ResourcePolicy>
|
||||
public void removeDsoEPersonPolicies(Context context, DSpaceObject dso, EPerson ePerson)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Removes all ResourcePolicies related to an EPerson
|
||||
*
|
||||
* @param context context
|
||||
* @param ePerson the EPerson for which the ResourcePolicies will be deleted
|
||||
* @throws SQLException if there's a database problem
|
||||
* @throws AuthorizeException when the current user is not authorized
|
||||
*/
|
||||
public void removeAllEPersonPolicies(Context context, EPerson ePerson) throws SQLException, AuthorizeException;
|
||||
|
||||
public void removeGroupPolicies(Context c, Group group) throws SQLException;
|
||||
|
||||
public void removeDsoAndTypeNotEqualsToPolicies(Context c, DSpaceObject o, String type)
|
||||
|
@@ -57,7 +57,6 @@ import org.dspace.harvest.HarvestedCollection;
|
||||
import org.dspace.harvest.service.HarvestedCollectionService;
|
||||
import org.dspace.workflow.factory.WorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.WorkflowConfigurationException;
|
||||
import org.dspace.xmlworkflow.XmlWorkflowFactoryImpl;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
|
||||
import org.dspace.xmlworkflow.state.Workflow;
|
||||
import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
|
||||
@@ -387,7 +386,7 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
|
||||
log.error(LogManager.getHeader(context, "setWorkflowGroup",
|
||||
"collection_id=" + collection.getID() + " " + e.getMessage()), e);
|
||||
}
|
||||
if (!StringUtils.equals(XmlWorkflowFactoryImpl.LEGACY_WORKFLOW_NAME, workflow.getID())) {
|
||||
if (!StringUtils.equals(workflowFactory.getDefaultWorkflow().getID(), workflow.getID())) {
|
||||
throw new IllegalArgumentException(
|
||||
"setWorkflowGroup can be used only on collection with the default basic dspace workflow. "
|
||||
+ "Instead, the collection: "
|
||||
|
@@ -629,6 +629,10 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
|
||||
case Constants.DELETE:
|
||||
if (AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion()) {
|
||||
adminObject = getParentObject(context, community);
|
||||
if (adminObject == null) {
|
||||
//top-level community, has to be admin of the current community
|
||||
adminObject = community;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Constants.ADD:
|
||||
|
@@ -207,8 +207,8 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
List<String> values) throws SQLException {
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, List<String> values) throws SQLException {
|
||||
MetadataField metadataField = metadataFieldService.findByElement(context, schema, element, qualifier);
|
||||
if (metadataField == null) {
|
||||
throw new SQLException(
|
||||
@@ -216,12 +216,12 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
"exist!");
|
||||
}
|
||||
|
||||
addMetadata(context, dso, metadataField, lang, values);
|
||||
return addMetadata(context, dso, metadataField, lang, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
List<String> values, List<String> authorities, List<Integer> confidences)
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, List<String> values, List<String> authorities, List<Integer> confidences)
|
||||
throws SQLException {
|
||||
// We will not verify that they are valid entries in the registry
|
||||
// until update() is called.
|
||||
@@ -231,15 +231,16 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
"bad_dublin_core schema=" + schema + "." + element + "." + qualifier + ". Metadata field does not " +
|
||||
"exist!");
|
||||
}
|
||||
addMetadata(context, dso, metadataField, lang, values, authorities, confidences);
|
||||
return addMetadata(context, dso, metadataField, lang, values, authorities, confidences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String lang, List<String> values,
|
||||
List<String> authorities, List<Integer> confidences)
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String lang,
|
||||
List<String> values, List<String> authorities, List<Integer> confidences)
|
||||
throws SQLException {
|
||||
boolean authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField);
|
||||
boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField);
|
||||
List<MetadataValue> newMetadata = new ArrayList<>(values.size());
|
||||
// We will not verify that they are valid entries in the registry
|
||||
// until update() is called.
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
@@ -250,6 +251,7 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
}
|
||||
}
|
||||
MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField);
|
||||
newMetadata.add(metadataValue);
|
||||
//Set place to list length of all metadatavalues for the given schema.element.qualifier combination.
|
||||
// Subtract one to adhere to the 0 as first element rule
|
||||
metadataValue.setPlace(
|
||||
@@ -304,29 +306,31 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
// metadataValueService.update(context, metadataValue);
|
||||
dso.addDetails(metadataField.toString());
|
||||
}
|
||||
return newMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String language, String value,
|
||||
String authority, int confidence) throws SQLException {
|
||||
addMetadata(context, dso, metadataField, language, Arrays.asList(value), Arrays.asList(authority),
|
||||
Arrays.asList(confidence));
|
||||
public MetadataValue addMetadata(Context context, T dso, MetadataField metadataField, String language,
|
||||
String value, String authority, int confidence) throws SQLException {
|
||||
return addMetadata(context, dso, metadataField, language, Arrays.asList(value), Arrays.asList(authority),
|
||||
Arrays.asList(confidence)).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
String value) throws SQLException {
|
||||
addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value));
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value) throws SQLException {
|
||||
return addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value)).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String language, String value)
|
||||
public MetadataValue addMetadata(Context context, T dso, MetadataField metadataField, String language, String value)
|
||||
throws SQLException {
|
||||
addMetadata(context, dso, metadataField, language, Arrays.asList(value));
|
||||
return addMetadata(context, dso, metadataField, language, Arrays.asList(value)).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String language, List<String> values)
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String language,
|
||||
List<String> values)
|
||||
throws SQLException {
|
||||
if (metadataField != null) {
|
||||
String fieldKey = metadataAuthorityService
|
||||
@@ -343,18 +347,19 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
getAuthoritiesAndConfidences(fieldKey, null, values, authorities, confidences, i);
|
||||
}
|
||||
}
|
||||
addMetadata(context, dso, metadataField, language, values, authorities, confidences);
|
||||
return addMetadata(context, dso, metadataField, language, values, authorities, confidences);
|
||||
} else {
|
||||
addMetadata(context, dso, metadataField, language, values, null, null);
|
||||
return addMetadata(context, dso, metadataField, language, values, null, null);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
String value, String authority, int confidence) throws SQLException {
|
||||
addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value), Arrays.asList(authority),
|
||||
Arrays.asList(confidence));
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value, String authority, int confidence) throws SQLException {
|
||||
return addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value),
|
||||
Arrays.asList(authority), Arrays.asList(confidence)).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -664,18 +669,20 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
|
||||
List<MetadataValue> list = getMetadata(dso, schema, element, qualifier);
|
||||
|
||||
clearMetadata(context, dso, schema, element, qualifier, Item.ANY);
|
||||
|
||||
int idx = 0;
|
||||
int place = 0;
|
||||
boolean last = true;
|
||||
for (MetadataValue rr : list) {
|
||||
if (idx == index) {
|
||||
addMetadata(context, dso, schema, element, qualifier,
|
||||
MetadataValue newMetadata = addMetadata(context, dso, schema, element, qualifier,
|
||||
lang, value, authority, confidence);
|
||||
|
||||
moveSingleMetadataValue(context, dso, place, newMetadata);
|
||||
place++;
|
||||
last = false;
|
||||
}
|
||||
addMetadata(context, dso, schema, element, qualifier,
|
||||
rr.getLanguage(), rr.getValue(), rr.getAuthority(), rr.getConfidence());
|
||||
moveSingleMetadataValue(context, dso, place, rr);
|
||||
place++;
|
||||
idx++;
|
||||
}
|
||||
if (last) {
|
||||
@@ -701,8 +708,6 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
"\n Idx from:" + from + " Idx to: " + to);
|
||||
}
|
||||
|
||||
clearMetadata(context, dso, schema, element, qualifier, Item.ANY);
|
||||
|
||||
int idx = 0;
|
||||
MetadataValue moved = null;
|
||||
for (MetadataValue md : list) {
|
||||
@@ -714,49 +719,46 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
int place = 0;
|
||||
boolean last = true;
|
||||
for (MetadataValue rr : list) {
|
||||
if (idx == to && to < from) {
|
||||
addMetadata(context, dso, schema, element, qualifier, moved.getLanguage(), moved.getValue(),
|
||||
moved.getAuthority(), moved.getConfidence());
|
||||
moveSingleMetadataValue(context, dso, place, moved);
|
||||
place++;
|
||||
last = false;
|
||||
}
|
||||
if (idx != from) {
|
||||
addMetadata(context, dso, schema, element, qualifier, rr.getLanguage(), rr.getValue(),
|
||||
rr.getAuthority(), rr.getConfidence());
|
||||
moveSingleMetadataValue(context, dso, place, rr);
|
||||
place++;
|
||||
}
|
||||
if (idx == to && to > from) {
|
||||
addMetadata(context, dso, schema, element, qualifier, moved.getLanguage(), moved.getValue(),
|
||||
moved.getAuthority(), moved.getConfidence());
|
||||
moveSingleMetadataValue(context, dso, place, moved);
|
||||
place++;
|
||||
last = false;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (last) {
|
||||
addMetadata(context, dso, schema, element, qualifier, moved.getLanguage(), moved.getValue(),
|
||||
moved.getAuthority(), moved.getConfidence());
|
||||
moveSingleMetadataValue(context, dso, place, moved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports moving metadata by updating the place of the metadata value
|
||||
*/
|
||||
protected void moveSingleMetadataValue(Context context, T dso, int place, MetadataValue rr) {
|
||||
//just move the metadata
|
||||
rr.setPlace(place);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
String value, String authority, int confidence, int index) throws SQLException {
|
||||
|
||||
List<MetadataValue> list = getMetadata(dso, schema, element, qualifier);
|
||||
|
||||
clearMetadata(context, dso, schema, element, qualifier, Item.ANY);
|
||||
|
||||
int idx = 0;
|
||||
for (MetadataValue rr : list) {
|
||||
if (idx == index) {
|
||||
addMetadata(context, dso, schema, element, qualifier,
|
||||
lang, value, authority, confidence);
|
||||
} else {
|
||||
addMetadata(context, dso, schema, element, qualifier,
|
||||
rr.getLanguage(), rr.getValue(), rr.getAuthority(), rr.getConfidence());
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
removeMetadataValues(context, dso, Arrays.asList(list.get(index)));
|
||||
addAndShiftRightMetadata(context, dso, schema, element, qualifier, lang, value, authority, confidence, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package org.dspace.content;
|
||||
|
||||
import java.util.Objects;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
@@ -15,6 +16,8 @@ import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
|
||||
/**
|
||||
@@ -45,6 +48,7 @@ public class EntityType implements ReloadableEntity<Integer> {
|
||||
|
||||
/**
|
||||
* The standard setter for the ID of this EntityType
|
||||
*
|
||||
* @param id The ID that this EntityType's ID will be set to
|
||||
*/
|
||||
public void setId(Integer id) {
|
||||
@@ -53,6 +57,7 @@ public class EntityType implements ReloadableEntity<Integer> {
|
||||
|
||||
/**
|
||||
* The standard getter for the label of this EntityType
|
||||
*
|
||||
* @return The label for this EntityType
|
||||
*/
|
||||
public String getLabel() {
|
||||
@@ -61,6 +66,7 @@ public class EntityType implements ReloadableEntity<Integer> {
|
||||
|
||||
/**
|
||||
* The standard setter for the label of this EntityType
|
||||
*
|
||||
* @param label The label that this EntityType's label will be set to
|
||||
*/
|
||||
public void setLabel(String label) {
|
||||
@@ -69,9 +75,40 @@ public class EntityType implements ReloadableEntity<Integer> {
|
||||
|
||||
/**
|
||||
* The standard getter for the ID of this EntityType
|
||||
*
|
||||
* @return The ID for this EntityType
|
||||
*/
|
||||
public Integer getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether two entity types are equal based on the id and the label
|
||||
* @param obj object to be compared
|
||||
* @return
|
||||
*/
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof EntityType)) {
|
||||
return false;
|
||||
}
|
||||
EntityType entityType = (EntityType) obj;
|
||||
|
||||
if (!Objects.equals(this.getID(), entityType.getID())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!StringUtils.equals(this.getLabel(), entityType.getLabel())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
* @return hash code value
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder().append(getID()).toHashCode();
|
||||
}
|
||||
}
|
||||
|
@@ -230,6 +230,12 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
|
||||
return itemDAO.findBySubmitter(context, eperson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
throws SQLException {
|
||||
return itemDAO.findBySubmitter(context, eperson, retrieveAllItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findBySubmitterDateSorted(Context context, EPerson eperson, Integer limit)
|
||||
throws SQLException {
|
||||
@@ -1100,19 +1106,7 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
}
|
||||
break;
|
||||
case Constants.DELETE:
|
||||
if (item.getOwningCollection() != null) {
|
||||
if (AuthorizeConfiguration.canCollectionAdminPerformItemDeletion()) {
|
||||
adminObject = collection;
|
||||
} else if (AuthorizeConfiguration.canCommunityAdminPerformItemDeletion()) {
|
||||
adminObject = community;
|
||||
}
|
||||
} else {
|
||||
if (AuthorizeConfiguration.canCollectionAdminManageTemplateItem()) {
|
||||
adminObject = collection;
|
||||
} else if (AuthorizeConfiguration.canCommunityAdminManageCollectionTemplateItem()) {
|
||||
adminObject = community;
|
||||
}
|
||||
}
|
||||
adminObject = item;
|
||||
break;
|
||||
case Constants.WRITE:
|
||||
// if it is a template item we need to check the
|
||||
@@ -1372,6 +1366,32 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports moving metadata by adding the metadata value or updating the place of the relationship
|
||||
*/
|
||||
@Override
|
||||
protected void moveSingleMetadataValue(Context context, Item dso, int place, MetadataValue rr) {
|
||||
if (rr instanceof RelationshipMetadataValue) {
|
||||
try {
|
||||
//Retrieve the applicable relationship
|
||||
Relationship rs = relationshipService.find(context,
|
||||
((RelationshipMetadataValue) rr).getRelationshipId());
|
||||
if (rs.getLeftItem() == dso) {
|
||||
rs.setLeftPlace(place);
|
||||
} else {
|
||||
rs.setRightPlace(place);
|
||||
}
|
||||
relationshipService.update(context, rs);
|
||||
} catch (Exception e) {
|
||||
//should not occur, otherwise metadata can't be updated either
|
||||
log.error("An error occurred while moving " + rr.getAuthority() + " for item " + dso.getID(), e);
|
||||
}
|
||||
} else {
|
||||
//just move the metadata
|
||||
rr.setPlace(place);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will sort the List of MetadataValue objects based on the MetadataSchema, MetadataField Element,
|
||||
* MetadataField Qualifier and MetadataField Place in that order.
|
||||
|
@@ -9,6 +9,8 @@ package org.dspace.content;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
@@ -20,8 +22,12 @@ import org.dspace.content.dao.MetadataFieldDAO;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
import org.dspace.content.service.MetadataSchemaService;
|
||||
import org.dspace.content.service.MetadataValueService;
|
||||
import org.dspace.content.service.SiteService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogManager;
|
||||
import org.dspace.discovery.indexobject.IndexableMetadataField;
|
||||
import org.dspace.event.Event;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
@@ -46,6 +52,8 @@ public class MetadataFieldServiceImpl implements MetadataFieldService {
|
||||
protected MetadataValueService metadataValueService;
|
||||
@Autowired(required = true)
|
||||
protected MetadataSchemaService metadataSchemaService;
|
||||
@Autowired
|
||||
protected SiteService siteService;
|
||||
|
||||
protected MetadataFieldServiceImpl() {
|
||||
|
||||
@@ -77,6 +85,8 @@ public class MetadataFieldServiceImpl implements MetadataFieldService {
|
||||
|
||||
log.info(LogManager.getHeader(context, "create_metadata_field",
|
||||
"metadata_field_id=" + metadataField.getID()));
|
||||
// Update the index of type metadatafield
|
||||
this.triggerEventToUpdateIndex(context, metadataField.getID());
|
||||
return metadataField;
|
||||
}
|
||||
|
||||
@@ -149,6 +159,8 @@ public class MetadataFieldServiceImpl implements MetadataFieldService {
|
||||
"metadata_field_id=" + metadataField.getID() + "element=" + metadataField
|
||||
.getElement()
|
||||
+ "qualifier=" + metadataField.getQualifier()));
|
||||
// Update the index of type metadatafield
|
||||
this.triggerEventToUpdateIndex(context, metadataField.getID());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,6 +189,21 @@ public class MetadataFieldServiceImpl implements MetadataFieldService {
|
||||
|
||||
log.info(LogManager.getHeader(context, "delete_metadata_field",
|
||||
"metadata_field_id=" + metadataField.getID()));
|
||||
// Update the index of type metadatafield
|
||||
this.triggerEventToUpdateIndex(context, metadataField.getID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a MODIFY SITE event with the identifier of the changed mdField, so it can be indexed in
|
||||
* {@link org.dspace.discovery.IndexEventConsumer}, with type of {@link org.dspace.discovery.IndexableObject} in
|
||||
* {@link Event}.detail and the identifiers of the changed mdFields in {@link Event}.identifiers
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param mdFieldId ID of the metadata field that needs to be (re)indexed
|
||||
*/
|
||||
private void triggerEventToUpdateIndex(Context context, int mdFieldId) {
|
||||
context.addEvent(new Event(Event.MODIFY, Constants.SITE, null, IndexableMetadataField.TYPE, new ArrayList<>(
|
||||
Arrays.asList(Integer.toString(mdFieldId)))));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
package org.dspace.content;
|
||||
|
||||
import org.dspace.core.Constants;
|
||||
|
||||
/**
|
||||
* This class is used as a representation of MetadataValues for the MetadataValues that are derived from the
|
||||
* Relationships that the item has. This includes the useForPlace property which we'll have to use to determine
|
||||
@@ -57,4 +59,13 @@ public class RelationshipMetadataValue extends MetadataValue {
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Relationship ID from which the current RelationshipMetadataValue is derived
|
||||
*
|
||||
* @return the relationship ID
|
||||
*/
|
||||
public int getRelationshipId() {
|
||||
return Integer.parseInt(getAuthority().substring(Constants.VIRTUAL_AUTHORITY_PREFIX.length()));
|
||||
}
|
||||
}
|
||||
|
@@ -212,9 +212,8 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
|
||||
*/
|
||||
Item item = workspaceItem.getItem();
|
||||
if (!authorizeService.isAdmin(context)
|
||||
&& ((context.getCurrentUser() == null) || (context
|
||||
.getCurrentUser().getID() != item.getSubmitter()
|
||||
.getID()))) {
|
||||
&& (item.getSubmitter() == null || (context.getCurrentUser() == null)
|
||||
|| (context.getCurrentUser().getID() != item.getSubmitter().getID()))) {
|
||||
// Not an admit, not the submitter
|
||||
throw new AuthorizeException("Must be an administrator or the "
|
||||
+ "original submitter to delete a workspace item");
|
||||
@@ -265,7 +264,12 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
|
||||
|
||||
// Need to delete the workspaceitem row first since it refers
|
||||
// to item ID
|
||||
try {
|
||||
workspaceItem.getSupervisorGroups().clear();
|
||||
} catch (Exception e) {
|
||||
log.error("failed to clear supervisor group", e);
|
||||
}
|
||||
|
||||
workspaceItemDAO.delete(context, workspaceItem);
|
||||
|
||||
}
|
||||
|
@@ -33,21 +33,62 @@ public class Choice {
|
||||
*/
|
||||
public String value = null;
|
||||
|
||||
/**
|
||||
* A boolean representing if choice entry value can selected (usually true).
|
||||
* Hierarchical authority can flag some choice as not selectable to force the
|
||||
* use to choice a more detailed terms in the tree, such a leaf or a deeper
|
||||
* branch
|
||||
*/
|
||||
public boolean selectable = true;
|
||||
|
||||
public Map<String, String> extras = new HashMap<String, String>();
|
||||
|
||||
public Choice() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal constructor for this data object. It assumes an empty map of extras
|
||||
* information and a selected choice
|
||||
*
|
||||
* @param authority the authority key
|
||||
* @param value the text value to store in the metadata
|
||||
* @param label the value to display to the user
|
||||
*/
|
||||
public Choice(String authority, String value, String label) {
|
||||
this.authority = authority;
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to quickly setup the data object for basic authorities. The choice is assumed to be selectable.
|
||||
*
|
||||
* @param authority the authority key
|
||||
* @param value the text value to store in the metadata
|
||||
* @param label the value to display to the user
|
||||
* @param extras a key value map of extra information related to this choice
|
||||
*/
|
||||
public Choice(String authority, String label, String value, Map<String, String> extras) {
|
||||
this.authority = authority;
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
this.extras = extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for common need of Hierarchical authorities that want to
|
||||
* explicitely set the selectable flag
|
||||
*
|
||||
* @param authority the authority key
|
||||
* @param value the text value to store in the metadata
|
||||
* @param label the value to display to the user
|
||||
* @param selectable true if the choice can be selected, false if the a more
|
||||
* accurate choice should be preferred
|
||||
*/
|
||||
public Choice(String authority, String label, String value, boolean selectable) {
|
||||
this.authority = authority;
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
this.selectable = selectable;
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,10 @@
|
||||
*/
|
||||
package org.dspace.content.authority;
|
||||
|
||||
import org.dspace.content.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.dspace.core.NameAwarePlugin;
|
||||
|
||||
/**
|
||||
* Plugin interface that supplies an authority control mechanism for
|
||||
@@ -17,7 +20,7 @@ import org.dspace.content.Collection;
|
||||
* @see ChoiceAuthorityServiceImpl
|
||||
* @see MetadataAuthorityServiceImpl
|
||||
*/
|
||||
public interface ChoiceAuthority {
|
||||
public interface ChoiceAuthority extends NameAwarePlugin {
|
||||
/**
|
||||
* Get all values from the authority that match the preferred value.
|
||||
* Note that the offering was entered by the user and may contain
|
||||
@@ -32,15 +35,13 @@ public interface ChoiceAuthority {
|
||||
* defaultSelected index in the Choices instance to the choice, if any,
|
||||
* that matches the value.
|
||||
*
|
||||
* @param field being matched for
|
||||
* @param text user's value to match
|
||||
* @param collection database ID of Collection for context (owner of Item)
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
*/
|
||||
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale);
|
||||
public Choices getMatches(String text, int start, int limit, String locale);
|
||||
|
||||
/**
|
||||
* Get the single "best" match (if any) of a value in the authority
|
||||
@@ -51,13 +52,11 @@ public interface ChoiceAuthority {
|
||||
* This call is typically used in non-interactive metadata ingest
|
||||
* where there is no interactive agent to choose from among options.
|
||||
*
|
||||
* @param field being matched for
|
||||
* @param text user's value to match
|
||||
* @param collection database ID of Collection for context (owner of Item)
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null) with 1 or 0 values.
|
||||
*/
|
||||
public Choices getBestMatch(String field, String text, Collection collection, String locale);
|
||||
public Choices getBestMatch(String text, String locale);
|
||||
|
||||
/**
|
||||
* Get the canonical user-visible "label" (i.e. short descriptive text)
|
||||
@@ -67,31 +66,97 @@ public interface ChoiceAuthority {
|
||||
* This may get called many times while populating a Web page so it should
|
||||
* be implemented as efficiently as possible.
|
||||
*
|
||||
* @param field being matched for
|
||||
* @param key authority key known to this authority.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return descriptive label - should always return something, never null.
|
||||
*/
|
||||
public String getLabel(String field, String key, String locale);
|
||||
public String getLabel(String key, String locale);
|
||||
|
||||
/**
|
||||
* Get the canonical value to store for a key in the authority. Can be localized
|
||||
* given the implicit or explicit locale specification.
|
||||
*
|
||||
* @param key authority key known to this authority.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return value to store - should always return something, never null.
|
||||
*/
|
||||
default String getValue(String key, String locale) {
|
||||
return getLabel(key, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of additional information related to the specified key in the
|
||||
* authority.
|
||||
*
|
||||
* @param key the key of the entry
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a map of additional information related to the key
|
||||
*/
|
||||
default Map<String, String> getExtra(String key, String locale) {
|
||||
return new HashMap<String, String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true for hierarchical authorities
|
||||
*
|
||||
* @return <code>true</code> if hierarchical, default <code>false</code>
|
||||
*/
|
||||
default boolean isHierarchical() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrollable authorities allows the scroll of the entries without applying
|
||||
* filter/query to the
|
||||
* {@link #getMatches(String, String, Collection, int, int, String)}
|
||||
*
|
||||
* @return <code>true</code> if scrollable, default <code>false</code>
|
||||
*/
|
||||
default boolean isScrollable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean hasIdentifier() {
|
||||
return true;
|
||||
/**
|
||||
* Hierarchical authority can provide an hint for the UI about how many levels
|
||||
* preload to improve the UX. It provides a valid default for hierarchical
|
||||
* authorities
|
||||
*
|
||||
* @return <code>0</code> if hierarchical, null otherwise
|
||||
*/
|
||||
default Integer getPreloadLevel() {
|
||||
return isHierarchical() ? 0 : null;
|
||||
}
|
||||
|
||||
default public Choice getChoice(String fieldKey, String authKey, String locale) {
|
||||
/**
|
||||
* Build the preferred choice associated with the authKey. The default
|
||||
* implementation delegate the creato to the {@link #getLabel(String, String)}
|
||||
* {@link #getValue(String, String)} and {@link #getExtra(String, String)}
|
||||
* methods but can be directly overriden for better efficiency or special
|
||||
* scenario
|
||||
*
|
||||
* @param authKey authority key known to this authority.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return the preferred choice for this authKey and locale
|
||||
*/
|
||||
default public Choice getChoice(String authKey, String locale) {
|
||||
Choice result = new Choice();
|
||||
result.authority = authKey;
|
||||
result.label = getLabel(fieldKey, authKey, locale);
|
||||
result.value = getLabel(fieldKey, authKey, locale);
|
||||
result.label = getLabel(authKey, locale);
|
||||
result.value = getValue(authKey, locale);
|
||||
result.extras.putAll(getExtra(authKey, locale));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a recommendation to store the authority in the metadata value if
|
||||
* available in the in the provided choice(s). Usually ChoiceAuthority should
|
||||
* recommend that so the default is true and it only need to be implemented in
|
||||
* the unusual scenario
|
||||
*
|
||||
* @return <code>true</code> if the authority provided in any choice of this
|
||||
* authority should be stored in the metadata value
|
||||
*/
|
||||
default public boolean storeAuthorityInMetadata() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -7,10 +7,13 @@
|
||||
*/
|
||||
package org.dspace.content.authority;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -19,6 +22,9 @@ import org.dspace.app.util.DCInput;
|
||||
import org.dspace.app.util.DCInputSet;
|
||||
import org.dspace.app.util.DCInputsReader;
|
||||
import org.dspace.app.util.DCInputsReaderException;
|
||||
import org.dspace.app.util.SubmissionConfig;
|
||||
import org.dspace.app.util.SubmissionConfigReader;
|
||||
import org.dspace.app.util.SubmissionConfigReaderException;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.authority.service.ChoiceAuthorityService;
|
||||
@@ -54,23 +60,37 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
// map of field key to authority plugin
|
||||
protected Map<String, ChoiceAuthority> controller = new HashMap<String, ChoiceAuthority>();
|
||||
|
||||
// map of field key, form definition to authority plugin
|
||||
protected Map<String, Map<String, ChoiceAuthority>> controllerFormDefinitions =
|
||||
new HashMap<String, Map<String, ChoiceAuthority>>();
|
||||
|
||||
// map of field key to presentation type
|
||||
protected Map<String, String> presentation = new HashMap<String, String>();
|
||||
|
||||
// map of field key to closed value
|
||||
protected Map<String, Boolean> closed = new HashMap<String, Boolean>();
|
||||
|
||||
// map of authority name to field key
|
||||
protected Map<String, String> authorities = new HashMap<String, String>();
|
||||
// flag to track the initialization status of the service
|
||||
private boolean initialized = false;
|
||||
|
||||
// map of authority name to field keys (the same authority can be configured over multiple metadata)
|
||||
protected Map<String, List<String>> authorities = new HashMap<String, List<String>>();
|
||||
|
||||
// map of authority name to form definition and field keys
|
||||
protected Map<String, Map<String, List<String>>> authoritiesFormDefinitions =
|
||||
new HashMap<String, Map<String, List<String>>>();
|
||||
|
||||
// the item submission reader
|
||||
private SubmissionConfigReader itemSubmissionConfigReader;
|
||||
|
||||
@Autowired(required = true)
|
||||
protected ConfigurationService configurationService;
|
||||
@Autowired(required = true)
|
||||
protected PluginService pluginService;
|
||||
|
||||
private final String CHOICES_PLUGIN_PREFIX = "choices.plugin.";
|
||||
private final String CHOICES_PRESENTATION_PREFIX = "choices.presentation.";
|
||||
private final String CHOICES_CLOSED_PREFIX = "choices.closed.";
|
||||
final static String CHOICES_PLUGIN_PREFIX = "choices.plugin.";
|
||||
final static String CHOICES_PRESENTATION_PREFIX = "choices.presentation.";
|
||||
final static String CHOICES_CLOSED_PREFIX = "choices.closed.";
|
||||
|
||||
protected ChoiceAuthorityServiceImpl() {
|
||||
}
|
||||
@@ -96,10 +116,25 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
|
||||
@Override
|
||||
public Set<String> getChoiceAuthoritiesNames() {
|
||||
if (authorities.keySet().isEmpty()) {
|
||||
loadChoiceAuthorityConfigurations();
|
||||
init();
|
||||
Set<String> authoritiesNames = new HashSet<String>();
|
||||
authoritiesNames.addAll(authorities.keySet());
|
||||
authoritiesNames.addAll(authoritiesFormDefinitions.keySet());
|
||||
return authoritiesNames;
|
||||
}
|
||||
|
||||
private synchronized void init() {
|
||||
if (!initialized) {
|
||||
try {
|
||||
itemSubmissionConfigReader = new SubmissionConfigReader();
|
||||
} catch (SubmissionConfigReaderException e) {
|
||||
// the system is in an illegal state as the submission definition is not valid
|
||||
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
|
||||
e);
|
||||
}
|
||||
loadChoiceAuthorityConfigurations();
|
||||
initialized = true;
|
||||
}
|
||||
return authorities.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,59 +147,62 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
@Override
|
||||
public Choices getMatches(String fieldKey, String query, Collection collection,
|
||||
int start, int limit, String locale) {
|
||||
ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey);
|
||||
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
|
||||
if (ma == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No choices plugin was configured for field \"" + fieldKey
|
||||
+ "\".");
|
||||
+ "\", collection=" + collection.getID().toString() + ".");
|
||||
}
|
||||
return ma.getMatches(fieldKey, query, collection, start, limit, locale);
|
||||
return ma.getMatches(query, start, limit, locale);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String fieldKey, String query, Collection collection, int start, int limit, String locale,
|
||||
boolean externalInput) {
|
||||
ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey);
|
||||
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
|
||||
if (ma == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No choices plugin was configured for field \"" + fieldKey
|
||||
+ "\".");
|
||||
+ "\", collection=" + collection.getID().toString() + ".");
|
||||
}
|
||||
if (externalInput && ma instanceof SolrAuthority) {
|
||||
((SolrAuthority) ma).addExternalResultsInNextMatches();
|
||||
}
|
||||
return ma.getMatches(fieldKey, query, collection, start, limit, locale);
|
||||
return ma.getMatches(query, start, limit, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String fieldKey, String query, Collection collection,
|
||||
String locale) {
|
||||
ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey);
|
||||
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
|
||||
if (ma == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No choices plugin was configured for field \"" + fieldKey
|
||||
+ "\".");
|
||||
+ "\", collection=" + collection.getID().toString() + ".");
|
||||
}
|
||||
return ma.getBestMatch(fieldKey, query, collection, locale);
|
||||
return ma.getBestMatch(query, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(MetadataValue metadataValue, String locale) {
|
||||
return getLabel(metadataValue.getMetadataField().toString(), metadataValue.getAuthority(), locale);
|
||||
public String getLabel(MetadataValue metadataValue, Collection collection, String locale) {
|
||||
return getLabel(metadataValue.getMetadataField().toString(), collection, metadataValue.getAuthority(), locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String fieldKey, String authKey, String locale) {
|
||||
ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey);
|
||||
public String getLabel(String fieldKey, Collection collection, String authKey, String locale) {
|
||||
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
|
||||
if (ma == null) {
|
||||
throw new IllegalArgumentException("No choices plugin was configured for field \"" + fieldKey + "\".");
|
||||
throw new IllegalArgumentException(
|
||||
"No choices plugin was configured for field \"" + fieldKey
|
||||
+ "\", collection=" + collection.getID().toString() + ".");
|
||||
}
|
||||
return ma.getLabel(fieldKey, authKey, locale);
|
||||
return ma.getLabel(authKey, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChoicesConfigured(String fieldKey) {
|
||||
return getChoiceAuthorityMap().containsKey(fieldKey);
|
||||
public boolean isChoicesConfigured(String fieldKey, Collection collection) {
|
||||
return getAuthorityByFieldKeyCollection(fieldKey, collection) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,8 +216,14 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getVariants(MetadataValue metadataValue) {
|
||||
ChoiceAuthority ma = getChoiceAuthorityMap().get(metadataValue.getMetadataField().toString());
|
||||
public List<String> getVariants(MetadataValue metadataValue, Collection collection) {
|
||||
String fieldKey = metadataValue.getMetadataField().toString();
|
||||
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
|
||||
if (ma == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No choices plugin was configured for field \"" + fieldKey
|
||||
+ "\", collection=" + collection.getID().toString() + ".");
|
||||
}
|
||||
if (ma instanceof AuthorityVariantsSupport) {
|
||||
AuthorityVariantsSupport avs = (AuthorityVariantsSupport) ma;
|
||||
return avs.getVariants(metadataValue.getAuthority(), metadataValue.getLanguage());
|
||||
@@ -189,42 +233,53 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
|
||||
|
||||
@Override
|
||||
public String getChoiceAuthorityName(String schema, String element, String qualifier) {
|
||||
String makeFieldKey = makeFieldKey(schema, element, qualifier);
|
||||
if (getChoiceAuthorityMap().containsKey(makeFieldKey)) {
|
||||
for (String key : this.authorities.keySet()) {
|
||||
if (this.authorities.get(key).equals(makeFieldKey)) {
|
||||
return key;
|
||||
public String getChoiceAuthorityName(String schema, String element, String qualifier, Collection collection) {
|
||||
init();
|
||||
String fieldKey = makeFieldKey(schema, element, qualifier);
|
||||
// check if there is an authority configured for the metadata valid for all the collections
|
||||
if (controller.containsKey(fieldKey)) {
|
||||
for (Entry<String, List<String>> authority2md : authorities.entrySet()) {
|
||||
if (authority2md.getValue().contains(fieldKey)) {
|
||||
return authority2md.getKey();
|
||||
}
|
||||
}
|
||||
} else if (collection != null && controllerFormDefinitions.containsKey(fieldKey)) {
|
||||
// there is an authority configured for the metadata valid for some collections,
|
||||
// check if it is the requested collection
|
||||
Map<String, ChoiceAuthority> controllerFormDef = controllerFormDefinitions.get(fieldKey);
|
||||
SubmissionConfig submissionConfig = itemSubmissionConfigReader
|
||||
.getSubmissionConfigByCollection(collection.getHandle());
|
||||
String submissionName = submissionConfig.getSubmissionName();
|
||||
// check if the requested collection has a submission definition that use an authority for the metadata
|
||||
if (controllerFormDef.containsKey(submissionName)) {
|
||||
for (Entry<String, Map<String, List<String>>> authority2defs2md :
|
||||
authoritiesFormDefinitions.entrySet()) {
|
||||
List<String> mdByDefinition = authority2defs2md.getValue().get(submissionName);
|
||||
if (mdByDefinition != null && mdByDefinition.contains(fieldKey)) {
|
||||
return authority2defs2md.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
return configurationService.getProperty(
|
||||
CHOICES_PLUGIN_PREFIX + schema + "." + element + (qualifier != null ? "." + qualifier : ""));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String makeFieldKey(String schema, String element, String qualifier) {
|
||||
return Utils.standardize(schema, element, qualifier, "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return map of key to ChoiceAuthority plugin
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Map<String, ChoiceAuthority> getChoiceAuthorityMap() {
|
||||
// If empty, load from configuration
|
||||
if (controller.isEmpty()) {
|
||||
loadChoiceAuthorityConfigurations();
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
controller.clear();
|
||||
authorities.clear();
|
||||
presentation.clear();
|
||||
closed.clear();
|
||||
controllerFormDefinitions.clear();
|
||||
authoritiesFormDefinitions.clear();
|
||||
itemSubmissionConfigReader = null;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private void loadChoiceAuthorityConfigurations() {
|
||||
// Get all configuration keys starting with a given prefix
|
||||
List<String> propKeys = configurationService.getPropertyKeys(CHOICES_PLUGIN_PREFIX);
|
||||
@@ -249,71 +304,127 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
"Skipping invalid configuration for " + key + " because named plugin not found: " + authorityName);
|
||||
continue;
|
||||
}
|
||||
if (!authorities.containsKey(authorityName)) {
|
||||
controller.put(fkey, ma);
|
||||
authorities.put(authorityName, fkey);
|
||||
} else {
|
||||
log.warn(
|
||||
"Skipping invalid configuration for " + key + " because plugin is alredy in use: " +
|
||||
authorityName + " used by " + authorities
|
||||
.get(authorityName));
|
||||
continue;
|
||||
}
|
||||
|
||||
controller.put(fkey, ma);
|
||||
List<String> fkeys;
|
||||
if (authorities.containsKey(authorityName)) {
|
||||
fkeys = authorities.get(authorityName);
|
||||
} else {
|
||||
fkeys = new ArrayList<String>();
|
||||
}
|
||||
fkeys.add(fkey);
|
||||
authorities.put(authorityName, fkeys);
|
||||
log.debug("Choice Control: For field=" + fkey + ", Plugin=" + ma);
|
||||
}
|
||||
autoRegisterChoiceAuthorityFromInputReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will register all the authorities that are required due to the
|
||||
* submission forms configuration. This includes authorities for value pairs and
|
||||
* xml vocabularies
|
||||
*/
|
||||
private void autoRegisterChoiceAuthorityFromInputReader() {
|
||||
try {
|
||||
List<SubmissionConfig> submissionConfigs = itemSubmissionConfigReader
|
||||
.getAllSubmissionConfigs(Integer.MAX_VALUE, 0);
|
||||
DCInputsReader dcInputsReader = new DCInputsReader();
|
||||
for (DCInputSet dcinputSet : dcInputsReader.getAllInputs(Integer.MAX_VALUE, 0)) {
|
||||
|
||||
// loop over all the defined item submission configuration
|
||||
for (SubmissionConfig subCfg : submissionConfigs) {
|
||||
String submissionName = subCfg.getSubmissionName();
|
||||
List<DCInputSet> inputsBySubmissionName = dcInputsReader.getInputsBySubmissionName(submissionName);
|
||||
// loop over the submission forms configuration eventually associated with the submission panel
|
||||
for (DCInputSet dcinputSet : inputsBySubmissionName) {
|
||||
DCInput[][] dcinputs = dcinputSet.getFields();
|
||||
for (DCInput[] dcrows : dcinputs) {
|
||||
for (DCInput dcinput : dcrows) {
|
||||
// for each input in the form check if it is associated with a real value pairs
|
||||
// or an xml vocabulary
|
||||
String authorityName = null;
|
||||
if (StringUtils.isNotBlank(dcinput.getPairsType())
|
||||
|| StringUtils.isNotBlank(dcinput.getVocabulary())) {
|
||||
String authorityName = dcinput.getPairsType();
|
||||
if (StringUtils.isBlank(authorityName)) {
|
||||
&& !StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
|
||||
authorityName = dcinput.getPairsType();
|
||||
} else if (StringUtils.isNotBlank(dcinput.getVocabulary())) {
|
||||
authorityName = dcinput.getVocabulary();
|
||||
}
|
||||
if (!StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
|
||||
|
||||
// do we have an authority?
|
||||
if (StringUtils.isNotBlank(authorityName)) {
|
||||
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
|
||||
dcinput.getQualifier());
|
||||
ChoiceAuthority ca = controller.get(authorityName);
|
||||
if (ca == null) {
|
||||
InputFormSelfRegisterWrapperAuthority ifa = new
|
||||
InputFormSelfRegisterWrapperAuthority();
|
||||
if (controller.containsKey(fieldKey)) {
|
||||
ifa = (InputFormSelfRegisterWrapperAuthority) controller.get(fieldKey);
|
||||
}
|
||||
|
||||
ChoiceAuthority ma = (ChoiceAuthority) pluginService
|
||||
ca = (ChoiceAuthority) pluginService
|
||||
.getNamedPlugin(ChoiceAuthority.class, authorityName);
|
||||
if (ma == null) {
|
||||
log.warn("Skipping invalid configuration for " + fieldKey
|
||||
+ " because named plugin not found: " + authorityName);
|
||||
continue;
|
||||
if (ca == null) {
|
||||
throw new IllegalStateException("Invalid configuration for " + fieldKey
|
||||
+ " in submission definition " + submissionName
|
||||
+ ", form definition " + dcinputSet.getFormName()
|
||||
+ " no named plugin found: " + authorityName);
|
||||
}
|
||||
ifa.getDelegates().put(dcinputSet.getFormName(), ma);
|
||||
controller.put(fieldKey, ifa);
|
||||
}
|
||||
|
||||
if (!authorities.containsKey(authorityName)) {
|
||||
authorities.put(authorityName, fieldKey);
|
||||
}
|
||||
|
||||
addAuthorityToFormCacheMap(submissionName, fieldKey, ca);
|
||||
addFormDetailsToAuthorityCacheMap(submissionName, authorityName, fieldKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DCInputsReaderException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
// the system is in an illegal state as the submission definition is not valid
|
||||
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the form/field to the cache map keeping track of which form/field are
|
||||
* associated with the specific authority name
|
||||
*
|
||||
* @param submissionName the form definition name
|
||||
* @param authorityName the name of the authority plugin
|
||||
* @param fieldKey the field key that use the authority
|
||||
*/
|
||||
private void addFormDetailsToAuthorityCacheMap(String submissionName, String authorityName, String fieldKey) {
|
||||
Map<String, List<String>> submissionDefinitionNames2fieldKeys;
|
||||
if (authoritiesFormDefinitions.containsKey(authorityName)) {
|
||||
submissionDefinitionNames2fieldKeys = authoritiesFormDefinitions.get(authorityName);
|
||||
} else {
|
||||
submissionDefinitionNames2fieldKeys = new HashMap<String, List<String>>();
|
||||
}
|
||||
|
||||
List<String> fields;
|
||||
if (submissionDefinitionNames2fieldKeys.containsKey(submissionName)) {
|
||||
fields = submissionDefinitionNames2fieldKeys.get(submissionName);
|
||||
} else {
|
||||
fields = new ArrayList<String>();
|
||||
}
|
||||
fields.add(fieldKey);
|
||||
submissionDefinitionNames2fieldKeys.put(submissionName, fields);
|
||||
authoritiesFormDefinitions.put(authorityName, submissionDefinitionNames2fieldKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the authority plugin to the cache map keeping track of which authority is
|
||||
* used by a specific form/field
|
||||
*
|
||||
* @param submissionName the submission definition name
|
||||
* @param fieldKey the field key that require the authority
|
||||
* @param ca the authority plugin
|
||||
*/
|
||||
private void addAuthorityToFormCacheMap(String submissionName, String fieldKey, ChoiceAuthority ca) {
|
||||
Map<String, ChoiceAuthority> definition2authority;
|
||||
if (controllerFormDefinitions.containsKey(fieldKey)) {
|
||||
definition2authority = controllerFormDefinitions.get(fieldKey);
|
||||
} else {
|
||||
definition2authority = new HashMap<String, ChoiceAuthority>();
|
||||
}
|
||||
definition2authority.put(submissionName, ca);
|
||||
controllerFormDefinitions.put(fieldKey, definition2authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return map of key to presentation
|
||||
*
|
||||
@@ -370,26 +481,6 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChoiceMetadatabyAuthorityName(String name) {
|
||||
if (authorities.isEmpty()) {
|
||||
loadChoiceAuthorityConfigurations();
|
||||
}
|
||||
if (authorities.containsKey(name)) {
|
||||
return authorities.get(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choice getChoice(String fieldKey, String authKey, String locale) {
|
||||
ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey);
|
||||
if (ma == null) {
|
||||
throw new IllegalArgumentException("No choices plugin was configured for field \"" + fieldKey + "\".");
|
||||
}
|
||||
return ma.getChoice(fieldKey, authKey, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceAuthority getChoiceAuthorityByAuthorityName(String authorityName) {
|
||||
ChoiceAuthority ma = (ChoiceAuthority)
|
||||
@@ -401,4 +492,68 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
}
|
||||
return ma;
|
||||
}
|
||||
|
||||
private ChoiceAuthority getAuthorityByFieldKeyCollection(String fieldKey, Collection collection) {
|
||||
init();
|
||||
ChoiceAuthority ma = controller.get(fieldKey);
|
||||
if (ma == null && collection != null) {
|
||||
SubmissionConfigReader configReader;
|
||||
try {
|
||||
configReader = new SubmissionConfigReader();
|
||||
SubmissionConfig submissionName = configReader.getSubmissionConfigByCollection(collection.getHandle());
|
||||
ma = controllerFormDefinitions.get(fieldKey).get(submissionName.getSubmissionName());
|
||||
} catch (SubmissionConfigReaderException e) {
|
||||
// the system is in an illegal state as the submission definition is not valid
|
||||
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
return ma;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean storeAuthority(String fieldKey, Collection collection) {
|
||||
// currently only named authority can eventually provide real authority
|
||||
return controller.containsKey(fieldKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that calls getChoicesByParent method of the plugin.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param parentId parent Id
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
* @see org.dspace.content.authority.ChoiceAuthority#getChoicesByParent(java.lang.String, java.lang.String,
|
||||
* int, int, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Choices getChoicesByParent(String authorityName, String parentId, int start, int limit, String locale) {
|
||||
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
|
||||
return ma.getChoicesByParent(authorityName, parentId, start, limit, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that calls getTopChoices method of the plugin.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
* @see org.dspace.content.authority.ChoiceAuthority#getTopChoices(java.lang.String, int, int, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Choices getTopChoices(String authorityName, int start, int limit, String locale) {
|
||||
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
|
||||
return ma.getTopChoices(authorityName, start, limit, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choice getParentChoice(String authorityName, String vocabularyId, String locale) {
|
||||
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
|
||||
return ma.getParentChoice(authorityName, vocabularyId, locale);
|
||||
}
|
||||
}
|
||||
|
@@ -9,14 +9,20 @@ package org.dspace.content.authority;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.DCInputsReader;
|
||||
import org.dspace.app.util.DCInputsReaderException;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.core.I18nUtil;
|
||||
import org.dspace.core.SelfNamedPlugin;
|
||||
|
||||
/**
|
||||
@@ -44,16 +50,38 @@ import org.dspace.core.SelfNamedPlugin;
|
||||
public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority {
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(DCInputAuthority.class);
|
||||
|
||||
private String values[] = null;
|
||||
private String labels[] = null;
|
||||
/**
|
||||
* The map of the values available for a specific language. Examples of keys are
|
||||
* "en", "it", "uk"
|
||||
*/
|
||||
private Map<String, String[]> values = null;
|
||||
|
||||
private static DCInputsReader dci = null;
|
||||
/**
|
||||
* The map of the labels available for a specific language. Examples of keys are
|
||||
* "en", "it", "uk"
|
||||
*/
|
||||
private Map<String, String[]> labels = null;
|
||||
|
||||
/**
|
||||
* The map of the input form reader associated to use for a specific java locale
|
||||
*/
|
||||
private static Map<Locale, DCInputsReader> dcis = null;
|
||||
private static String pluginNames[] = null;
|
||||
|
||||
public DCInputAuthority() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean storeAuthorityInMetadata() {
|
||||
// For backward compatibility value pairs don't store authority in
|
||||
// the metadatavalue
|
||||
return false;
|
||||
}
|
||||
public static void reset() {
|
||||
pluginNames = null;
|
||||
}
|
||||
|
||||
public static String[] getPluginNames() {
|
||||
if (pluginNames == null) {
|
||||
initPluginNames();
|
||||
@@ -63,20 +91,28 @@ public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority
|
||||
}
|
||||
|
||||
private static synchronized void initPluginNames() {
|
||||
Locale[] locales = I18nUtil.getSupportedLocales();
|
||||
Set<String> names = new HashSet<String>();
|
||||
if (pluginNames == null) {
|
||||
try {
|
||||
if (dci == null) {
|
||||
dci = new DCInputsReader();
|
||||
dcis = new HashMap<Locale, DCInputsReader>();
|
||||
for (Locale locale : locales) {
|
||||
dcis.put(locale, new DCInputsReader(I18nUtil.getInputFormsFileName(locale)));
|
||||
}
|
||||
for (Locale l : locales) {
|
||||
Iterator pi = dcis.get(l).getPairsNameIterator();
|
||||
while (pi.hasNext()) {
|
||||
names.add((String) pi.next());
|
||||
}
|
||||
}
|
||||
DCInputsReader dcirDefault = new DCInputsReader();
|
||||
Iterator pi = dcirDefault.getPairsNameIterator();
|
||||
while (pi.hasNext()) {
|
||||
names.add((String) pi.next());
|
||||
}
|
||||
} catch (DCInputsReaderException e) {
|
||||
log.error("Failed reading DCInputs initialization: ", e);
|
||||
}
|
||||
List<String> names = new ArrayList<String>();
|
||||
Iterator pi = dci.getPairsNameIterator();
|
||||
while (pi.hasNext()) {
|
||||
names.add((String) pi.next());
|
||||
}
|
||||
|
||||
pluginNames = names.toArray(new String[names.size()]);
|
||||
log.debug("Got plugin names = " + Arrays.deepToString(pluginNames));
|
||||
}
|
||||
@@ -85,45 +121,65 @@ public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority
|
||||
// once-only load of values and labels
|
||||
private void init() {
|
||||
if (values == null) {
|
||||
values = new HashMap<String, String[]>();
|
||||
labels = new HashMap<String, String[]>();
|
||||
String pname = this.getPluginInstanceName();
|
||||
for (Locale l : dcis.keySet()) {
|
||||
DCInputsReader dci = dcis.get(l);
|
||||
List<String> pairs = dci.getPairs(pname);
|
||||
if (pairs != null) {
|
||||
values = new String[pairs.size() / 2];
|
||||
labels = new String[pairs.size() / 2];
|
||||
String[] valuesLocale = new String[pairs.size() / 2];
|
||||
String[]labelsLocale = new String[pairs.size() / 2];
|
||||
for (int i = 0; i < pairs.size(); i += 2) {
|
||||
labels[i / 2] = pairs.get(i);
|
||||
values[i / 2] = pairs.get(i + 1);
|
||||
labelsLocale[i / 2] = pairs.get(i);
|
||||
valuesLocale[i / 2] = pairs.get(i + 1);
|
||||
}
|
||||
log.debug("Found pairs for name=" + pname);
|
||||
values.put(l.getLanguage(), valuesLocale);
|
||||
labels.put(l.getLanguage(), labelsLocale);
|
||||
log.debug("Found pairs for name=" + pname + ",locale=" + l);
|
||||
} else {
|
||||
log.error("Failed to find any pairs for name=" + pname, new IllegalStateException());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String field, String query, Collection collection, int start, int limit, String locale) {
|
||||
public Choices getMatches(String query, int start, int limit, String locale) {
|
||||
init();
|
||||
|
||||
Locale currentLocale = I18nUtil.getSupportedLocale(locale);
|
||||
String[] valuesLocale = values.get(currentLocale.getLanguage());
|
||||
String[] labelsLocale = labels.get(currentLocale.getLanguage());
|
||||
int dflt = -1;
|
||||
Choice v[] = new Choice[values.length];
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
v[i] = new Choice(values[i], values[i], labels[i]);
|
||||
if (values[i].equalsIgnoreCase(query)) {
|
||||
int found = 0;
|
||||
List<Choice> v = new ArrayList<Choice>();
|
||||
for (int i = 0; i < valuesLocale.length; ++i) {
|
||||
if (query == null || StringUtils.containsIgnoreCase(valuesLocale[i], query)) {
|
||||
if (found >= start && v.size() < limit) {
|
||||
v.add(new Choice(null, valuesLocale[i], labelsLocale[i]));
|
||||
if (valuesLocale[i].equalsIgnoreCase(query)) {
|
||||
dflt = i;
|
||||
}
|
||||
}
|
||||
return new Choices(v, 0, v.length, Choices.CF_AMBIGUOUS, false, dflt);
|
||||
found++;
|
||||
}
|
||||
}
|
||||
Choice[] vArray = new Choice[v.size()];
|
||||
return new Choices(v.toArray(vArray), start, found, Choices.CF_AMBIGUOUS, false, dflt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
init();
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
if (text.equalsIgnoreCase(values[i])) {
|
||||
Locale currentLocale = I18nUtil.getSupportedLocale(locale);
|
||||
String[] valuesLocale = values.get(currentLocale.getLanguage());
|
||||
String[] labelsLocale = labels.get(currentLocale.getLanguage());
|
||||
for (int i = 0; i < valuesLocale.length; ++i) {
|
||||
if (text.equalsIgnoreCase(valuesLocale[i])) {
|
||||
Choice v[] = new Choice[1];
|
||||
v[0] = new Choice(String.valueOf(i), values[i], labels[i]);
|
||||
v[0] = new Choice(String.valueOf(i), valuesLocale[i], labelsLocale[i]);
|
||||
return new Choices(v, 0, v.length, Choices.CF_UNCERTAIN, false, 0);
|
||||
}
|
||||
}
|
||||
@@ -131,19 +187,31 @@ public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String field, String key, String locale) {
|
||||
public String getLabel(String key, String locale) {
|
||||
init();
|
||||
|
||||
// Get default if locale is empty
|
||||
if (StringUtils.isBlank(locale)) {
|
||||
locale = I18nUtil.getDefaultLocale().getLanguage();
|
||||
}
|
||||
|
||||
String[] labelsLocale = labels.get(locale);
|
||||
int pos = -1;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i].equals(key)) {
|
||||
for (int i = 0; i < labelsLocale.length; i++) {
|
||||
if (labelsLocale[i].equals(key)) {
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos != -1) {
|
||||
return labels[pos];
|
||||
return labelsLocale[pos];
|
||||
} else {
|
||||
return "UNKNOWN KEY " + key;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isScrollable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,9 @@ package org.dspace.content.authority;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
@@ -19,7 +21,6 @@ import javax.xml.xpath.XPathFactory;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.core.SelfNamedPlugin;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
@@ -54,25 +55,35 @@ import org.xml.sax.InputSource;
|
||||
* @author Michael B. Klein
|
||||
*/
|
||||
|
||||
public class DSpaceControlledVocabulary extends SelfNamedPlugin implements ChoiceAuthority {
|
||||
public class DSpaceControlledVocabulary extends SelfNamedPlugin implements HierarchicalAuthority {
|
||||
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(DSpaceControlledVocabulary.class);
|
||||
protected static String xpathTemplate = "//node[contains(translate(@label,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'," +
|
||||
"'abcdefghijklmnopqrstuvwxyz'),'%s')]";
|
||||
protected static String idTemplate = "//node[@id = '%s']";
|
||||
protected static String idParentTemplate = "//node[@id = '%s']/parent::isComposedBy";
|
||||
protected static String labelTemplate = "//node[@label = '%s']";
|
||||
protected static String idParentTemplate = "//node[@id = '%s']/parent::isComposedBy/parent::node";
|
||||
protected static String rootTemplate = "/node";
|
||||
protected static String pluginNames[] = null;
|
||||
|
||||
protected String vocabularyName = null;
|
||||
protected InputSource vocabulary = null;
|
||||
protected Boolean suggestHierarchy = true;
|
||||
protected Boolean suggestHierarchy = false;
|
||||
protected Boolean storeHierarchy = true;
|
||||
protected String hierarchyDelimiter = "::";
|
||||
protected Integer preloadLevel = 1;
|
||||
|
||||
public DSpaceControlledVocabulary() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean storeAuthorityInMetadata() {
|
||||
// For backward compatibility controlled vocabularies don't store the node id in
|
||||
// the metadatavalue
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String[] getPluginNames() {
|
||||
if (pluginNames == null) {
|
||||
initPluginNames();
|
||||
@@ -112,6 +123,7 @@ public class DSpaceControlledVocabulary extends SelfNamedPlugin implements Choic
|
||||
String configurationPrefix = "vocabulary.plugin." + vocabularyName;
|
||||
storeHierarchy = config.getBooleanProperty(configurationPrefix + ".hierarchy.store", storeHierarchy);
|
||||
suggestHierarchy = config.getBooleanProperty(configurationPrefix + ".hierarchy.suggest", suggestHierarchy);
|
||||
preloadLevel = config.getIntProperty(configurationPrefix + ".hierarchy.preloadLevel", preloadLevel);
|
||||
String configuredDelimiter = config.getProperty(configurationPrefix + ".delimiter");
|
||||
if (configuredDelimiter != null) {
|
||||
hierarchyDelimiter = configuredDelimiter.replaceAll("(^\"|\"$)", "");
|
||||
@@ -142,7 +154,7 @@ public class DSpaceControlledVocabulary extends SelfNamedPlugin implements Choic
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale) {
|
||||
public Choices getMatches(String text, int start, int limit, String locale) {
|
||||
init();
|
||||
log.debug("Getting matches for '" + text + "'");
|
||||
String xpathExpression = "";
|
||||
@@ -151,59 +163,60 @@ public class DSpaceControlledVocabulary extends SelfNamedPlugin implements Choic
|
||||
xpathExpression += String.format(xpathTemplate, textHierarchy[i].replaceAll("'", "'").toLowerCase());
|
||||
}
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
Choice[] choices;
|
||||
int total = 0;
|
||||
List<Choice> choices = new ArrayList<Choice>();
|
||||
try {
|
||||
NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET);
|
||||
String[] authorities = new String[results.getLength()];
|
||||
String[] values = new String[results.getLength()];
|
||||
String[] labels = new String[results.getLength()];
|
||||
String[] parent = new String[results.getLength()];
|
||||
String[] notes = new String[results.getLength()];
|
||||
for (int i = 0; i < results.getLength(); i++) {
|
||||
Node node = results.item(i);
|
||||
readNode(authorities, values, labels, parent, notes, i, node);
|
||||
}
|
||||
int resultCount = labels.length - start;
|
||||
// limit = 0 means no limit
|
||||
if ((limit > 0) && (resultCount > limit)) {
|
||||
resultCount = limit;
|
||||
}
|
||||
choices = new Choice[resultCount];
|
||||
if (resultCount > 0) {
|
||||
for (int i = 0; i < resultCount; i++) {
|
||||
choices[i] = new Choice(authorities[start + i], values[start + i], labels[start + i]);
|
||||
if (StringUtils.isNotBlank(parent[i])) {
|
||||
choices[i].extras.put("parent", parent[i]);
|
||||
}
|
||||
if (StringUtils.isNotBlank(notes[i])) {
|
||||
choices[i].extras.put("note", notes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
total = results.getLength();
|
||||
choices = getChoicesFromNodeList(results, start, limit);
|
||||
} catch (XPathExpressionException e) {
|
||||
choices = new Choice[0];
|
||||
log.warn(e.getMessage(), e);
|
||||
return new Choices(true);
|
||||
}
|
||||
return new Choices(choices, 0, choices.length, Choices.CF_AMBIGUOUS, false);
|
||||
return new Choices(choices.toArray(new Choice[choices.size()]), start, total, Choices.CF_AMBIGUOUS,
|
||||
total > start + limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
init();
|
||||
log.debug("Getting best match for '" + text + "'");
|
||||
return getMatches(field, text, collection, 0, 2, locale);
|
||||
log.debug("Getting best matches for '" + text + "'");
|
||||
String xpathExpression = "";
|
||||
String[] textHierarchy = text.split(hierarchyDelimiter, -1);
|
||||
for (int i = 0; i < textHierarchy.length; i++) {
|
||||
xpathExpression += String.format(labelTemplate, textHierarchy[i].replaceAll("'", "'"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String field, String key, String locale) {
|
||||
init();
|
||||
String xpathExpression = String.format(idTemplate, key);
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
List<Choice> choices = new ArrayList<Choice>();
|
||||
try {
|
||||
Node node = (Node) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODE);
|
||||
return node.getAttributes().getNamedItem("label").getNodeValue();
|
||||
NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET);
|
||||
choices = getChoicesFromNodeList(results, 0, 1);
|
||||
} catch (XPathExpressionException e) {
|
||||
return ("");
|
||||
log.warn(e.getMessage(), e);
|
||||
return new Choices(true);
|
||||
}
|
||||
return new Choices(choices.toArray(new Choice[choices.size()]), 0, choices.size(), Choices.CF_AMBIGUOUS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String key, String locale) {
|
||||
return getNodeLabel(key, this.suggestHierarchy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(String key, String locale) {
|
||||
return getNodeLabel(key, this.storeHierarchy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choice getChoice(String authKey, String locale) {
|
||||
Node node;
|
||||
try {
|
||||
node = getNode(authKey);
|
||||
} catch (XPathExpressionException e) {
|
||||
return null;
|
||||
}
|
||||
return createChoiceFromNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -212,81 +225,227 @@ public class DSpaceControlledVocabulary extends SelfNamedPlugin implements Choic
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choice getChoice(String fieldKey, String authKey, String locale) {
|
||||
public Choices getTopChoices(String authorityName, int start, int limit, String locale) {
|
||||
init();
|
||||
log.debug("Getting matches for '" + authKey + "'");
|
||||
String xpathExpression = String.format(idTemplate, authKey);
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
try {
|
||||
Node node = (Node) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODE);
|
||||
if (node != null) {
|
||||
String[] authorities = new String[1];
|
||||
String[] values = new String[1];
|
||||
String[] labels = new String[1];
|
||||
String[] parent = new String[1];
|
||||
String[] note = new String[1];
|
||||
readNode(authorities, values, labels, parent, note, 0, node);
|
||||
String xpathExpression = rootTemplate;
|
||||
return getChoicesByXpath(xpathExpression, start, limit);
|
||||
}
|
||||
|
||||
if (values.length > 0) {
|
||||
Choice choice = new Choice(authorities[0], values[0], labels[0]);
|
||||
if (StringUtils.isNotBlank(parent[0])) {
|
||||
choice.extras.put("parent", parent[0]);
|
||||
}
|
||||
if (StringUtils.isNotBlank(note[0])) {
|
||||
choice.extras.put("note", note[0]);
|
||||
@Override
|
||||
public Choices getChoicesByParent(String authorityName, String parentId, int start, int limit, String locale) {
|
||||
init();
|
||||
String xpathExpression = String.format(idTemplate, parentId);
|
||||
return getChoicesByXpath(xpathExpression, start, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choice getParentChoice(String authorityName, String childId, String locale) {
|
||||
init();
|
||||
try {
|
||||
String xpathExpression = String.format(idParentTemplate, childId);
|
||||
Choice choice = createChoiceFromNode(getNodeFromXPath(xpathExpression));
|
||||
return choice;
|
||||
}
|
||||
}
|
||||
} catch (XPathExpressionException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void readNode(String[] authorities, String[] values, String[] labels, String[] parent, String[] notes,
|
||||
int i, Node node) {
|
||||
@Override
|
||||
public Integer getPreloadLevel() {
|
||||
return preloadLevel;
|
||||
}
|
||||
|
||||
private boolean isRootElement(Node node) {
|
||||
if (node != null && node.getOwnerDocument().getDocumentElement().equals(node)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Node getNode(String key) throws XPathExpressionException {
|
||||
init();
|
||||
String xpathExpression = String.format(idTemplate, key);
|
||||
Node node = getNodeFromXPath(xpathExpression);
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node getNodeFromXPath(String xpathExpression) throws XPathExpressionException {
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
Node node = (Node) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODE);
|
||||
return node;
|
||||
}
|
||||
|
||||
private List<Choice> getChoicesFromNodeList(NodeList results, int start, int limit) {
|
||||
List<Choice> choices = new ArrayList<Choice>();
|
||||
for (int i = 0; i < results.getLength(); i++) {
|
||||
if (i < start) {
|
||||
continue;
|
||||
}
|
||||
if (choices.size() == limit) {
|
||||
break;
|
||||
}
|
||||
Node node = results.item(i);
|
||||
Choice choice = new Choice(getAuthority(node), getLabel(node), getValue(node),
|
||||
isSelectable(node));
|
||||
choice.extras = addOtherInformation(getParent(node), getNote(node), getChildren(node), getAuthority(node));
|
||||
choices.add(choice);
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
private Map<String, String> addOtherInformation(String parentCurr, String noteCurr,
|
||||
List<String> childrenCurr, String authorityCurr) {
|
||||
Map<String, String> extras = new HashMap<String, String>();
|
||||
if (StringUtils.isNotBlank(parentCurr)) {
|
||||
extras.put("parent", parentCurr);
|
||||
}
|
||||
if (StringUtils.isNotBlank(noteCurr)) {
|
||||
extras.put("note", noteCurr);
|
||||
}
|
||||
if (childrenCurr.size() > 0) {
|
||||
extras.put("hasChildren", "true");
|
||||
} else {
|
||||
extras.put("hasChildren", "false");
|
||||
}
|
||||
extras.put("id", authorityCurr);
|
||||
return extras;
|
||||
}
|
||||
|
||||
private String getNodeLabel(String key, boolean useHierarchy) {
|
||||
try {
|
||||
Node node = getNode(key);
|
||||
if (useHierarchy) {
|
||||
return this.buildString(node);
|
||||
} else {
|
||||
return node.getAttributes().getNamedItem("label").getNodeValue();
|
||||
}
|
||||
} catch (XPathExpressionException e) {
|
||||
return ("");
|
||||
}
|
||||
}
|
||||
|
||||
private String getLabel(Node node) {
|
||||
String hierarchy = this.buildString(node);
|
||||
if (this.suggestHierarchy) {
|
||||
labels[i] = hierarchy;
|
||||
return hierarchy;
|
||||
} else {
|
||||
labels[i] = node.getAttributes().getNamedItem("label").getNodeValue();
|
||||
return node.getAttributes().getNamedItem("label").getNodeValue();
|
||||
}
|
||||
if (this.storeHierarchy) {
|
||||
values[i] = hierarchy;
|
||||
} else {
|
||||
values[i] = node.getAttributes().getNamedItem("label").getNodeValue();
|
||||
}
|
||||
|
||||
private String getValue(Node node) {
|
||||
String hierarchy = this.buildString(node);
|
||||
if (this.storeHierarchy) {
|
||||
return hierarchy;
|
||||
} else {
|
||||
return node.getAttributes().getNamedItem("label").getNodeValue();
|
||||
}
|
||||
}
|
||||
|
||||
private String getNote(Node node) {
|
||||
NodeList childNodes = node.getChildNodes();
|
||||
for (int ci = 0; ci < childNodes.getLength(); ci++) {
|
||||
Node firstChild = childNodes.item(ci);
|
||||
if (firstChild != null && "hasNote".equals(firstChild.getNodeName())) {
|
||||
String nodeValue = firstChild.getTextContent();
|
||||
if (StringUtils.isNotBlank(nodeValue)) {
|
||||
notes[i] = nodeValue;
|
||||
return nodeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Node idAttr = node.getAttributes().getNamedItem("id");
|
||||
if (null != idAttr) { // 'id' is optional
|
||||
authorities[i] = idAttr.getNodeValue();
|
||||
if (isHierarchical()) {
|
||||
Node parentN = node.getParentNode();
|
||||
if (parentN != null) {
|
||||
parentN = parentN.getParentNode();
|
||||
if (parentN != null) {
|
||||
Node parentIdAttr = parentN.getAttributes().getNamedItem("id");
|
||||
if (null != parentIdAttr) {
|
||||
parent[i] = parentIdAttr.getNodeValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> getChildren(Node node) {
|
||||
List<String> children = new ArrayList<String>();
|
||||
NodeList childNodes = node.getChildNodes();
|
||||
for (int ci = 0; ci < childNodes.getLength(); ci++) {
|
||||
Node firstChild = childNodes.item(ci);
|
||||
if (firstChild != null && "isComposedBy".equals(firstChild.getNodeName())) {
|
||||
for (int cii = 0; cii < firstChild.getChildNodes().getLength(); cii++) {
|
||||
Node childN = firstChild.getChildNodes().item(cii);
|
||||
if (childN != null && "node".equals(childN.getNodeName())) {
|
||||
Node childIdAttr = childN.getAttributes().getNamedItem("id");
|
||||
if (null != childIdAttr) {
|
||||
children.add(childIdAttr.getNodeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
authorities[i] = null;
|
||||
parent[i] = null;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
private boolean isSelectable(Node node) {
|
||||
Node selectableAttr = node.getAttributes().getNamedItem("selectable");
|
||||
if (null != selectableAttr) {
|
||||
return Boolean.valueOf(selectableAttr.getNodeValue());
|
||||
} else { // Default is true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private String getParent(Node node) {
|
||||
Node parentN = node.getParentNode();
|
||||
if (parentN != null) {
|
||||
parentN = parentN.getParentNode();
|
||||
if (parentN != null && !isRootElement(parentN)) {
|
||||
return buildString(parentN);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getAuthority(Node node) {
|
||||
Node idAttr = node.getAttributes().getNamedItem("id");
|
||||
if (null != idAttr) { // 'id' is optional
|
||||
return idAttr.getNodeValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Choices getChoicesByXpath(String xpathExpression, int start, int limit) {
|
||||
List<Choice> choices = new ArrayList<Choice>();
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
try {
|
||||
Node parentNode = (Node) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODE);
|
||||
int count = 0;
|
||||
if (parentNode != null) {
|
||||
NodeList childNodes = (NodeList) xpath.evaluate(".//isComposedBy", parentNode, XPathConstants.NODE);
|
||||
if (null != childNodes) {
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node childNode = childNodes.item(i);
|
||||
if (childNode != null && "node".equals(childNode.getNodeName())) {
|
||||
if (count < start || choices.size() >= limit) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
choices.add(createChoiceFromNode(childNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Choices(choices.toArray(new Choice[choices.size()]), start, count,
|
||||
Choices.CF_AMBIGUOUS, false);
|
||||
}
|
||||
} catch (XPathExpressionException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
return new Choices(true);
|
||||
}
|
||||
return new Choices(false);
|
||||
}
|
||||
|
||||
private Choice createChoiceFromNode(Node node) {
|
||||
if (node != null && !isRootElement(node)) {
|
||||
Choice choice = new Choice(getAuthority(node), getLabel(node), getValue(node),
|
||||
isSelectable(node));
|
||||
choice.extras = addOtherInformation(getParent(node), getNote(node),getChildren(node), getAuthority(node));
|
||||
return choice;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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.authority;
|
||||
|
||||
/**
|
||||
* Plugin interface that supplies an authority control mechanism for
|
||||
* one metadata field.
|
||||
*
|
||||
* @author Larry Stone
|
||||
* @see ChoiceAuthority
|
||||
*/
|
||||
public interface HierarchicalAuthority extends ChoiceAuthority {
|
||||
|
||||
/**
|
||||
* Get all values from the authority that match the preferred value.
|
||||
* Note that the offering was entered by the user and may contain
|
||||
* mixed/incorrect case, whitespace, etc so the plugin should be careful
|
||||
* to clean up user data before making comparisons.
|
||||
*
|
||||
* Value of a "Name" field will be in canonical DSpace person name format,
|
||||
* which is "Lastname, Firstname(s)", e.g. "Smith, John Q.".
|
||||
*
|
||||
* Some authorities with a small set of values may simply return the whole
|
||||
* set for any sample value, although it's a good idea to set the
|
||||
* defaultSelected index in the Choices instance to the choice, if any,
|
||||
* that matches the value.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
*/
|
||||
public Choices getTopChoices(String authorityName, int start, int limit, String locale);
|
||||
|
||||
/**
|
||||
* Get all values from the authority that match the preferred value.
|
||||
* Note that the offering was entered by the user and may contain
|
||||
* mixed/incorrect case, whitespace, etc so the plugin should be careful
|
||||
* to clean up user data before making comparisons.
|
||||
*
|
||||
* Value of a "Name" field will be in canonical DSpace person name format,
|
||||
* which is "Lastname, Firstname(s)", e.g. "Smith, John Q.".
|
||||
*
|
||||
* Some authorities with a small set of values may simply return the whole
|
||||
* set for any sample value, although it's a good idea to set the
|
||||
* defaultSelected index in the Choices instance to the choice, if any,
|
||||
* that matches the value.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param parentId user's value to match
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
*/
|
||||
public Choices getChoicesByParent(String authorityName, String parentId, int start, int limit, String locale);
|
||||
|
||||
/**
|
||||
* It returns the parent choice in the hierarchy if any
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param vocabularyId user's value to match
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choice object
|
||||
*/
|
||||
public Choice getParentChoice(String authorityName, String vocabularyId, String locale);
|
||||
|
||||
/**
|
||||
* Provides an hint for the UI to preload some levels to improve the UX. It
|
||||
* usually mean that these preloaded level will be shown expanded by default
|
||||
*/
|
||||
public Integer getPreloadLevel();
|
||||
|
||||
@Override
|
||||
default boolean isHierarchical() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -1,166 +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.content.authority;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.DCInputsReader;
|
||||
import org.dspace.app.util.DCInputsReaderException;
|
||||
import org.dspace.content.Collection;
|
||||
|
||||
/**
|
||||
* This authority is registered automatically by the ChoiceAuthorityService for
|
||||
* all the metadata that use a value-pair or a vocabulary in the submission-form.xml
|
||||
*
|
||||
* It keeps a map of form-name vs ChoiceAuthority to delegate the execution of
|
||||
* the method to the specific ChoiceAuthority configured for the collection when
|
||||
* the same metadata have different vocabulary or value-pair on a collection
|
||||
* basis
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class InputFormSelfRegisterWrapperAuthority implements ChoiceAuthority {
|
||||
|
||||
private static Logger log =
|
||||
org.apache.logging.log4j.LogManager.getLogger(InputFormSelfRegisterWrapperAuthority.class);
|
||||
|
||||
private Map<String, ChoiceAuthority> delegates = new HashMap<String, ChoiceAuthority>();
|
||||
|
||||
private static DCInputsReader dci = null;
|
||||
|
||||
private void init() {
|
||||
try {
|
||||
if (dci == null) {
|
||||
dci = new DCInputsReader();
|
||||
}
|
||||
} catch (DCInputsReaderException e) {
|
||||
log.error("Failed reading DCInputs initialization: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String field, String query, Collection collection, int start, int limit, String locale) {
|
||||
String formName;
|
||||
try {
|
||||
init();
|
||||
if (collection == null) {
|
||||
Set<Choice> choices = new HashSet<Choice>();
|
||||
//workaround search in all authority configured
|
||||
for (ChoiceAuthority ca : delegates.values()) {
|
||||
Choices tmp = ca.getMatches(field, query, null, start, limit, locale);
|
||||
if (tmp.total > 0) {
|
||||
Set<Choice> mySet = new HashSet<Choice>(Arrays.asList(tmp.values));
|
||||
choices.addAll(mySet);
|
||||
}
|
||||
}
|
||||
if (!choices.isEmpty()) {
|
||||
Choice[] results = new Choice[choices.size()];
|
||||
choices.toArray(results);
|
||||
return new Choices(results, 0, choices.size(), Choices.CF_AMBIGUOUS, false);
|
||||
}
|
||||
} else {
|
||||
formName = dci.getInputFormNameByCollectionAndField(collection, field);
|
||||
return delegates.get(formName).getMatches(field, query, collection, start, limit, locale);
|
||||
}
|
||||
} catch (DCInputsReaderException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return new Choices(Choices.CF_NOTFOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
|
||||
String formName;
|
||||
try {
|
||||
init();
|
||||
if (collection == null) {
|
||||
Set<Choice> choices = new HashSet<Choice>();
|
||||
//workaround search in all authority configured
|
||||
for (ChoiceAuthority ca : delegates.values()) {
|
||||
Choices tmp = ca.getBestMatch(field, text, null, locale);
|
||||
if (tmp.total > 0) {
|
||||
Set<Choice> mySet = new HashSet<Choice>(Arrays.asList(tmp.values));
|
||||
choices.addAll(mySet);
|
||||
}
|
||||
}
|
||||
if (!choices.isEmpty()) {
|
||||
Choice[] results = new Choice[choices.size() - 1];
|
||||
choices.toArray(results);
|
||||
return new Choices(results, 0, choices.size(), Choices.CF_UNCERTAIN, false);
|
||||
}
|
||||
} else {
|
||||
formName = dci.getInputFormNameByCollectionAndField(collection, field);
|
||||
return delegates.get(formName).getBestMatch(field, text, collection, locale);
|
||||
}
|
||||
} catch (DCInputsReaderException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return new Choices(Choices.CF_NOTFOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String field, String key, String locale) {
|
||||
// TODO we need to manage REALLY the authority
|
||||
// WRONG BEHAVIOUR: now in each delegates can exists the same key with
|
||||
// different value
|
||||
for (ChoiceAuthority delegate : delegates.values()) {
|
||||
String label = delegate.getLabel(field, key, locale);
|
||||
if (StringUtils.isNotBlank(label)) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
return "UNKNOWN KEY " + key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHierarchical() {
|
||||
// TODO we need to manage REALLY the authority
|
||||
// WRONG BEHAVIOUR: now in each delegates can exists the same key with
|
||||
// different value
|
||||
for (ChoiceAuthority delegate : delegates.values()) {
|
||||
return delegate.isHierarchical();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isScrollable() {
|
||||
// TODO we need to manage REALLY the authority
|
||||
// WRONG BEHAVIOUR: now in each delegates can exists the same key with
|
||||
// different value
|
||||
for (ChoiceAuthority delegate : delegates.values()) {
|
||||
return delegate.isScrollable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdentifier() {
|
||||
// TODO we need to manage REALLY the authority
|
||||
// WRONG BEHAVIOUR: now in each delegates can exists the same key with
|
||||
// different value
|
||||
for (ChoiceAuthority delegate : delegates.values()) {
|
||||
return delegate.hasIdentifier();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, ChoiceAuthority> getDelegates() {
|
||||
return delegates;
|
||||
}
|
||||
|
||||
public void setDelegates(Map<String, ChoiceAuthority> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
}
|
@@ -14,12 +14,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.DCInput;
|
||||
import org.dspace.app.util.DCInputSet;
|
||||
import org.dspace.app.util.DCInputsReader;
|
||||
import org.dspace.app.util.DCInputsReaderException;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.authority.service.MetadataAuthorityService;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
@@ -144,8 +139,6 @@ public class MetadataAuthorityServiceImpl implements MetadataAuthorityService {
|
||||
if (dmc >= Choices.CF_UNSET) {
|
||||
defaultMinConfidence = dmc;
|
||||
}
|
||||
|
||||
autoRegisterAuthorityFromInputReader();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +198,6 @@ public class MetadataAuthorityServiceImpl implements MetadataAuthorityService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Give the minimal level of confidence required to consider valid an authority value
|
||||
* for the given metadata.
|
||||
@@ -229,35 +221,4 @@ public class MetadataAuthorityServiceImpl implements MetadataAuthorityService {
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
private void autoRegisterAuthorityFromInputReader() {
|
||||
try {
|
||||
DCInputsReader dcInputsReader = new DCInputsReader();
|
||||
for (DCInputSet dcinputSet : dcInputsReader.getAllInputs(Integer.MAX_VALUE, 0)) {
|
||||
DCInput[][] dcinputs = dcinputSet.getFields();
|
||||
for (DCInput[] dcrows : dcinputs) {
|
||||
for (DCInput dcinput : dcrows) {
|
||||
if (StringUtils.isNotBlank(dcinput.getPairsType())
|
||||
|| StringUtils.isNotBlank(dcinput.getVocabulary())) {
|
||||
String authorityName = dcinput.getPairsType();
|
||||
if (StringUtils.isBlank(authorityName)) {
|
||||
authorityName = dcinput.getVocabulary();
|
||||
}
|
||||
if (!StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
|
||||
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
|
||||
dcinput.getQualifier());
|
||||
boolean req = ConfigurationManager
|
||||
.getBooleanProperty("authority.required." + fieldKey, false);
|
||||
controlled.put(fieldKey, true);
|
||||
isAuthorityRequired.put(fieldKey, req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DCInputsReaderException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,13 +7,13 @@
|
||||
*/
|
||||
package org.dspace.content.authority;
|
||||
|
||||
import org.dspace.content.Collection;
|
||||
|
||||
/**
|
||||
* This is a *very* stupid test fixture for authority control, and also
|
||||
* serves as a trivial example of an authority plugin implementation.
|
||||
*/
|
||||
public class SampleAuthority implements ChoiceAuthority {
|
||||
private String pluginInstanceName;
|
||||
|
||||
protected static String values[] = {
|
||||
"sun",
|
||||
"mon",
|
||||
@@ -35,7 +35,7 @@ public class SampleAuthority implements ChoiceAuthority {
|
||||
};
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String field, String query, Collection collection, int start, int limit, String locale) {
|
||||
public Choices getMatches(String query, int start, int limit, String locale) {
|
||||
int dflt = -1;
|
||||
Choice v[] = new Choice[values.length];
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
@@ -48,7 +48,7 @@ public class SampleAuthority implements ChoiceAuthority {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
if (text.equalsIgnoreCase(values[i])) {
|
||||
Choice v[] = new Choice[1];
|
||||
@@ -60,7 +60,17 @@ public class SampleAuthority implements ChoiceAuthority {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String field, String key, String locale) {
|
||||
public String getLabel(String key, String locale) {
|
||||
return labels[Integer.parseInt(key)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPluginInstanceName() {
|
||||
return pluginInstanceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPluginInstanceName(String name) {
|
||||
this.pluginInstanceName = name;
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -24,8 +25,9 @@ import org.dspace.authority.AuthorityValue;
|
||||
import org.dspace.authority.SolrAuthorityInterface;
|
||||
import org.dspace.authority.factory.AuthorityServiceFactory;
|
||||
import org.dspace.authority.service.AuthorityValueService;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.NameAwarePlugin;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
@@ -35,7 +37,14 @@ import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class SolrAuthority implements ChoiceAuthority {
|
||||
/** the name assigned to the specific instance by the PluginService, @see {@link NameAwarePlugin} **/
|
||||
private String authorityName;
|
||||
|
||||
/**
|
||||
* the metadata managed by the plugin instance, derived from its authority name
|
||||
* in the form schema_element_qualifier
|
||||
*/
|
||||
private String field;
|
||||
protected SolrAuthorityInterface source =
|
||||
DSpaceServicesFactory.getInstance().getServiceManager()
|
||||
.getServiceByName("AuthoritySource", SolrAuthorityInterface.class);
|
||||
@@ -45,8 +54,9 @@ public class SolrAuthority implements ChoiceAuthority {
|
||||
protected boolean externalResults = false;
|
||||
protected final AuthorityValueService authorityValueService = AuthorityServiceFactory.getInstance()
|
||||
.getAuthorityValueService();
|
||||
|
||||
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale,
|
||||
protected final ConfigurationService configurationService = DSpaceServicesFactory.getInstance()
|
||||
.getConfigurationService();
|
||||
public Choices getMatches(String text, int start, int limit, String locale,
|
||||
boolean bestMatch) {
|
||||
if (limit == 0) {
|
||||
limit = 10;
|
||||
@@ -193,13 +203,13 @@ public class SolrAuthority implements ChoiceAuthority {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale) {
|
||||
return getMatches(field, text, collection, start, limit, locale, true);
|
||||
public Choices getMatches(String text, int start, int limit, String locale) {
|
||||
return getMatches(text, start, limit, locale, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
|
||||
Choices matches = getMatches(field, text, collection, 0, 1, locale, false);
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
Choices matches = getMatches(text, 0, 1, locale, false);
|
||||
if (matches.values.length != 0 && !matches.values[0].value.equalsIgnoreCase(text)) {
|
||||
matches = new Choices(false);
|
||||
}
|
||||
@@ -207,7 +217,7 @@ public class SolrAuthority implements ChoiceAuthority {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String field, String key, String locale) {
|
||||
public String getLabel(String key, String locale) {
|
||||
try {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("requesting label for key " + key + " using locale " + locale);
|
||||
@@ -276,4 +286,23 @@ public class SolrAuthority implements ChoiceAuthority {
|
||||
public void addExternalResultsInNextMatches() {
|
||||
this.externalResults = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPluginInstanceName(String name) {
|
||||
authorityName = name;
|
||||
for (Entry conf : configurationService.getProperties().entrySet()) {
|
||||
if (StringUtils.startsWith((String) conf.getKey(), ChoiceAuthorityServiceImpl.CHOICES_PLUGIN_PREFIX)
|
||||
&& StringUtils.equals((String) conf.getValue(), authorityName)) {
|
||||
field = ((String) conf.getKey()).substring(ChoiceAuthorityServiceImpl.CHOICES_PLUGIN_PREFIX.length())
|
||||
.replace(".", "_");
|
||||
// exit the look immediately as we have found it
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPluginInstanceName() {
|
||||
return authorityName;
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.Collection;
|
||||
|
||||
/**
|
||||
* This is a *very* stupid test fixture for authority control with AuthorityVariantsSupport.
|
||||
@@ -19,6 +18,7 @@ import org.dspace.content.Collection;
|
||||
* @author Andrea Bollini (CILEA)
|
||||
*/
|
||||
public class TestAuthority implements ChoiceAuthority, AuthorityVariantsSupport {
|
||||
private String pluginInstanceName;
|
||||
|
||||
@Override
|
||||
public List<String> getVariants(String key, String locale) {
|
||||
@@ -33,8 +33,7 @@ public class TestAuthority implements ChoiceAuthority, AuthorityVariantsSupport
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getMatches(String field, String text, Collection collection,
|
||||
int start, int limit, String locale) {
|
||||
public Choices getMatches(String text, int start, int limit, String locale) {
|
||||
Choices choices = new Choices(false);
|
||||
if (StringUtils.isNotBlank(text)) {
|
||||
|
||||
@@ -52,8 +51,7 @@ public class TestAuthority implements ChoiceAuthority, AuthorityVariantsSupport
|
||||
}
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String field, String text, Collection collection,
|
||||
String locale) {
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
Choices choices = new Choices(false);
|
||||
if (StringUtils.isNotBlank(text)) {
|
||||
|
||||
@@ -70,10 +68,20 @@ public class TestAuthority implements ChoiceAuthority, AuthorityVariantsSupport
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(String field, String key, String locale) {
|
||||
public String getLabel(String key, String locale) {
|
||||
if (StringUtils.isNotBlank(key)) {
|
||||
return key.replaceAll("authority", "label");
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPluginInstanceName() {
|
||||
return pluginInstanceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPluginInstanceName(String name) {
|
||||
this.pluginInstanceName = name;
|
||||
}
|
||||
}
|
||||
|
@@ -48,10 +48,10 @@ public interface ChoiceAuthorityService {
|
||||
* @param element element of metadata field
|
||||
* @param qualifier qualifier of metadata field
|
||||
* @return the name of the choice authority associated with the specified
|
||||
* metadata. Throw IllegalArgumentException if the supplied metadat
|
||||
* metadata. Throw IllegalArgumentException if the supplied metadata
|
||||
* is not associated with an authority choice
|
||||
*/
|
||||
public String getChoiceAuthorityName(String schema, String element, String qualifier);
|
||||
public String getChoiceAuthorityName(String schema, String element, String qualifier, Collection collection);
|
||||
|
||||
/**
|
||||
* Wrapper that calls getMatches method of the plugin corresponding to
|
||||
@@ -112,30 +112,33 @@ public interface ChoiceAuthorityService {
|
||||
* the metadata field defined by schema,element,qualifier.
|
||||
*
|
||||
* @param metadataValue metadata value
|
||||
* @param collection Collection owner of Item
|
||||
* @param locale explicit localization key if available
|
||||
* @return label
|
||||
*/
|
||||
public String getLabel(MetadataValue metadataValue, String locale);
|
||||
public String getLabel(MetadataValue metadataValue, Collection collection, String locale);
|
||||
|
||||
/**
|
||||
* Wrapper that calls getLabel method of the plugin corresponding to
|
||||
* the metadata field defined by single field key.
|
||||
*
|
||||
* @param fieldKey single string identifying metadata field
|
||||
* @param collection Collection owner of Item
|
||||
* @param locale explicit localization key if available
|
||||
* @param authKey authority key
|
||||
* @return label
|
||||
*/
|
||||
public String getLabel(String fieldKey, String authKey, String locale);
|
||||
public String getLabel(String fieldKey, Collection collection, String authKey, String locale);
|
||||
|
||||
/**
|
||||
* Predicate, is there a Choices configuration of any kind for the
|
||||
* given metadata field?
|
||||
*
|
||||
* @param fieldKey single string identifying metadata field
|
||||
* @param collection Collection owner of Item
|
||||
* @return true if choices are configured for this field.
|
||||
*/
|
||||
public boolean isChoicesConfigured(String fieldKey);
|
||||
public boolean isChoicesConfigured(String fieldKey, Collection collection);
|
||||
|
||||
/**
|
||||
* Get the presentation keyword (should be "lookup", "select" or "suggest", but this
|
||||
@@ -160,12 +163,14 @@ public interface ChoiceAuthorityService {
|
||||
* @param metadataValue metadata value
|
||||
* @return List of variants
|
||||
*/
|
||||
public List<String> getVariants(MetadataValue metadataValue);
|
||||
|
||||
public String getChoiceMetadatabyAuthorityName(String name);
|
||||
|
||||
public Choice getChoice(String fieldKey, String authKey, String locale);
|
||||
public List<String> getVariants(MetadataValue metadataValue, Collection collection);
|
||||
|
||||
/**
|
||||
* Return the ChoiceAuthority instance identified by the specified name
|
||||
*
|
||||
* @param authorityName the ChoiceAuthority instance name
|
||||
* @return the ChoiceAuthority identified by the specified name
|
||||
*/
|
||||
public ChoiceAuthority getChoiceAuthorityByAuthorityName(String authorityName);
|
||||
|
||||
/**
|
||||
@@ -173,4 +178,49 @@ public interface ChoiceAuthorityService {
|
||||
*/
|
||||
public void clearCache();
|
||||
|
||||
/**
|
||||
* Should we store the authority key (if any) for such field key and collection?
|
||||
*
|
||||
* @param fieldKey single string identifying metadata field
|
||||
* @param collection Collection owner of Item or where the item is submitted to
|
||||
* @return true if the configuration allows to store the authority value
|
||||
*/
|
||||
public boolean storeAuthority(String fieldKey, Collection collection);
|
||||
|
||||
/**
|
||||
* Wrapper that calls getChoicesByParent method of the plugin.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param parentId parent Id
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
* @see org.dspace.content.authority.ChoiceAuthority#getChoicesByParent(java.lang.String, java.lang.String,
|
||||
* int, int, java.lang.String)
|
||||
*/
|
||||
public Choices getChoicesByParent(String authorityName, String parentId, int start, int limit, String locale);
|
||||
|
||||
/**
|
||||
* Wrapper that calls getTopChoices method of the plugin.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param start choice at which to start, 0 is first.
|
||||
* @param limit maximum number of choices to return, 0 for no limit.
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return a Choices object (never null).
|
||||
* @see org.dspace.content.authority.ChoiceAuthority#getTopChoices(java.lang.String, int, int, java.lang.String)
|
||||
*/
|
||||
public Choices getTopChoices(String authorityName, int start, int limit, String locale);
|
||||
|
||||
/**
|
||||
* Return the direct parent of an entry identified by its id in an hierarchical
|
||||
* authority.
|
||||
*
|
||||
* @param authorityName authority name
|
||||
* @param vocabularyId child id
|
||||
* @param locale explicit localization key if available, or null
|
||||
* @return the parent Choice object if any
|
||||
*/
|
||||
public Choice getParentChoice(String authorityName, String vocabularyId, String locale);
|
||||
}
|
||||
|
@@ -47,6 +47,19 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all the items by a given submitter. The order is
|
||||
* indeterminate. All items are included.
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param eperson the submitter
|
||||
* @param retrieveAllItems flag to determine if only archive should be returned
|
||||
* @return an iterator over the items submitted by eperson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
throws SQLException;
|
||||
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, MetadataField metadataField, int limit)
|
||||
throws SQLException;
|
||||
|
||||
|
@@ -13,6 +13,7 @@ import java.util.List;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.GenericDAO;
|
||||
import org.dspace.scripts.Process;
|
||||
import org.dspace.scripts.ProcessQueryParameterContainer;
|
||||
|
||||
/**
|
||||
* This is the Data Access Object for the {@link Process} object
|
||||
@@ -54,4 +55,30 @@ public interface ProcessDAO extends GenericDAO<Process> {
|
||||
*/
|
||||
int countRows(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns a list of all Processes in the database which match the given field requirements. If the
|
||||
* requirements are not null, they will be combined with an AND operation.
|
||||
* @param context The relevant DSpace context
|
||||
* @param processQueryParameterContainer The {@link ProcessQueryParameterContainer} containing all the values
|
||||
* that the returned {@link Process} objects must adhere to
|
||||
* @param limit The limit for the amount of Processes returned
|
||||
* @param offset The offset for the Processes to be returned
|
||||
* @return The list of all Processes which match the metadata requirements
|
||||
* @throws SQLException If something goes wrong
|
||||
*/
|
||||
List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer, int limit,
|
||||
int offset) throws SQLException;
|
||||
|
||||
/**
|
||||
* Count all the processes which match the requirements. The requirements are evaluated like the search
|
||||
* method.
|
||||
* @param context The relevant DSpace context
|
||||
* @param processQueryParameterContainer The {@link ProcessQueryParameterContainer} containing all the values
|
||||
* that the returned {@link Process} objects must adhere to
|
||||
* @return The number of results matching the query
|
||||
* @throws SQLException If something goes wrong
|
||||
*/
|
||||
|
||||
int countTotalWithParameters(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
|
||||
throws SQLException;
|
||||
}
|
||||
|
@@ -108,6 +108,17 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
|
||||
return iterate(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
throws SQLException {
|
||||
if (!retrieveAllItems) {
|
||||
return findBySubmitter(context, eperson);
|
||||
}
|
||||
Query query = createQuery(context, "FROM Item WHERE submitter= :submitter");
|
||||
query.setParameter("submitter", eperson);
|
||||
return iterate(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, MetadataField metadataField, int limit)
|
||||
throws SQLException {
|
||||
|
@@ -8,15 +8,20 @@
|
||||
package org.dspace.content.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.dao.ProcessDAO;
|
||||
import org.dspace.core.AbstractHibernateDAO;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.scripts.Process;
|
||||
import org.dspace.scripts.ProcessQueryParameterContainer;
|
||||
import org.dspace.scripts.Process_;
|
||||
|
||||
/**
|
||||
@@ -56,6 +61,7 @@ public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements Pro
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
|
||||
Root<Process> processRoot = criteriaQuery.from(Process.class);
|
||||
criteriaQuery.select(processRoot);
|
||||
criteriaQuery.orderBy(criteriaBuilder.desc(processRoot.get(Process_.processId)));
|
||||
|
||||
return list(context, criteriaQuery, false, Process.class, limit, offset);
|
||||
}
|
||||
@@ -71,6 +77,76 @@ public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements Pro
|
||||
return count(context, criteriaQuery, criteriaBuilder, processRoot);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer,
|
||||
int limit, int offset) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
|
||||
Root<Process> processRoot = criteriaQuery.from(Process.class);
|
||||
criteriaQuery.select(processRoot);
|
||||
|
||||
handleProcessQueryParameters(processQueryParameterContainer, criteriaBuilder, criteriaQuery, processRoot);
|
||||
return list(context, criteriaQuery, false, Process.class, limit, offset);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will ensure that the params contained in the {@link ProcessQueryParameterContainer} are transferred
|
||||
* to the ProcessRoot and that the correct conditions apply to the query
|
||||
* @param processQueryParameterContainer The object containing the conditions that need to be met
|
||||
* @param criteriaBuilder The criteriaBuilder to be used
|
||||
* @param criteriaQuery The criteriaQuery to be used
|
||||
* @param processRoot The processRoot to be used
|
||||
*/
|
||||
private void handleProcessQueryParameters(ProcessQueryParameterContainer processQueryParameterContainer,
|
||||
CriteriaBuilder criteriaBuilder, CriteriaQuery criteriaQuery,
|
||||
Root<Process> processRoot) {
|
||||
addProcessQueryParameters(processQueryParameterContainer, criteriaBuilder, criteriaQuery, processRoot);
|
||||
if (StringUtils.equalsIgnoreCase(processQueryParameterContainer.getSortOrder(), "asc")) {
|
||||
criteriaQuery
|
||||
.orderBy(criteriaBuilder.asc(processRoot.get(processQueryParameterContainer.getSortProperty())));
|
||||
} else if (StringUtils.equalsIgnoreCase(processQueryParameterContainer.getSortOrder(), "desc")) {
|
||||
criteriaQuery
|
||||
.orderBy(criteriaBuilder.desc(processRoot.get(processQueryParameterContainer.getSortProperty())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will apply the variables in the {@link ProcessQueryParameterContainer} as criteria for the
|
||||
* {@link Process} objects to the given CriteriaQuery.
|
||||
* They'll need to adhere to these variables in order to be eligible for return
|
||||
* @param processQueryParameterContainer The object containing the variables for the {@link Process}
|
||||
* to adhere to
|
||||
* @param criteriaBuilder The current CriteriaBuilder
|
||||
* @param criteriaQuery The current CriteriaQuery
|
||||
* @param processRoot The processRoot
|
||||
*/
|
||||
private void addProcessQueryParameters(ProcessQueryParameterContainer processQueryParameterContainer,
|
||||
CriteriaBuilder criteriaBuilder, CriteriaQuery criteriaQuery,
|
||||
Root<Process> processRoot) {
|
||||
List<Predicate> andPredicates = new LinkedList<>();
|
||||
|
||||
for (Map.Entry<String, Object> entry : processQueryParameterContainer.getQueryParameterMap().entrySet()) {
|
||||
andPredicates.add(criteriaBuilder.equal(processRoot.get(entry.getKey()), entry.getValue()));
|
||||
}
|
||||
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[]{})));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countTotalWithParameters(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
|
||||
throws SQLException {
|
||||
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
|
||||
Root<Process> processRoot = criteriaQuery.from(Process.class);
|
||||
criteriaQuery.select(processRoot);
|
||||
|
||||
addProcessQueryParameters(processQueryParameterContainer, criteriaBuilder, criteriaQuery, processRoot);
|
||||
return count(context, criteriaQuery, criteriaBuilder, processRoot);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -200,10 +200,11 @@ public interface DSpaceObjectService<T extends DSpaceObject> {
|
||||
* and the ISO3166 country code. <code>null</code> means the
|
||||
* value has no language (for example, a date).
|
||||
* @param values the values to add.
|
||||
* @return the list of MetadataValues added to the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
List<String> values) throws SQLException;
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, List<String> values) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add metadata fields. These are appended to existing values.
|
||||
@@ -223,10 +224,11 @@ public interface DSpaceObjectService<T extends DSpaceObject> {
|
||||
* @param values the values to add.
|
||||
* @param authorities the external authority key for this value (or null)
|
||||
* @param confidences the authority confidence (default 0)
|
||||
* @return the list of MetadataValues added to the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
List<String> values, List<String> authorities, List<Integer> confidences)
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, List<String> values, List<String> authorities, List<Integer> confidences)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -243,32 +245,64 @@ public interface DSpaceObjectService<T extends DSpaceObject> {
|
||||
* @param values the values to add.
|
||||
* @param authorities the external authority key for this value (or null)
|
||||
* @param confidences the authority confidence (default 0)
|
||||
* @return the list of MetadataValues added to the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String lang, List<String> values,
|
||||
List<String> authorities, List<Integer> confidences) throws SQLException;
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String lang,
|
||||
List<String> values, List<String> authorities, List<Integer> confidences) throws SQLException;
|
||||
|
||||
/**
|
||||
* Shortcut for {@link #addMetadata(Context, DSpaceObject, MetadataField, String, List, List, List)} when a single
|
||||
* value need to be added
|
||||
*
|
||||
* @param context
|
||||
* @param dso
|
||||
* @param metadataField
|
||||
* @param language
|
||||
* @param value
|
||||
* @param authority
|
||||
* @param confidence
|
||||
* @param context DSpace context
|
||||
* @param dso DSpaceObject
|
||||
* @param metadataField the metadata field to which the value is to be set
|
||||
* @param language the ISO639 language code, optionally followed by an underscore
|
||||
* and the ISO3166 country code. <code>null</code> means the
|
||||
* value has no language (for example, a date).
|
||||
* @param value the value to add.
|
||||
* @param authority the external authority key for this value (or null)
|
||||
* @param confidence the authority confidence (default 0)
|
||||
* @return the MetadataValue added ot the object
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String language, String value,
|
||||
String authority, int confidence) throws SQLException;
|
||||
public MetadataValue addMetadata(Context context, T dso, MetadataField metadataField, String language,
|
||||
String value, String authority, int confidence) throws SQLException;
|
||||
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String language, String value)
|
||||
/**
|
||||
* Add a metadatafield. These are appended to existing values.
|
||||
* Use <code>clearMetadata</code> to remove values.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param dso DSpaceObject
|
||||
* @param metadataField the metadata field to which the value is to be set
|
||||
* @param language the ISO639 language code, optionally followed by an underscore
|
||||
* and the ISO3166 country code. <code>null</code> means the
|
||||
* value has no language (for example, a date).
|
||||
* @param value the value to add.
|
||||
* @return the MetadataValue added ot the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public MetadataValue addMetadata(Context context, T dso, MetadataField metadataField, String language, String value)
|
||||
throws SQLException;
|
||||
|
||||
public void addMetadata(Context context, T dso, MetadataField metadataField, String language, List<String> values)
|
||||
throws SQLException;
|
||||
/**
|
||||
* Add a metadatafields. These are appended to existing values.
|
||||
* Use <code>clearMetadata</code> to remove values.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param dso DSpaceObject
|
||||
* @param metadataField the metadata field to which the value is to be set
|
||||
* @param language the ISO639 language code, optionally followed by an underscore
|
||||
* and the ISO3166 country code. <code>null</code> means the
|
||||
* value has no language (for example, a date).
|
||||
* @param values the values to add.
|
||||
* @return the list of MetadataValues added to the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String language,
|
||||
List<String> values) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add a single metadata field. This is appended to existing
|
||||
@@ -285,10 +319,11 @@ public interface DSpaceObjectService<T extends DSpaceObject> {
|
||||
* and the ISO3166 country code. <code>null</code> means the
|
||||
* value has no language (for example, a date).
|
||||
* @param value the value to add.
|
||||
* @return the MetadataValue added ot the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
String value) throws SQLException;
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add a single metadata field. This is appended to existing
|
||||
@@ -307,10 +342,11 @@ public interface DSpaceObjectService<T extends DSpaceObject> {
|
||||
* @param value the value to add.
|
||||
* @param authority the external authority key for this value (or null)
|
||||
* @param confidence the authority confidence (default 0)
|
||||
* @return the MetadataValue added ot the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void addMetadata(Context context, T dso, String schema, String element, String qualifier, String lang,
|
||||
String value, String authority, int confidence) throws SQLException;
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value, String authority, int confidence) throws SQLException;
|
||||
|
||||
/**
|
||||
* Clear metadata values. As with <code>getDC</code> above,
|
||||
|
@@ -113,6 +113,21 @@ public interface ItemService
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all the items by a given submitter. The order is
|
||||
* indeterminate. All items are included.
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param eperson the submitter
|
||||
* @param retrieveAllItems flag to determine if all items should be returned or only archived items.
|
||||
* If true, all items (regardless of status) are returned.
|
||||
* If false, only archived items will be returned.
|
||||
* @return an iterator over the items submitted by eperson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Retrieve the list of items submitted by eperson, ordered by recently submitted, optionally limitable
|
||||
*
|
||||
|
@@ -190,7 +190,15 @@ public class Context implements AutoCloseable {
|
||||
setMode(this.mode);
|
||||
}
|
||||
|
||||
public static boolean updateDatabase() {
|
||||
/**
|
||||
* Update the DSpace database, ensuring that any necessary migrations are run prior to initializing
|
||||
* Hibernate.
|
||||
* <P>
|
||||
* This is synchronized as it only needs to be run successfully *once* (for the first Context initialized).
|
||||
*
|
||||
* @return true/false, based on whether database was successfully updated
|
||||
*/
|
||||
public static synchronized boolean updateDatabase() {
|
||||
//If the database has not been updated yet, update it and remember that.
|
||||
if (databaseUpdated.compareAndSet(false, true)) {
|
||||
|
||||
@@ -200,7 +208,7 @@ public class Context implements AutoCloseable {
|
||||
try {
|
||||
DatabaseUtils.updateDatabase();
|
||||
} catch (SQLException sqle) {
|
||||
log.fatal("Cannot initialize database via Flyway!", sqle);
|
||||
log.fatal("Cannot update or initialize database via Flyway!", sqle);
|
||||
databaseUpdated.set(false);
|
||||
}
|
||||
}
|
||||
|
@@ -191,6 +191,23 @@ public class I18nUtil {
|
||||
return supportedLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate supported Locale according for a given Locale If
|
||||
* no appropriate supported locale is found, the DEFAULTLOCALE is used
|
||||
*
|
||||
* @param locale String to find the corresponding Locale
|
||||
* @return supportedLocale
|
||||
* Locale for session according to locales supported by this DSpace instance as set in dspace.cfg
|
||||
*/
|
||||
public static Locale getSupportedLocale(String locale) {
|
||||
Locale currentLocale = null;
|
||||
if (locale != null) {
|
||||
currentLocale = I18nUtil.getSupportedLocale(new Locale(locale));
|
||||
} else {
|
||||
currentLocale = I18nUtil.getDefaultLocale();
|
||||
}
|
||||
return currentLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate localized version of submission-forms.xml according to language settings
|
||||
|
@@ -345,8 +345,8 @@ public class LegacyPluginServiceImpl implements PluginService {
|
||||
" for interface=" + iname +
|
||||
" pluginName=" + name);
|
||||
Object result = pluginClass.newInstance();
|
||||
if (result instanceof SelfNamedPlugin) {
|
||||
((SelfNamedPlugin) result).setPluginInstanceName(name);
|
||||
if (result instanceof NameAwarePlugin) {
|
||||
((NameAwarePlugin) result).setPluginInstanceName(name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.core;
|
||||
|
||||
/**
|
||||
* This is the interface that should be implemented by all the named plugin that
|
||||
* like to be aware of their name
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
* @version $Revision$
|
||||
* @see org.dspace.core.service.PluginService
|
||||
*/
|
||||
public interface NameAwarePlugin {
|
||||
|
||||
/**
|
||||
* Get the instance's particular name.
|
||||
* Returns the name by which the class was chosen when
|
||||
* this instance was created. Only works for instances created
|
||||
* by <code>PluginService</code>, or if someone remembers to call <code>setPluginName.</code>
|
||||
* <p>
|
||||
* Useful when the implementation class wants to be configured differently
|
||||
* when it is invoked under different names.
|
||||
*
|
||||
* @return name or null if not available.
|
||||
*/
|
||||
public String getPluginInstanceName();
|
||||
|
||||
/**
|
||||
* Set the name under which this plugin was instantiated.
|
||||
* Not to be invoked by application code, it is
|
||||
* called automatically by <code>PluginService.getNamedPlugin()</code>
|
||||
* when the plugin is instantiated.
|
||||
*
|
||||
* @param name -- name used to select this class.
|
||||
*/
|
||||
public void setPluginInstanceName(String name);
|
||||
}
|
@@ -28,7 +28,7 @@ package org.dspace.core;
|
||||
* @version $Revision$
|
||||
* @see org.dspace.core.service.PluginService
|
||||
*/
|
||||
public abstract class SelfNamedPlugin {
|
||||
public abstract class SelfNamedPlugin implements NameAwarePlugin {
|
||||
// the specific alias used to find the class that created this instance.
|
||||
private String myName = null;
|
||||
|
||||
@@ -52,30 +52,13 @@ public abstract class SelfNamedPlugin {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance's particular name.
|
||||
* Returns the name by which the class was chosen when
|
||||
* this instance was created. Only works for instances created
|
||||
* by <code>PluginService</code>, or if someone remembers to call <code>setPluginName.</code>
|
||||
* <p>
|
||||
* Useful when the implementation class wants to be configured differently
|
||||
* when it is invoked under different names.
|
||||
*
|
||||
* @return name or null if not available.
|
||||
*/
|
||||
@Override
|
||||
public String getPluginInstanceName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name under which this plugin was instantiated.
|
||||
* Not to be invoked by application code, it is
|
||||
* called automatically by <code>PluginService.getNamedPlugin()</code>
|
||||
* when the plugin is instantiated.
|
||||
*
|
||||
* @param name -- name used to select this class.
|
||||
*/
|
||||
protected void setPluginInstanceName(String name) {
|
||||
@Override
|
||||
public void setPluginInstanceName(String name) {
|
||||
myName = name;
|
||||
}
|
||||
}
|
||||
|
371
dspace-api/src/main/java/org/dspace/curate/Curation.java
Normal file
371
dspace-api/src/main/java/org/dspace/curate/Curation.java
Normal 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.curate;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Writer;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.factory.CoreServiceFactory;
|
||||
import org.dspace.curate.factory.CurateServiceFactory;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.handle.factory.HandleServiceFactory;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
/**
|
||||
* CurationCli provides command-line access to Curation tools and processes.
|
||||
*
|
||||
* @author richardrodgers
|
||||
*/
|
||||
public class Curation extends DSpaceRunnable<CurationScriptConfiguration> {
|
||||
|
||||
protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||
|
||||
protected Context context;
|
||||
private CurationClientOptions curationClientOptions;
|
||||
|
||||
private String task;
|
||||
private String taskFile;
|
||||
private String id;
|
||||
private String queue;
|
||||
private String scope;
|
||||
private String reporter;
|
||||
private Map<String, String> parameters;
|
||||
private boolean verbose;
|
||||
|
||||
@Override
|
||||
public void internalRun() throws Exception {
|
||||
if (curationClientOptions == CurationClientOptions.HELP) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
Curator curator = initCurator();
|
||||
|
||||
// load curation tasks
|
||||
if (curationClientOptions == CurationClientOptions.TASK) {
|
||||
long start = System.currentTimeMillis();
|
||||
handleCurationTask(curator);
|
||||
this.endScript(start);
|
||||
}
|
||||
|
||||
// process task queue
|
||||
if (curationClientOptions == CurationClientOptions.QUEUE) {
|
||||
// process the task queue
|
||||
TaskQueue taskQueue = (TaskQueue) CoreServiceFactory.getInstance().getPluginService()
|
||||
.getSinglePlugin(TaskQueue.class);
|
||||
if (taskQueue == null) {
|
||||
super.handler.logError("No implementation configured for queue");
|
||||
throw new UnsupportedOperationException("No queue service available");
|
||||
}
|
||||
long timeRun = this.runQueue(taskQueue, curator);
|
||||
this.endScript(timeRun);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the curation task (-t) or the task in the given file (-T).
|
||||
* Checks:
|
||||
* - if required option -i is missing.
|
||||
* - if option -t has a valid task option
|
||||
*/
|
||||
private void handleCurationTask(Curator curator) throws IOException, SQLException {
|
||||
String taskName;
|
||||
if (commandLine.hasOption('t')) {
|
||||
if (verbose) {
|
||||
handler.logInfo("Adding task: " + this.task);
|
||||
}
|
||||
curator.addTask(this.task);
|
||||
if (verbose && !curator.hasTask(this.task)) {
|
||||
handler.logInfo("Task: " + this.task + " not resolved");
|
||||
}
|
||||
} else if (commandLine.hasOption('T')) {
|
||||
// load taskFile
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(this.taskFile));
|
||||
while ((taskName = reader.readLine()) != null) {
|
||||
if (verbose) {
|
||||
super.handler.logInfo("Adding task: " + taskName);
|
||||
}
|
||||
curator.addTask(taskName);
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
// run tasks against object
|
||||
if (verbose) {
|
||||
super.handler.logInfo("Starting curation");
|
||||
super.handler.logInfo("Curating id: " + this.id);
|
||||
}
|
||||
if ("all".equals(this.id)) {
|
||||
// run on whole Site
|
||||
curator.curate(context,
|
||||
ContentServiceFactory.getInstance().getSiteService().findSite(context).getHandle());
|
||||
} else {
|
||||
curator.curate(context, this.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs task queue (-q set)
|
||||
*
|
||||
* @param queue The task queue
|
||||
* @param curator The curator
|
||||
* @return Time when queue started
|
||||
*/
|
||||
private long runQueue(TaskQueue queue, Curator curator) throws SQLException, AuthorizeException, IOException {
|
||||
// use current time as our reader 'ticket'
|
||||
long ticket = System.currentTimeMillis();
|
||||
Iterator<TaskQueueEntry> entryIter = queue.dequeue(this.queue, ticket).iterator();
|
||||
while (entryIter.hasNext()) {
|
||||
TaskQueueEntry entry = entryIter.next();
|
||||
if (verbose) {
|
||||
super.handler.logInfo("Curating id: " + entry.getObjectId());
|
||||
}
|
||||
curator.clear();
|
||||
// does entry relate to a DSO or workflow object?
|
||||
if (entry.getObjectId().indexOf('/') > 0) {
|
||||
for (String taskName : entry.getTaskNames()) {
|
||||
curator.addTask(taskName);
|
||||
}
|
||||
curator.curate(context, entry.getObjectId());
|
||||
} else {
|
||||
// make eperson who queued task the effective user
|
||||
EPerson agent = ePersonService.findByEmail(context, entry.getEpersonId());
|
||||
if (agent != null) {
|
||||
context.setCurrentUser(agent);
|
||||
}
|
||||
CurateServiceFactory.getInstance().getWorkflowCuratorService()
|
||||
.curate(curator, context, entry.getObjectId());
|
||||
}
|
||||
}
|
||||
queue.release(this.queue, ticket, true);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of curation script; logs script time if -v verbose is set
|
||||
*
|
||||
* @param timeRun Time script was started
|
||||
* @throws SQLException If DSpace contextx can't complete
|
||||
*/
|
||||
private void endScript(long timeRun) throws SQLException {
|
||||
context.complete();
|
||||
if (verbose) {
|
||||
long elapsed = System.currentTimeMillis() - timeRun;
|
||||
this.handler.logInfo("Ending curation. Elapsed time: " + elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the curator with command line variables
|
||||
*
|
||||
* @return Initialised curator
|
||||
* @throws FileNotFoundException If file of command line variable -r reporter is not found
|
||||
*/
|
||||
private Curator initCurator() throws FileNotFoundException {
|
||||
Curator curator = new Curator();
|
||||
OutputStream reporterStream;
|
||||
if (null == this.reporter) {
|
||||
reporterStream = new NullOutputStream();
|
||||
} else if ("-".equals(this.reporter)) {
|
||||
reporterStream = System.out;
|
||||
} else {
|
||||
reporterStream = new PrintStream(this.reporter);
|
||||
}
|
||||
Writer reportWriter = new OutputStreamWriter(reporterStream);
|
||||
curator.setReporter(reportWriter);
|
||||
|
||||
if (this.scope != null) {
|
||||
Curator.TxScope txScope = Curator.TxScope.valueOf(this.scope.toUpperCase());
|
||||
curator.setTransactionScope(txScope);
|
||||
}
|
||||
|
||||
curator.addParameters(parameters);
|
||||
// we are operating in batch mode, if anyone cares.
|
||||
curator.setInvoked(Curator.Invoked.BATCH);
|
||||
return curator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printHelp() {
|
||||
super.printHelp();
|
||||
super.handler.logInfo("\nwhole repo: CurationCli -t estimate -i all");
|
||||
super.handler.logInfo("single item: CurationCli -t generate -i itemId");
|
||||
super.handler.logInfo("task queue: CurationCli -q monthly");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurationScriptConfiguration getScriptConfiguration() {
|
||||
return new DSpace().getServiceManager().getServiceByName("curate", CurationScriptConfiguration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws ParseException {
|
||||
assignCurrentUserInContext();
|
||||
this.curationClientOptions = CurationClientOptions.getClientOption(commandLine);
|
||||
|
||||
if (this.curationClientOptions != null) {
|
||||
this.initGeneralLineOptionsAndCheckIfValid();
|
||||
if (curationClientOptions == CurationClientOptions.TASK) {
|
||||
this.initTaskLineOptionsAndCheckIfValid();
|
||||
} else if (curationClientOptions == CurationClientOptions.QUEUE) {
|
||||
this.queue = this.commandLine.getOptionValue('q');
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("[--help || --task|--taskfile <> -identifier <> || -queue <> ] must be" +
|
||||
" specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will assign the currentUser to the {@link Context} variable which is also created in this method.
|
||||
* The instance of the method in this class will fetch the EPersonIdentifier from this class, this identifier
|
||||
* was given to this class upon instantiation, it'll then be used to find the {@link EPerson} associated with it
|
||||
* and this {@link EPerson} will be set as the currentUser of the created {@link Context}
|
||||
* @throws ParseException If something went wrong with the retrieval of the EPerson Identifier
|
||||
*/
|
||||
protected void assignCurrentUserInContext() throws ParseException {
|
||||
UUID currentUserUuid = this.getEpersonIdentifier();
|
||||
try {
|
||||
this.context = new Context(Context.Mode.BATCH_EDIT);
|
||||
EPerson eperson = ePersonService.find(context, currentUserUuid);
|
||||
if (eperson == null) {
|
||||
super.handler.logError("EPerson not found: " + currentUserUuid);
|
||||
throw new IllegalArgumentException("Unable to find a user with uuid: " + currentUserUuid);
|
||||
}
|
||||
this.context.setCurrentUser(eperson);
|
||||
} catch (SQLException e) {
|
||||
handler.handleException("Something went wrong trying to fetch eperson for uuid: " + currentUserUuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in some optional command line options.
|
||||
* Checks if there are missing required options or invalid values for options.
|
||||
*/
|
||||
private void initGeneralLineOptionsAndCheckIfValid() {
|
||||
// report file
|
||||
if (this.commandLine.hasOption('r')) {
|
||||
this.reporter = this.commandLine.getOptionValue('r');
|
||||
}
|
||||
|
||||
// parameters
|
||||
this.parameters = new HashMap<>();
|
||||
if (this.commandLine.hasOption('p')) {
|
||||
for (String parameter : this.commandLine.getOptionValues('p')) {
|
||||
String[] parts = parameter.split("=", 2);
|
||||
String name = parts[0].trim();
|
||||
String value;
|
||||
if (parts.length > 1) {
|
||||
value = parts[1].trim();
|
||||
} else {
|
||||
value = "true";
|
||||
}
|
||||
this.parameters.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// verbose
|
||||
verbose = false;
|
||||
if (commandLine.hasOption('v')) {
|
||||
verbose = true;
|
||||
}
|
||||
|
||||
// scope
|
||||
if (this.commandLine.getOptionValue('s') != null) {
|
||||
this.scope = this.commandLine.getOptionValue('s');
|
||||
if (this.scope != null && Curator.TxScope.valueOf(this.scope.toUpperCase()) == null) {
|
||||
this.handler.logError("Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " +
|
||||
"'open' recognized");
|
||||
throw new IllegalArgumentException(
|
||||
"Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " +
|
||||
"'open' recognized");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in required command line options for the task or taskFile option.
|
||||
* Checks if there are is a missing required -i option and if -i is either 'all' or a valid dso handle.
|
||||
* Checks if -t task has a valid task option.
|
||||
* Checks if -T taskfile is a valid file.
|
||||
*/
|
||||
private void initTaskLineOptionsAndCheckIfValid() {
|
||||
// task or taskFile
|
||||
if (this.commandLine.hasOption('t')) {
|
||||
this.task = this.commandLine.getOptionValue('t');
|
||||
if (!CurationClientOptions.getTaskOptions().contains(this.task)) {
|
||||
super.handler
|
||||
.logError("-t task must be one of: " + CurationClientOptions.getTaskOptions());
|
||||
throw new IllegalArgumentException(
|
||||
"-t task must be one of: " + CurationClientOptions.getTaskOptions());
|
||||
}
|
||||
} else if (this.commandLine.hasOption('T')) {
|
||||
this.taskFile = this.commandLine.getOptionValue('T');
|
||||
if (!(new File(this.taskFile).isFile())) {
|
||||
super.handler
|
||||
.logError("-T taskFile must be valid file: " + this.taskFile);
|
||||
throw new IllegalArgumentException("-T taskFile must be valid file: " + this.taskFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.commandLine.hasOption('i')) {
|
||||
this.id = this.commandLine.getOptionValue('i').toLowerCase();
|
||||
if (!this.id.equalsIgnoreCase("all")) {
|
||||
HandleService handleService = HandleServiceFactory.getInstance().getHandleService();
|
||||
DSpaceObject dso;
|
||||
try {
|
||||
dso = handleService.resolveToObject(this.context, id);
|
||||
} catch (SQLException e) {
|
||||
super.handler.logError("SQLException trying to resolve handle " + id + " to a valid dso");
|
||||
throw new IllegalArgumentException(
|
||||
"SQLException trying to resolve handle " + id + " to a valid dso");
|
||||
}
|
||||
if (dso == null) {
|
||||
super.handler.logError("Id must be specified: a valid dso handle or 'all'; " + this.id + " could " +
|
||||
"not be resolved to valid dso handle");
|
||||
throw new IllegalArgumentException(
|
||||
"Id must be specified: a valid dso handle or 'all'; " + this.id + " could " +
|
||||
"not be resolved to valid dso handle");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.handler.logError("Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " +
|
||||
"help)");
|
||||
throw new IllegalArgumentException(
|
||||
"Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " +
|
||||
"help)");
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,229 +7,27 @@
|
||||
*/
|
||||
package org.dspace.curate;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Writer;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.factory.CoreServiceFactory;
|
||||
import org.dspace.curate.factory.CurateServiceFactory;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.handle.factory.HandleServiceFactory;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
/**
|
||||
* CurationCli provides command-line access to Curation tools and processes.
|
||||
*
|
||||
* @author richardrodgers
|
||||
* This is the CLI version of the {@link Curation} script.
|
||||
* This will only be called when the curate script is called from a commandline instance.
|
||||
*/
|
||||
public class CurationCli extends DSpaceRunnable<CurationScriptConfiguration> {
|
||||
|
||||
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||
|
||||
private Context context;
|
||||
private CurationClientOptions curationClientOptions;
|
||||
|
||||
private String task;
|
||||
private String taskFile;
|
||||
private String id;
|
||||
private String queue;
|
||||
private String scope;
|
||||
private String reporter;
|
||||
private Map<String, String> parameters;
|
||||
private boolean verbose;
|
||||
|
||||
@Override
|
||||
public void internalRun() throws Exception {
|
||||
if (curationClientOptions == CurationClientOptions.HELP) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
Curator curator = initCurator();
|
||||
|
||||
// load curation tasks
|
||||
if (curationClientOptions == CurationClientOptions.TASK) {
|
||||
long start = System.currentTimeMillis();
|
||||
handleCurationTask(curator);
|
||||
this.endScript(start);
|
||||
}
|
||||
|
||||
// process task queue
|
||||
if (curationClientOptions == CurationClientOptions.QUEUE) {
|
||||
// process the task queue
|
||||
TaskQueue taskQueue = (TaskQueue) CoreServiceFactory.getInstance().getPluginService()
|
||||
.getSinglePlugin(TaskQueue.class);
|
||||
if (taskQueue == null) {
|
||||
super.handler.logError("No implementation configured for queue");
|
||||
throw new UnsupportedOperationException("No queue service available");
|
||||
}
|
||||
long timeRun = this.runQueue(taskQueue, curator);
|
||||
this.endScript(timeRun);
|
||||
}
|
||||
}
|
||||
public class CurationCli extends Curation {
|
||||
|
||||
/**
|
||||
* Does the curation task (-t) or the task in the given file (-T).
|
||||
* Checks:
|
||||
* - if required option -i is missing.
|
||||
* - if option -t has a valid task option
|
||||
* This is the overridden instance of the {@link Curation#assignCurrentUserInContext()} method in the parent class
|
||||
* {@link Curation}.
|
||||
* This is done so that the CLI version of the Script is able to retrieve its currentUser from the -e flag given
|
||||
* with the parameters of the Script.
|
||||
* @throws ParseException If the e flag was not given to the parameters when calling the script
|
||||
*/
|
||||
private void handleCurationTask(Curator curator) throws IOException, SQLException {
|
||||
String taskName;
|
||||
if (commandLine.hasOption('t')) {
|
||||
if (verbose) {
|
||||
handler.logInfo("Adding task: " + this.task);
|
||||
}
|
||||
curator.addTask(this.task);
|
||||
if (verbose && !curator.hasTask(this.task)) {
|
||||
handler.logInfo("Task: " + this.task + " not resolved");
|
||||
}
|
||||
} else if (commandLine.hasOption('T')) {
|
||||
// load taskFile
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(this.taskFile));
|
||||
while ((taskName = reader.readLine()) != null) {
|
||||
if (verbose) {
|
||||
super.handler.logInfo("Adding task: " + taskName);
|
||||
}
|
||||
curator.addTask(taskName);
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
// run tasks against object
|
||||
if (verbose) {
|
||||
super.handler.logInfo("Starting curation");
|
||||
super.handler.logInfo("Curating id: " + this.id);
|
||||
}
|
||||
if ("all".equals(this.id)) {
|
||||
// run on whole Site
|
||||
curator.curate(context,
|
||||
ContentServiceFactory.getInstance().getSiteService().findSite(context).getHandle());
|
||||
} else {
|
||||
curator.curate(context, this.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs task queue (-q set)
|
||||
*
|
||||
* @param queue The task queue
|
||||
* @param curator The curator
|
||||
* @return Time when queue started
|
||||
*/
|
||||
private long runQueue(TaskQueue queue, Curator curator) throws SQLException, AuthorizeException, IOException {
|
||||
// use current time as our reader 'ticket'
|
||||
long ticket = System.currentTimeMillis();
|
||||
Iterator<TaskQueueEntry> entryIter = queue.dequeue(this.queue, ticket).iterator();
|
||||
while (entryIter.hasNext()) {
|
||||
TaskQueueEntry entry = entryIter.next();
|
||||
if (verbose) {
|
||||
super.handler.logInfo("Curating id: " + entry.getObjectId());
|
||||
}
|
||||
curator.clear();
|
||||
// does entry relate to a DSO or workflow object?
|
||||
if (entry.getObjectId().indexOf('/') > 0) {
|
||||
for (String taskName : entry.getTaskNames()) {
|
||||
curator.addTask(taskName);
|
||||
}
|
||||
curator.curate(context, entry.getObjectId());
|
||||
} else {
|
||||
// make eperson who queued task the effective user
|
||||
EPerson agent = ePersonService.findByEmail(context, entry.getEpersonId());
|
||||
if (agent != null) {
|
||||
context.setCurrentUser(agent);
|
||||
}
|
||||
CurateServiceFactory.getInstance().getWorkflowCuratorService()
|
||||
.curate(curator, context, entry.getObjectId());
|
||||
}
|
||||
}
|
||||
queue.release(this.queue, ticket, true);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of curation script; logs script time if -v verbose is set
|
||||
*
|
||||
* @param timeRun Time script was started
|
||||
* @throws SQLException If DSpace contextx can't complete
|
||||
*/
|
||||
private void endScript(long timeRun) throws SQLException {
|
||||
context.complete();
|
||||
if (verbose) {
|
||||
long elapsed = System.currentTimeMillis() - timeRun;
|
||||
this.handler.logInfo("Ending curation. Elapsed time: " + elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the curator with command line variables
|
||||
*
|
||||
* @return Initialised curator
|
||||
* @throws FileNotFoundException If file of command line variable -r reporter is not found
|
||||
*/
|
||||
private Curator initCurator() throws FileNotFoundException {
|
||||
Curator curator = new Curator();
|
||||
OutputStream reporterStream;
|
||||
if (null == this.reporter) {
|
||||
reporterStream = new NullOutputStream();
|
||||
} else if ("-".equals(this.reporter)) {
|
||||
reporterStream = System.out;
|
||||
} else {
|
||||
reporterStream = new PrintStream(this.reporter);
|
||||
}
|
||||
Writer reportWriter = new OutputStreamWriter(reporterStream);
|
||||
curator.setReporter(reportWriter);
|
||||
|
||||
if (this.scope != null) {
|
||||
Curator.TxScope txScope = Curator.TxScope.valueOf(this.scope.toUpperCase());
|
||||
curator.setTransactionScope(txScope);
|
||||
}
|
||||
|
||||
curator.addParameters(parameters);
|
||||
// we are operating in batch mode, if anyone cares.
|
||||
curator.setInvoked(Curator.Invoked.BATCH);
|
||||
return curator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printHelp() {
|
||||
super.printHelp();
|
||||
super.handler.logInfo("\nwhole repo: CurationCli -t estimate -i all");
|
||||
super.handler.logInfo("single item: CurationCli -t generate -i itemId");
|
||||
super.handler.logInfo("task queue: CurationCli -q monthly");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurationScriptConfiguration getScriptConfiguration() {
|
||||
return new DSpace().getServiceManager().getServiceByName("curate", CurationScriptConfiguration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
protected void assignCurrentUserInContext() throws ParseException {
|
||||
if (this.commandLine.hasOption('e')) {
|
||||
String ePersonEmail = this.commandLine.getOptionValue('e');
|
||||
this.context = new Context(Context.Mode.BATCH_EDIT);
|
||||
@@ -244,119 +42,7 @@ public class CurationCli extends DSpaceRunnable<CurationScriptConfiguration> {
|
||||
throw new IllegalArgumentException("SQLException trying to find user with email: " + ePersonEmail);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Needs an -e to set eperson (admin)");
|
||||
}
|
||||
this.curationClientOptions = CurationClientOptions.getClientOption(commandLine);
|
||||
|
||||
if (this.curationClientOptions != null) {
|
||||
this.initGeneralLineOptionsAndCheckIfValid();
|
||||
if (curationClientOptions == CurationClientOptions.TASK) {
|
||||
this.initTaskLineOptionsAndCheckIfValid();
|
||||
} else if (curationClientOptions == CurationClientOptions.QUEUE) {
|
||||
this.queue = this.commandLine.getOptionValue('q');
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("[--help || --task|--taskfile <> -identifier <> || -queue <> ] must be" +
|
||||
" specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in some optional command line options.
|
||||
* Checks if there are missing required options or invalid values for options.
|
||||
*/
|
||||
private void initGeneralLineOptionsAndCheckIfValid() {
|
||||
// report file
|
||||
if (this.commandLine.hasOption('r')) {
|
||||
this.reporter = this.commandLine.getOptionValue('r');
|
||||
}
|
||||
|
||||
// parameters
|
||||
this.parameters = new HashMap<>();
|
||||
if (this.commandLine.hasOption('p')) {
|
||||
for (String parameter : this.commandLine.getOptionValues('p')) {
|
||||
String[] parts = parameter.split("=", 2);
|
||||
String name = parts[0].trim();
|
||||
String value;
|
||||
if (parts.length > 1) {
|
||||
value = parts[1].trim();
|
||||
} else {
|
||||
value = "true";
|
||||
}
|
||||
this.parameters.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// verbose
|
||||
verbose = false;
|
||||
if (commandLine.hasOption('v')) {
|
||||
verbose = true;
|
||||
}
|
||||
|
||||
// scope
|
||||
if (this.commandLine.getOptionValue('s') != null) {
|
||||
this.scope = this.commandLine.getOptionValue('s');
|
||||
if (this.scope != null && Curator.TxScope.valueOf(this.scope.toUpperCase()) == null) {
|
||||
this.handler.logError("Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " +
|
||||
"'open' recognized");
|
||||
throw new IllegalArgumentException(
|
||||
"Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " +
|
||||
"'open' recognized");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in required command line options for the task or taskFile option.
|
||||
* Checks if there are is a missing required -i option and if -i is either 'all' or a valid dso handle.
|
||||
* Checks if -t task has a valid task option.
|
||||
* Checks if -T taskfile is a valid file.
|
||||
*/
|
||||
private void initTaskLineOptionsAndCheckIfValid() {
|
||||
// task or taskFile
|
||||
if (this.commandLine.hasOption('t')) {
|
||||
this.task = this.commandLine.getOptionValue('t');
|
||||
if (!CurationClientOptions.getTaskOptions().contains(this.task)) {
|
||||
super.handler
|
||||
.logError("-t task must be one of: " + CurationClientOptions.getTaskOptions());
|
||||
throw new IllegalArgumentException(
|
||||
"-t task must be one of: " + CurationClientOptions.getTaskOptions());
|
||||
}
|
||||
} else if (this.commandLine.hasOption('T')) {
|
||||
this.taskFile = this.commandLine.getOptionValue('T');
|
||||
if (!(new File(this.taskFile).isFile())) {
|
||||
super.handler
|
||||
.logError("-T taskFile must be valid file: " + this.taskFile);
|
||||
throw new IllegalArgumentException("-T taskFile must be valid file: " + this.taskFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.commandLine.hasOption('i')) {
|
||||
this.id = this.commandLine.getOptionValue('i').toLowerCase();
|
||||
if (!this.id.equalsIgnoreCase("all")) {
|
||||
HandleService handleService = HandleServiceFactory.getInstance().getHandleService();
|
||||
DSpaceObject dso;
|
||||
try {
|
||||
dso = handleService.resolveToObject(this.context, id);
|
||||
} catch (SQLException e) {
|
||||
super.handler.logError("SQLException trying to resolve handle " + id + " to a valid dso");
|
||||
throw new IllegalArgumentException(
|
||||
"SQLException trying to resolve handle " + id + " to a valid dso");
|
||||
}
|
||||
if (dso == null) {
|
||||
super.handler.logError("Id must be specified: a valid dso handle or 'all'; " + this.id + " could " +
|
||||
"not be resolved to valid dso handle");
|
||||
throw new IllegalArgumentException(
|
||||
"Id must be specified: a valid dso handle or 'all'; " + this.id + " could " +
|
||||
"not be resolved to valid dso handle");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.handler.logError("Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " +
|
||||
"help)");
|
||||
throw new IllegalArgumentException(
|
||||
"Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " +
|
||||
"help)");
|
||||
throw new ParseException("Required parameter -e missing!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.curate;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
|
||||
/**
|
||||
* This is the CLI version of the {@link CurationScriptConfiguration} class that handles the configuration for the
|
||||
* {@link CurationCli} script
|
||||
*/
|
||||
public class CurationCliScriptConfiguration extends CurationScriptConfiguration<Curation> {
|
||||
|
||||
@Override
|
||||
public Options getOptions() {
|
||||
options = super.getOptions();
|
||||
options.addOption("e", "eperson", true, "email address of curating eperson");
|
||||
options.getOption("e").setType(String.class);
|
||||
options.getOption("e").setRequired(true);
|
||||
return options;
|
||||
}
|
||||
}
|
@@ -45,6 +45,11 @@ public enum CurationClientOptions {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will create all the possible Options for the {@link Curation} script.
|
||||
* This will be used by {@link CurationScriptConfiguration}
|
||||
* @return The options for the {@link Curation} script
|
||||
*/
|
||||
protected static Options constructOptions() {
|
||||
Options options = new Options();
|
||||
|
||||
@@ -54,7 +59,6 @@ public enum CurationClientOptions {
|
||||
"Id (handle) of object to perform task on, or 'all' to perform on whole repository");
|
||||
options.addOption("p", "parameter", true, "a task parameter 'NAME=VALUE'");
|
||||
options.addOption("q", "queue", true, "name of task queue to process");
|
||||
options.addOption("e", "eperson", true, "email address of curating eperson");
|
||||
options.addOption("r", "reporter", true,
|
||||
"relative or absolute path to the desired report file. Use '-' to report to console. If absent, no " +
|
||||
"reporting");
|
||||
|
@@ -16,11 +16,11 @@ import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* The {@link ScriptConfiguration} for the {@link CurationCli} script
|
||||
* The {@link ScriptConfiguration} for the {@link Curation} script
|
||||
*
|
||||
* @author Maria Verdonck (Atmire) on 23/06/2020
|
||||
*/
|
||||
public class CurationScriptConfiguration<T extends CurationCli> extends ScriptConfiguration<T> {
|
||||
public class CurationScriptConfiguration<T extends Curation> extends ScriptConfiguration<T> {
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
package org.dspace.discovery;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -15,6 +16,7 @@ import org.dspace.content.Bundle;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.indexobject.factory.IndexFactory;
|
||||
import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
|
||||
import org.dspace.event.Consumer;
|
||||
import org.dspace.event.Event;
|
||||
@@ -67,7 +69,7 @@ public class IndexEventConsumer implements Consumer {
|
||||
|
||||
int st = event.getSubjectType();
|
||||
if (!(st == Constants.ITEM || st == Constants.BUNDLE
|
||||
|| st == Constants.COLLECTION || st == Constants.COMMUNITY)) {
|
||||
|| st == Constants.COLLECTION || st == Constants.COMMUNITY || st == Constants.SITE)) {
|
||||
log
|
||||
.warn("IndexConsumer should not have been given this kind of Subject in an event, skipping: "
|
||||
+ event.toString());
|
||||
@@ -104,10 +106,28 @@ public class IndexEventConsumer implements Consumer {
|
||||
case Event.MODIFY:
|
||||
case Event.MODIFY_METADATA:
|
||||
if (subject == null) {
|
||||
if (st == Constants.SITE) {
|
||||
// Update the indexable objects of type in event.detail of objects with ids in event.identifiers
|
||||
for (String id : event.getIdentifiers()) {
|
||||
IndexFactory indexableObjectService = IndexObjectFactoryFactory.getInstance().
|
||||
getIndexFactoryByType(event.getDetail());
|
||||
Optional<IndexableObject> indexableObject = Optional.empty();
|
||||
indexableObject = indexableObjectService.findIndexableObject(ctx, id);
|
||||
if (indexableObject.isPresent()) {
|
||||
log.debug("consume() adding event to update queue: " + event.toString());
|
||||
objectsToUpdate
|
||||
.addAll(indexObjectServiceFactory
|
||||
.getIndexableObjects(ctx, indexableObject.get().getIndexedObject()));
|
||||
} else {
|
||||
log.warn("Cannot resolve " + id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn(event.getEventTypeAsString() + " event, could not get object for "
|
||||
+ event.getSubjectTypeAsString() + " id="
|
||||
+ event.getSubjectID()
|
||||
+ ", perhaps it has been deleted.");
|
||||
}
|
||||
} else {
|
||||
log.debug("consume() adding event to update queue: " + event.toString());
|
||||
objectsToUpdate.addAll(indexObjectServiceFactory.getIndexableObjects(ctx, subject));
|
||||
|
@@ -17,6 +17,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.dspace.browse.BrowseException;
|
||||
import org.dspace.browse.BrowseIndex;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.authority.service.ChoiceAuthorityService;
|
||||
@@ -63,7 +64,7 @@ public class SolrServiceMetadataBrowseIndexingPlugin implements SolrServiceIndex
|
||||
return;
|
||||
}
|
||||
Item item = ((IndexableItem) indexableObject).getIndexedObject();
|
||||
|
||||
Collection collection = item.getOwningCollection();
|
||||
// Get the currently configured browse indexes
|
||||
BrowseIndex[] bis;
|
||||
try {
|
||||
@@ -175,7 +176,7 @@ public class SolrServiceMetadataBrowseIndexingPlugin implements SolrServiceIndex
|
||||
true);
|
||||
if (!ignorePrefered) {
|
||||
preferedLabel = choiceAuthorityService
|
||||
.getLabel(values.get(x), values.get(x).getLanguage());
|
||||
.getLabel(values.get(x), collection, values.get(x).getLanguage());
|
||||
}
|
||||
List<String> variants = null;
|
||||
|
||||
@@ -195,7 +196,7 @@ public class SolrServiceMetadataBrowseIndexingPlugin implements SolrServiceIndex
|
||||
if (!ignoreVariants) {
|
||||
variants = choiceAuthorityService
|
||||
.getVariants(
|
||||
values.get(x));
|
||||
values.get(x), collection);
|
||||
}
|
||||
|
||||
if (StringUtils
|
||||
|
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.discovery.indexobject;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
|
||||
/**
|
||||
* This class exists in order to provide a default implementation for the equals and hashCode methods.
|
||||
* Since IndexableObjects can be made multiple times for the same underlying object, we needed a more finetuned
|
||||
* equals and hashcode methods. We're simply checking that the underlying objects are equal and generating the hashcode
|
||||
* for the underlying object. This way, we'll always get a proper result when calling equals or hashcode on an
|
||||
* IndexableObject because it'll depend on the underlying object
|
||||
* @param <T> Refers to the underlying entity that is linked to this object
|
||||
* @param <PK> The type of ID that this entity uses
|
||||
*/
|
||||
public abstract class AbstractIndexableObject<T extends ReloadableEntity<PK>, PK extends Serializable>
|
||||
implements IndexableObject<T,PK> {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
//Two IndexableObjects of the same DSpaceObject are considered equal
|
||||
if (!(obj instanceof AbstractIndexableObject)) {
|
||||
return false;
|
||||
}
|
||||
IndexableDSpaceObject other = (IndexableDSpaceObject) obj;
|
||||
return other.getIndexedObject().equals(getIndexedObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
//Two IndexableObjects of the same DSpaceObject are considered equal
|
||||
return getIndexedObject().hashCode();
|
||||
}
|
||||
|
||||
}
|
@@ -12,6 +12,7 @@ import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
@@ -56,7 +57,7 @@ public abstract class IndexFactoryImpl<T extends IndexableObject, S> implements
|
||||
doc.addField(SearchUtils.RESOURCE_ID_FIELD, indexableObject.getID().toString());
|
||||
|
||||
//Do any additional indexing, depends on the plugins
|
||||
for (SolrServiceIndexPlugin solrServiceIndexPlugin : solrServiceIndexPlugins) {
|
||||
for (SolrServiceIndexPlugin solrServiceIndexPlugin : ListUtils.emptyIfNull(solrServiceIndexPlugins)) {
|
||||
solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc);
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
package org.dspace.discovery.indexobject;
|
||||
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
|
||||
/**
|
||||
@@ -15,7 +14,7 @@ import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
*/
|
||||
public class IndexableClaimedTask implements IndexableObject<ClaimedTask, Integer> {
|
||||
public class IndexableClaimedTask extends AbstractIndexableObject<ClaimedTask, Integer> {
|
||||
|
||||
private ClaimedTask claimedTask;
|
||||
public static final String TYPE = ClaimedTask.class.getSimpleName();
|
||||
|
@@ -10,7 +10,6 @@ package org.dspace.discovery.indexobject;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
|
||||
/**
|
||||
* DSpaceObject implementation for the IndexableObject, contains methods used by all DSpaceObject methods
|
||||
@@ -18,7 +17,7 @@ import org.dspace.discovery.IndexableObject;
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
*/
|
||||
public abstract class IndexableDSpaceObject<T extends DSpaceObject> implements IndexableObject<T, UUID> {
|
||||
public abstract class IndexableDSpaceObject<T extends DSpaceObject> extends AbstractIndexableObject<T, UUID> {
|
||||
|
||||
private T dso;
|
||||
|
||||
@@ -40,4 +39,6 @@ public abstract class IndexableDSpaceObject<T extends DSpaceObject> implements I
|
||||
public UUID getID() {
|
||||
return dso.getID();
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -8,14 +8,13 @@
|
||||
package org.dspace.discovery.indexobject;
|
||||
|
||||
import org.dspace.content.InProgressSubmission;
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
|
||||
/**
|
||||
* InProgressSubmission implementation for the IndexableObject
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
*/
|
||||
public abstract class IndexableInProgressSubmission<T extends InProgressSubmission>
|
||||
implements IndexableObject<T, Integer> {
|
||||
extends AbstractIndexableObject<T, Integer> {
|
||||
|
||||
protected T inProgressSubmission;
|
||||
|
||||
|
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.discovery.indexobject;
|
||||
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
|
||||
/**
|
||||
* {@link MetadataField} implementation for the {@link IndexableObject}
|
||||
*
|
||||
* @author Maria Verdonck (Atmire) on 14/07/2020
|
||||
*/
|
||||
public class IndexableMetadataField extends AbstractIndexableObject<MetadataField, Integer> {
|
||||
|
||||
private MetadataField metadataField;
|
||||
public static final String TYPE = MetadataField.class.getSimpleName();
|
||||
|
||||
public IndexableMetadataField(MetadataField metadataField) {
|
||||
this.metadataField = metadataField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getID() {
|
||||
return this.metadataField.getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataField getIndexedObject() {
|
||||
return this.metadataField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndexedObject(MetadataField metadataField) {
|
||||
this.metadataField = metadataField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeText() {
|
||||
return TYPE.toUpperCase();
|
||||
}
|
||||
}
|
@@ -7,14 +7,13 @@
|
||||
*/
|
||||
package org.dspace.discovery.indexobject;
|
||||
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
|
||||
|
||||
/**
|
||||
* PoolTask implementation for the IndexableObject
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
*/
|
||||
public class IndexablePoolTask implements IndexableObject<PoolTask, Integer> {
|
||||
public class IndexablePoolTask extends AbstractIndexableObject<PoolTask, Integer> {
|
||||
|
||||
public static final String TYPE = PoolTask.class.getSimpleName();
|
||||
|
||||
|
@@ -173,6 +173,8 @@ public class ItemIndexFactoryImpl extends DSpaceObjectIndexFactoryImpl<Indexable
|
||||
public void addDiscoveryFields(SolrInputDocument doc, Context context, Item item,
|
||||
List<DiscoveryConfiguration> discoveryConfigurations)
|
||||
throws SQLException, IOException {
|
||||
// use the item service to retrieve the owning collection also for inprogress submission
|
||||
Collection collection = (Collection) itemService.getParentObject(context, item);
|
||||
//Keep a list of our sort values which we added, sort values can only be added once
|
||||
List<String> sortFieldsAdded = new ArrayList<>();
|
||||
Map<String, List<DiscoverySearchFilter>> searchFilters = null;
|
||||
@@ -359,7 +361,7 @@ public class ItemIndexFactoryImpl extends DSpaceObjectIndexFactoryImpl<Indexable
|
||||
if (!ignorePrefered) {
|
||||
|
||||
preferedLabel = choiceAuthorityService
|
||||
.getLabel(meta, meta.getLanguage());
|
||||
.getLabel(meta, collection, meta.getLanguage());
|
||||
}
|
||||
|
||||
boolean ignoreVariants =
|
||||
@@ -375,7 +377,7 @@ public class ItemIndexFactoryImpl extends DSpaceObjectIndexFactoryImpl<Indexable
|
||||
true);
|
||||
if (!ignoreVariants) {
|
||||
variants = choiceAuthorityService
|
||||
.getVariants(meta);
|
||||
.getVariants(meta, collection);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.discovery.indexobject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.indexobject.factory.MetadataFieldIndexFactory;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Factory implementation for indexing/retrieving {@link org.dspace.content.MetadataField} items in the search core
|
||||
*
|
||||
* @author Maria Verdonck (Atmire) on 14/07/2020
|
||||
*/
|
||||
public class MetadataFieldIndexFactoryImpl extends IndexFactoryImpl<IndexableMetadataField, MetadataField>
|
||||
implements MetadataFieldIndexFactory {
|
||||
|
||||
public static final String SCHEMA_FIELD_NAME = "schema";
|
||||
public static final String ELEMENT_FIELD_NAME = "element";
|
||||
public static final String QUALIFIER_FIELD_NAME = "qualifier";
|
||||
public static final String FIELD_NAME_VARIATIONS = "fieldName";
|
||||
|
||||
protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
|
||||
@Override
|
||||
public SolrInputDocument buildDocument(Context context, IndexableMetadataField indexableObject) throws SQLException,
|
||||
IOException {
|
||||
// Add the ID's, types and call the SolrServiceIndexPlugins
|
||||
final SolrInputDocument doc = super.buildDocument(context, indexableObject);
|
||||
final MetadataField metadataField = indexableObject.getIndexedObject();
|
||||
// add schema, element, qualifier and full fieldName
|
||||
addFacetIndex(doc, SCHEMA_FIELD_NAME, metadataField.getMetadataSchema().getName(),
|
||||
metadataField.getMetadataSchema().getName());
|
||||
addFacetIndex(doc, ELEMENT_FIELD_NAME, metadataField.getElement(), metadataField.getElement());
|
||||
String fieldName = metadataField.toString().replace('_', '.');
|
||||
addFacetIndex(doc, FIELD_NAME_VARIATIONS, fieldName, fieldName);
|
||||
if (StringUtils.isNotBlank(metadataField.getQualifier())) {
|
||||
addFacetIndex(doc, QUALIFIER_FIELD_NAME, metadataField.getQualifier(), metadataField.getQualifier());
|
||||
addFacetIndex(doc, FIELD_NAME_VARIATIONS, fieldName,
|
||||
metadataField.getElement() + "." + metadataField.getQualifier());
|
||||
addFacetIndex(doc, FIELD_NAME_VARIATIONS, metadataField.getQualifier(), metadataField.getQualifier());
|
||||
} else {
|
||||
addFacetIndex(doc, FIELD_NAME_VARIATIONS, metadataField.getElement(), metadataField.getElement());
|
||||
}
|
||||
addNamedResourceTypeIndex(doc, indexableObject.getTypeText());
|
||||
Group anonymousGroup = groupService.findByName(context, Group.ANONYMOUS);
|
||||
// add read permission on doc for anonymous group
|
||||
doc.addField("read", "g" + anonymousGroup.getID());
|
||||
return doc;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private MetadataFieldService metadataFieldService;
|
||||
|
||||
@Override
|
||||
public Iterator<IndexableMetadataField> findAll(Context context) throws SQLException {
|
||||
final Iterator<MetadataField> metadataFields = metadataFieldService.findAll(context).iterator();
|
||||
return new Iterator<>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return metadataFields.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexableMetadataField next() {
|
||||
return new IndexableMetadataField(metadataFields.next());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return IndexableMetadataField.TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IndexableMetadataField> findIndexableObject(Context context, String id) throws SQLException {
|
||||
final MetadataField metadataField = metadataFieldService.find(context, Integer.parseInt(id));
|
||||
return metadataField == null ? Optional.empty() : Optional.of(new IndexableMetadataField(metadataField));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Object object) {
|
||||
return object instanceof MetadataField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getIndexableObjects(Context context, MetadataField object) {
|
||||
return Arrays.asList(new IndexableMetadataField(object));
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.discovery.indexobject.factory;
|
||||
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.discovery.indexobject.IndexableMetadataField;
|
||||
|
||||
/**
|
||||
* Factory interface for indexing/retrieving {@link org.dspace.content.MetadataField} items in the search core
|
||||
*
|
||||
* @author Maria Verdonck (Atmire) on 14/07/2020
|
||||
*/
|
||||
public interface MetadataFieldIndexFactory extends IndexFactory<IndexableMetadataField, MetadataField> {
|
||||
}
|
@@ -141,7 +141,7 @@ public class EPerson extends DSpaceObject implements DSpaceObjectLegacySupport {
|
||||
return false;
|
||||
}
|
||||
final EPerson other = (EPerson) obj;
|
||||
if (this.getID() != other.getID()) {
|
||||
if (!this.getID().equals(other.getID())) {
|
||||
return false;
|
||||
}
|
||||
if (!StringUtils.equals(this.getEmail(), other.getEmail())) {
|
||||
|
@@ -7,8 +7,11 @@
|
||||
*/
|
||||
package org.dspace.eperson;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
@@ -196,7 +199,6 @@ public class EPersonCLITool {
|
||||
|
||||
try {
|
||||
ePersonService.update(context, eperson);
|
||||
context.complete();
|
||||
System.out.printf("Created EPerson %s\n", eperson.getID().toString());
|
||||
} catch (SQLException ex) {
|
||||
context.abort();
|
||||
@@ -259,16 +261,26 @@ public class EPersonCLITool {
|
||||
}
|
||||
|
||||
try {
|
||||
List<String> tableList = ePersonService.getDeleteConstraints(context, eperson);
|
||||
if (!tableList.isEmpty()) {
|
||||
System.out.printf("The EPerson with ID: %s is referenced by the following database tables:%n",
|
||||
eperson.getID().toString());
|
||||
tableList.forEach((s) -> {
|
||||
System.out.println(s);
|
||||
});
|
||||
}
|
||||
System.out.printf("Are you sure you want to delete this EPerson with ID: %s? (y or n): ",
|
||||
eperson.getID().toString());
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
|
||||
System.out.flush();
|
||||
String s = input.readLine();
|
||||
if (s != null && s.trim().toLowerCase().startsWith("y")) {
|
||||
ePersonService.delete(context, eperson);
|
||||
context.complete();
|
||||
System.out.printf("Deleted EPerson %s\n", eperson.getID().toString());
|
||||
} catch (SQLException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
return 1;
|
||||
} catch (AuthorizeException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
return 1;
|
||||
} catch (IOException ex) {
|
||||
System.out.printf("%nDeleted EPerson with ID: %s", eperson.getID().toString());
|
||||
} else {
|
||||
System.out.printf("%nAbort Deletion of EPerson with ID: %s %n", eperson.getID().toString());
|
||||
}
|
||||
} catch (SQLException | AuthorizeException | IOException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
return 1;
|
||||
}
|
||||
@@ -373,7 +385,6 @@ public class EPersonCLITool {
|
||||
if (modified) {
|
||||
try {
|
||||
ePersonService.update(context, eperson);
|
||||
context.complete();
|
||||
System.out.printf("Modified EPerson %s\n", eperson.getID().toString());
|
||||
} catch (SQLException ex) {
|
||||
context.abort();
|
||||
|
@@ -9,6 +9,8 @@ package org.dspace.eperson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
/**
|
||||
* Exception indicating that an EPerson may not be deleted due to the presence
|
||||
* of the EPerson's ID in certain tables
|
||||
@@ -33,7 +35,10 @@ public class EPersonDeletionException extends Exception {
|
||||
* deleted if it exists in these tables.
|
||||
*/
|
||||
public EPersonDeletionException(List<String> tableList) {
|
||||
super();
|
||||
// this may not be the most beautiful way to print the tablenames as part or the error message.
|
||||
// but it has to be a one liner, as the super() call must be the first statement in the constructor.
|
||||
super("Cannot delete EPerson as it is referenced by the following database tables: "
|
||||
+ ArrayUtils.toString(tableList.toArray()));
|
||||
myTableList = tableList;
|
||||
}
|
||||
|
||||
|
@@ -7,10 +7,13 @@
|
||||
*/
|
||||
package org.dspace.eperson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -21,26 +24,56 @@ import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.authorize.service.ResourcePolicyService;
|
||||
import org.dspace.content.DSpaceObjectServiceImpl;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.WorkspaceItem;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.WorkspaceItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogManager;
|
||||
import org.dspace.core.Utils;
|
||||
import org.dspace.eperson.dao.EPersonDAO;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.eperson.service.SubscribeService;
|
||||
import org.dspace.event.Event;
|
||||
import org.dspace.versioning.Version;
|
||||
import org.dspace.versioning.VersionHistory;
|
||||
import org.dspace.versioning.dao.VersionDAO;
|
||||
import org.dspace.versioning.factory.VersionServiceFactory;
|
||||
import org.dspace.versioning.service.VersionHistoryService;
|
||||
import org.dspace.versioning.service.VersioningService;
|
||||
import org.dspace.workflow.WorkflowService;
|
||||
import org.dspace.workflow.factory.WorkflowServiceFactory;
|
||||
import org.dspace.workflowbasic.BasicWorkflowItem;
|
||||
import org.dspace.workflowbasic.BasicWorkflowServiceImpl;
|
||||
import org.dspace.workflowbasic.factory.BasicWorkflowServiceFactory;
|
||||
import org.dspace.workflowbasic.service.BasicWorkflowItemService;
|
||||
import org.dspace.workflowbasic.service.BasicWorkflowService;
|
||||
import org.dspace.workflowbasic.service.TaskListItemService;
|
||||
import org.dspace.xmlworkflow.WorkflowConfigurationException;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory;
|
||||
import org.dspace.xmlworkflow.service.WorkflowRequirementsService;
|
||||
import org.dspace.xmlworkflow.service.XmlWorkflowService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
|
||||
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.ClaimedTaskService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.PoolTaskService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.WorkflowItemRoleService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.XmlWorkflowItemService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Service implementation for the EPerson object.
|
||||
* This class is responsible for all business logic calls for the EPerson object and is autowired by spring.
|
||||
* Service implementation for the EPerson object. This class is responsible for
|
||||
* all business logic calls for the EPerson object and is autowired by spring.
|
||||
* This class should never be accessed directly.
|
||||
*
|
||||
* @author kevinvandevelde at atmire.com
|
||||
@@ -60,7 +93,17 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
||||
@Autowired(required = true)
|
||||
protected ItemService itemService;
|
||||
@Autowired(required = true)
|
||||
protected WorkflowItemRoleService workflowItemRoleService;
|
||||
@Autowired(required = true)
|
||||
CollectionRoleService collectionRoleService;
|
||||
@Autowired(required = true)
|
||||
protected GroupService groupService;
|
||||
@Autowired(required = true)
|
||||
protected SubscribeService subscribeService;
|
||||
@Autowired(required = true)
|
||||
protected VersionDAO versionDAO;
|
||||
@Autowired(required = true)
|
||||
protected ClaimedTaskService claimedTaskService;
|
||||
|
||||
protected EPersonServiceImpl() {
|
||||
super();
|
||||
@@ -196,28 +239,185 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
||||
|
||||
@Override
|
||||
public void delete(Context context, EPerson ePerson) throws SQLException, AuthorizeException {
|
||||
try {
|
||||
delete(context, ePerson, true);
|
||||
} catch (AuthorizeException ex) {
|
||||
log.error("This AuthorizeException: " + ex + " occured while deleting Eperson with the ID: " +
|
||||
ePerson.getID());
|
||||
throw new AuthorizeException(ex);
|
||||
} catch (IOException ex) {
|
||||
log.error("This IOException: " + ex + " occured while deleting Eperson with the ID: " + ePerson.getID());
|
||||
throw new AuthorizeException(ex);
|
||||
} catch (EPersonDeletionException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an EPerson. The argument cascade defines whether all references
|
||||
* on an EPerson should be deleted as well (by either deleting the
|
||||
* referencing object - e.g. WorkspaceItem, ResourcePolicy - or by setting
|
||||
* the foreign key null - e.g. archived Items). If cascade is set to false
|
||||
* and the EPerson is referenced somewhere, this leads to an
|
||||
* AuthorizeException. EPersons may be referenced by Items, ResourcePolicies
|
||||
* and workflow tasks.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param ePerson The EPerson to delete.
|
||||
* @param cascade Whether to delete references on the EPerson (cascade =
|
||||
* true) or to abort the deletion (cascade = false) if the EPerson is
|
||||
* referenced within DSpace.
|
||||
*
|
||||
* @throws SQLException
|
||||
* @throws AuthorizeException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void delete(Context context, EPerson ePerson, boolean cascade)
|
||||
throws SQLException, AuthorizeException, IOException, EPersonDeletionException {
|
||||
// authorized?
|
||||
if (!authorizeService.isAdmin(context)) {
|
||||
throw new AuthorizeException(
|
||||
"You must be an admin to delete an EPerson");
|
||||
}
|
||||
|
||||
Set<Group> workFlowGroups = getAllWorkFlowGroups(context, ePerson);
|
||||
for (Group group: workFlowGroups) {
|
||||
List<EPerson> ePeople = groupService.allMembers(context, group);
|
||||
if (ePeople.size() == 1 && ePeople.contains(ePerson)) {
|
||||
throw new IllegalStateException(
|
||||
"Refused to delete user " + ePerson.getID() + " because it the only member of the workflow group"
|
||||
+ group.getID() + ". Delete the tasks and group first if you want to remove this user.");
|
||||
}
|
||||
}
|
||||
// check for presence of eperson in tables that
|
||||
// have constraints on eperson_id
|
||||
List<String> constraintList = getDeleteConstraints(context, ePerson);
|
||||
|
||||
// if eperson exists in tables that have constraints
|
||||
// on eperson, throw an exception
|
||||
if (constraintList.size() > 0) {
|
||||
throw new AuthorizeException(new EPersonDeletionException(constraintList));
|
||||
}
|
||||
// Check if the constraints we found should be deleted
|
||||
if (cascade) {
|
||||
boolean isBasicFramework = WorkflowServiceFactory.getInstance().getWorkflowService()
|
||||
instanceof BasicWorkflowService;
|
||||
boolean isXmlFramework = WorkflowServiceFactory.getInstance().getWorkflowService()
|
||||
instanceof XmlWorkflowService;
|
||||
Iterator<String> constraintsIterator = constraintList.iterator();
|
||||
|
||||
while (constraintsIterator.hasNext()) {
|
||||
String tableName = constraintsIterator.next();
|
||||
if (StringUtils.equals(tableName, "item") || StringUtils.equals(tableName, "workspaceitem")) {
|
||||
Iterator<Item> itemIterator = itemService.findBySubmitter(context, ePerson, true);
|
||||
|
||||
VersionHistoryService versionHistoryService = VersionServiceFactory.getInstance()
|
||||
.getVersionHistoryService();
|
||||
VersioningService versioningService = VersionServiceFactory.getInstance().getVersionService();
|
||||
|
||||
while (itemIterator.hasNext()) {
|
||||
Item item = itemIterator.next();
|
||||
|
||||
VersionHistory versionHistory = versionHistoryService.findByItem(context, item);
|
||||
if (null != versionHistory) {
|
||||
for (Version version : versioningService.getVersionsByHistory(context,
|
||||
versionHistory)) {
|
||||
version.setePerson(null);
|
||||
versionDAO.save(context, version);
|
||||
}
|
||||
}
|
||||
WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance()
|
||||
.getWorkspaceItemService();
|
||||
WorkspaceItem wsi = workspaceItemService.findByItem(context, item);
|
||||
|
||||
if (null != wsi) {
|
||||
workspaceItemService.deleteAll(context, wsi);
|
||||
} else {
|
||||
// we can do that as dc.provenance still contains
|
||||
// information about who submitted and who
|
||||
// archived an item.
|
||||
item.setSubmitter(null);
|
||||
itemService.update(context, item);
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.equals(tableName, "cwf_claimtask") && isXmlFramework) {
|
||||
// Unclaim all XmlWorkflow tasks
|
||||
XmlWorkflowItemService xmlWorkflowItemService = XmlWorkflowServiceFactory
|
||||
.getInstance().getXmlWorkflowItemService();
|
||||
ClaimedTaskService claimedTaskService = XmlWorkflowServiceFactory
|
||||
.getInstance().getClaimedTaskService();
|
||||
XmlWorkflowService xmlWorkflowService = XmlWorkflowServiceFactory
|
||||
.getInstance().getXmlWorkflowService();
|
||||
WorkflowRequirementsService workflowRequirementsService = XmlWorkflowServiceFactory
|
||||
.getInstance().getWorkflowRequirementsService();
|
||||
|
||||
List<XmlWorkflowItem> xmlWorkflowItems = xmlWorkflowItemService
|
||||
.findBySubmitter(context, ePerson);
|
||||
List<ClaimedTask> claimedTasks = claimedTaskService.findByEperson(context, ePerson);
|
||||
|
||||
for (ClaimedTask task : claimedTasks) {
|
||||
xmlWorkflowService.deleteClaimedTask(context, task.getWorkflowItem(), task);
|
||||
|
||||
try {
|
||||
workflowRequirementsService.removeClaimedUser(context, task.getWorkflowItem(),
|
||||
ePerson, task.getStepID());
|
||||
} catch (WorkflowConfigurationException ex) {
|
||||
log.error("This WorkflowConfigurationException: " + ex +
|
||||
" occured while deleting Eperson with the ID: " + ePerson.getID());
|
||||
throw new AuthorizeException(new EPersonDeletionException(Collections
|
||||
.singletonList(tableName)));
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.equals(tableName, "workflowitem") && isBasicFramework) {
|
||||
// Remove basicWorkflow workflowitem and unclaim them
|
||||
BasicWorkflowItemService basicWorkflowItemService = BasicWorkflowServiceFactory.getInstance()
|
||||
.getBasicWorkflowItemService();
|
||||
BasicWorkflowService basicWorkflowService = BasicWorkflowServiceFactory.getInstance()
|
||||
.getBasicWorkflowService();
|
||||
TaskListItemService taskListItemService = BasicWorkflowServiceFactory.getInstance()
|
||||
.getTaskListItemService();
|
||||
List<BasicWorkflowItem> workflowItems = basicWorkflowItemService.findByOwner(context, ePerson);
|
||||
for (BasicWorkflowItem workflowItem : workflowItems) {
|
||||
int state = workflowItem.getState();
|
||||
// unclaim tasks that are in the pool.
|
||||
if (state == BasicWorkflowServiceImpl.WFSTATE_STEP1
|
||||
|| state == BasicWorkflowServiceImpl.WFSTATE_STEP2
|
||||
|| state == BasicWorkflowServiceImpl.WFSTATE_STEP3) {
|
||||
log.info(LogManager.getHeader(context, "unclaim_workflow",
|
||||
"workflow_id=" + workflowItem.getID() + ", claiming EPerson is deleted"));
|
||||
basicWorkflowService.unclaim(context, workflowItem, context.getCurrentUser());
|
||||
// remove the EPerson from the list of persons that can (re-)claim the task
|
||||
// while we are doing it below, we must do this here as well as the previously
|
||||
// unclaimed tasks was put back into pool and we do not know the order the tables
|
||||
// are checked.
|
||||
taskListItemService.deleteByWorkflowItemAndEPerson(context, workflowItem, ePerson);
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.equals(tableName, "resourcepolicy")) {
|
||||
// we delete the EPerson, it won't need any rights anymore.
|
||||
authorizeService.removeAllEPersonPolicies(context, ePerson);
|
||||
} else if (StringUtils.equals(tableName, "tasklistitem") && isBasicFramework) {
|
||||
// remove EPerson from the list of EPersons that may claim some specific workflow tasks.
|
||||
TaskListItemService taskListItemService = BasicWorkflowServiceFactory.getInstance()
|
||||
.getTaskListItemService();
|
||||
taskListItemService.deleteByEPerson(context, ePerson);
|
||||
} else if (StringUtils.equals(tableName, "cwf_pooltask") && isXmlFramework) {
|
||||
PoolTaskService poolTaskService = XmlWorkflowServiceFactory.getInstance().getPoolTaskService();
|
||||
poolTaskService.deleteByEperson(context, ePerson);
|
||||
} else if (StringUtils.equals(tableName, "cwf_workflowitemrole") && isXmlFramework) {
|
||||
WorkflowItemRoleService workflowItemRoleService = XmlWorkflowServiceFactory.getInstance()
|
||||
.getWorkflowItemRoleService();
|
||||
workflowItemRoleService.deleteByEPerson(context, ePerson);
|
||||
} else {
|
||||
log.warn("EPerson is referenced in table '" + tableName
|
||||
+ "'. Deletion of EPerson " + ePerson.getID() + " may fail "
|
||||
+ "if the database does not handle this "
|
||||
+ "reference.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new EPersonDeletionException(constraintList);
|
||||
}
|
||||
}
|
||||
context.addEvent(new Event(Event.DELETE, Constants.EPERSON, ePerson.getID(), ePerson.getEmail(),
|
||||
getIdentifiers(context, ePerson)));
|
||||
|
||||
// XXX FIXME: This sidesteps the object model code so it won't
|
||||
// generate REMOVE events on the affected Groups.
|
||||
|
||||
// Remove any group memberships first
|
||||
// Remove any group memberships first
|
||||
Iterator<Group> groups = ePerson.getGroups().iterator();
|
||||
@@ -237,6 +437,19 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
||||
"eperson_id=" + ePerson.getID()));
|
||||
}
|
||||
|
||||
private Set<Group> getAllWorkFlowGroups(Context context, EPerson ePerson) throws SQLException {
|
||||
Set<Group> workFlowGroups = new HashSet<>();
|
||||
|
||||
Set<Group> groups = groupService.allMemberGroupsSet(context, ePerson);
|
||||
for (Group group: groups) {
|
||||
List<CollectionRole> collectionRoles = collectionRoleService.findByGroup(context, group);
|
||||
if (!collectionRoles.isEmpty()) {
|
||||
workFlowGroups.add(group);
|
||||
}
|
||||
}
|
||||
return workFlowGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSupportsTypeConstant() {
|
||||
return Constants.EPERSON;
|
||||
@@ -339,11 +552,22 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
||||
List<String> tableList = new ArrayList<String>();
|
||||
|
||||
// check for eperson in item table
|
||||
Iterator<Item> itemsBySubmitter = itemService.findBySubmitter(context, ePerson);
|
||||
Iterator<Item> itemsBySubmitter = itemService.findBySubmitter(context, ePerson, true);
|
||||
if (itemsBySubmitter.hasNext()) {
|
||||
tableList.add("item");
|
||||
}
|
||||
|
||||
WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
|
||||
List<WorkspaceItem> workspaceBySubmitter = workspaceItemService.findByEPerson(context, ePerson);
|
||||
if (workspaceBySubmitter.size() > 0) {
|
||||
tableList.add("workspaceitem");
|
||||
}
|
||||
|
||||
ResourcePolicyService resourcePolicyService = AuthorizeServiceFactory.getInstance().getResourcePolicyService();
|
||||
if (resourcePolicyService.find(context, ePerson).size() > 0) {
|
||||
tableList.add("resourcepolicy");
|
||||
}
|
||||
|
||||
WorkflowService workflowService = WorkflowServiceFactory.getInstance().getWorkflowService();
|
||||
List<String> workflowConstraints = workflowService.getEPersonDeleteConstraints(context, ePerson);
|
||||
tableList.addAll(workflowConstraints);
|
||||
|
@@ -42,8 +42,15 @@ import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.event.Event;
|
||||
import org.dspace.util.UUIDUtils;
|
||||
import org.dspace.xmlworkflow.Role;
|
||||
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
|
||||
import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
|
||||
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.ClaimedTaskService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService;
|
||||
import org.dspace.xmlworkflow.storedcomponents.service.PoolTaskService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -81,6 +88,13 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
|
||||
@Autowired(required = true)
|
||||
protected ResourcePolicyService resourcePolicyService;
|
||||
|
||||
@Autowired(required = true)
|
||||
protected PoolTaskService poolTaskService;
|
||||
@Autowired(required = true)
|
||||
protected ClaimedTaskService claimedTaskService;
|
||||
@Autowired(required = true)
|
||||
protected XmlWorkflowFactory workflowFactory;
|
||||
|
||||
protected GroupServiceImpl() {
|
||||
super();
|
||||
}
|
||||
@@ -143,8 +157,48 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
|
||||
groupChild.getName(), getIdentifiers(context, groupParent)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a member of a group.
|
||||
* The removal will be refused if the group is linked to a workflow step which has claimed tasks or pool tasks
|
||||
* and no other member is present in the group to handle these.
|
||||
* @param context DSpace context object
|
||||
* @param group DSpace group
|
||||
* @param ePerson eperson
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Override
|
||||
public void removeMember(Context context, Group group, EPerson ePerson) {
|
||||
public void removeMember(Context context, Group group, EPerson ePerson) throws SQLException {
|
||||
List<CollectionRole> collectionRoles = collectionRoleService.findByGroup(context, group);
|
||||
if (!collectionRoles.isEmpty()) {
|
||||
List<PoolTask> poolTasks = poolTaskService.findByGroup(context, group);
|
||||
List<ClaimedTask> claimedTasks = claimedTaskService.findByEperson(context, ePerson);
|
||||
for (ClaimedTask claimedTask : claimedTasks) {
|
||||
Step stepByName = workflowFactory.getStepByName(claimedTask.getStepID());
|
||||
Role role = stepByName.getRole();
|
||||
for (CollectionRole collectionRole : collectionRoles) {
|
||||
if (StringUtils.equals(collectionRole.getRoleId(), role.getId())
|
||||
&& claimedTask.getWorkflowItem().getCollection() == collectionRole.getCollection()) {
|
||||
List<EPerson> ePeople = allMembers(context, group);
|
||||
if (ePeople.size() == 1 && ePeople.contains(ePerson)) {
|
||||
throw new IllegalStateException(
|
||||
"Refused to remove user " + ePerson
|
||||
.getID() + " from workflow group because the group " + group
|
||||
.getID() + " has tasks assigned and no other members");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!poolTasks.isEmpty()) {
|
||||
List<EPerson> ePeople = allMembers(context, group);
|
||||
if (ePeople.size() == 1 && ePeople.contains(ePerson)) {
|
||||
throw new IllegalStateException(
|
||||
"Refused to remove user " + ePerson
|
||||
.getID() + " from workflow group because the group " + group
|
||||
.getID() + " has tasks assigned and no other members");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (group.remove(ePerson)) {
|
||||
context.addEvent(new Event(Event.REMOVE, Constants.GROUP, group.getID(), Constants.EPERSON, ePerson.getID(),
|
||||
ePerson.getEmail(), getIdentifiers(context, group)));
|
||||
@@ -153,6 +207,20 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
|
||||
|
||||
@Override
|
||||
public void removeMember(Context context, Group groupParent, Group childGroup) throws SQLException {
|
||||
List<CollectionRole> collectionRoles = collectionRoleService.findByGroup(context, groupParent);
|
||||
if (!collectionRoles.isEmpty()) {
|
||||
List<PoolTask> poolTasks = poolTaskService.findByGroup(context, groupParent);
|
||||
if (!poolTasks.isEmpty()) {
|
||||
List<EPerson> parentPeople = allMembers(context, groupParent);
|
||||
List<EPerson> childPeople = allMembers(context, childGroup);
|
||||
if (childPeople.containsAll(parentPeople)) {
|
||||
throw new IllegalStateException(
|
||||
"Refused to remove sub group " + childGroup
|
||||
.getID() + " from workflow group because the group " + groupParent
|
||||
.getID() + " has tasks assigned and no other members");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (groupParent.remove(childGroup)) {
|
||||
childGroup.removeParentGroup(groupParent);
|
||||
context.addEvent(
|
||||
|
@@ -76,7 +76,7 @@ public interface GroupService extends DSpaceObjectService<Group>, DSpaceObjectLe
|
||||
* @param group DSpace group
|
||||
* @param ePerson eperson
|
||||
*/
|
||||
public void removeMember(Context context, Group group, EPerson ePerson);
|
||||
public void removeMember(Context context, Group group, EPerson ePerson) throws SQLException;
|
||||
|
||||
|
||||
/**
|
||||
|
162
dspace-api/src/main/java/org/dspace/external/provider/impl/LiveImportDataProvider.java
vendored
Normal file
162
dspace-api/src/main/java/org/dspace/external/provider/impl/LiveImportDataProvider.java
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* 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.external.provider.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.dspace.external.provider.ExternalDataProvider;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.exception.MetadataSourceException;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.dspace.importer.external.service.components.QuerySource;
|
||||
|
||||
/**
|
||||
* This class allows to configure a Live Import Provider as an External Data Provider
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*
|
||||
*/
|
||||
public class LiveImportDataProvider implements ExternalDataProvider {
|
||||
/**
|
||||
* The {@link QuerySource} live import provider
|
||||
*/
|
||||
private QuerySource querySource;
|
||||
|
||||
/**
|
||||
* An unique human readable identifier for this provider
|
||||
*/
|
||||
private String sourceIdentifier;
|
||||
|
||||
private String recordIdMetadata;
|
||||
|
||||
private String displayMetadata = "dc.title";
|
||||
|
||||
@Override
|
||||
public String getSourceIdentifier() {
|
||||
return sourceIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method set the SourceIdentifier for the ExternalDataProvider
|
||||
* @param sourceIdentifier The UNIQUE sourceIdentifier to be set on any LiveImport data provider
|
||||
*/
|
||||
public void setSourceIdentifier(String sourceIdentifier) {
|
||||
this.sourceIdentifier = sourceIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method set the MetadataSource for the ExternalDataProvider
|
||||
* @param metadataSource {@link org.dspace.importer.external.service.components.MetadataSource} implementation used to process the input data
|
||||
*/
|
||||
public void setMetadataSource(QuerySource querySource) {
|
||||
this.querySource = querySource;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method set dublin core identifier to use as metadata id
|
||||
* @param recordIdMetadata dublin core identifier to use as metadata id
|
||||
*/
|
||||
public void setRecordIdMetadata(String recordIdMetadata) {
|
||||
this.recordIdMetadata = recordIdMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method set the dublin core identifier to display the title
|
||||
* @param displayMetadata metadata to use as title
|
||||
*/
|
||||
public void setDisplayMetadata(String displayMetadata) {
|
||||
this.displayMetadata = displayMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ExternalDataObject> getExternalDataObject(String id) {
|
||||
try {
|
||||
ExternalDataObject externalDataObject = getExternalDataObject(querySource.getRecord(id));
|
||||
return Optional.of(externalDataObject);
|
||||
} catch (MetadataSourceException e) {
|
||||
throw new RuntimeException(
|
||||
"The live import provider " + querySource.getImportSource() + " throws an exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit) {
|
||||
Collection<ImportRecord> records;
|
||||
try {
|
||||
records = querySource.getRecords(query, start, limit);
|
||||
return records.stream().map(r -> getExternalDataObject(r)).collect(Collectors.toList());
|
||||
} catch (MetadataSourceException e) {
|
||||
throw new RuntimeException(
|
||||
"The live import provider " + querySource.getImportSource() + " throws an exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(String source) {
|
||||
return StringUtils.equalsIgnoreCase(sourceIdentifier, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfResults(String query) {
|
||||
try {
|
||||
return querySource.getRecordsCount(query);
|
||||
} catch (MetadataSourceException e) {
|
||||
throw new RuntimeException(
|
||||
"The live import provider " + querySource.getImportSource() + " throws an exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to convert an ImportRecord to an ExternalDataObject
|
||||
*
|
||||
* FIXME it would be useful to remove ImportRecord at all in favor of the
|
||||
* ExternalDataObject
|
||||
*
|
||||
* @param record
|
||||
* @return
|
||||
*/
|
||||
private ExternalDataObject getExternalDataObject(ImportRecord record) {
|
||||
//return 400 if no record were found
|
||||
if (record == null) {
|
||||
throw new IllegalArgumentException("No record found for query or id");
|
||||
}
|
||||
ExternalDataObject externalDataObject = new ExternalDataObject(sourceIdentifier);
|
||||
String id = getFirstValue(record, recordIdMetadata);
|
||||
String display = getFirstValue(record, displayMetadata);
|
||||
externalDataObject.setId(id);
|
||||
externalDataObject.setDisplayValue(display);
|
||||
externalDataObject.setValue(display);
|
||||
for (MetadatumDTO dto : record.getValueList()) {
|
||||
// FIXME it would be useful to remove MetadatumDTO in favor of MetadataValueDTO
|
||||
MetadataValueDTO mvDTO = new MetadataValueDTO();
|
||||
mvDTO.setSchema(dto.getSchema());
|
||||
mvDTO.setElement(dto.getElement());
|
||||
mvDTO.setQualifier(dto.getQualifier());
|
||||
mvDTO.setValue(dto.getValue());
|
||||
externalDataObject.addMetadata(mvDTO);
|
||||
}
|
||||
return externalDataObject;
|
||||
}
|
||||
|
||||
private String getFirstValue(ImportRecord record, String metadata) {
|
||||
String id = null;
|
||||
String[] split = StringUtils.split(metadata, ".", 3);
|
||||
Collection<MetadatumDTO> values = record.getValue(split[0], split[1], split.length == 3 ? split[2] : null);
|
||||
if (!values.isEmpty()) {
|
||||
id = (values.iterator().next().getValue());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
@@ -761,9 +761,9 @@ public class DOIIdentifierProvider
|
||||
Item item = (Item) dso;
|
||||
|
||||
List<MetadataValue> metadata = itemService.getMetadata(item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
|
||||
String leftPart = DOI.RESOLVER + SLASH + getPrefix() + SLASH + getNamespaceSeparator();
|
||||
for (MetadataValue id : metadata) {
|
||||
if (id.getValue().startsWith(
|
||||
DOI.RESOLVER + String.valueOf(SLASH) + PREFIX + String.valueOf(SLASH) + NAMESPACE_SEPARATOR)) {
|
||||
if (id.getValue().startsWith(leftPart)) {
|
||||
return doiService.DOIFromExternalFormat(id.getValue());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.arxiv.metadatamapping;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping;
|
||||
|
||||
/**
|
||||
* An implementation of {@link AbstractMetadataFieldMapping}
|
||||
* Responsible for defining the mapping of the ArXiv metadatum fields on the DSpace metadatum fields
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*/
|
||||
public class ArXivFieldMapping extends AbstractMetadataFieldMapping {
|
||||
|
||||
/**
|
||||
* Defines which metadatum is mapped on which metadatum. Note that while the key must be unique it
|
||||
* only matters here for postprocessing of the value. The mapped MetadatumContributor has full control over
|
||||
* what metadatafield is generated.
|
||||
*
|
||||
* @param metadataFieldMap The map containing the link between retrieve metadata and metadata that will be set to
|
||||
* the item.
|
||||
*/
|
||||
@Override
|
||||
@Resource(name = "arxivMetadataFieldMap")
|
||||
public void setMetadataFieldMap(Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -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.importer.external.arxiv.metadatamapping.contributor;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.axiom.om.OMElement;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.dspace.importer.external.metadatamapping.contributor.MetadataContributor;
|
||||
import org.dspace.importer.external.metadatamapping.contributor.SimpleXpathMetadatumContributor;
|
||||
|
||||
/**
|
||||
* Arxiv specific implementation of {@link MetadataContributor}
|
||||
* Responsible for generating the ArXiv Id from the retrieved item.
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class ArXivIdMetadataContributor extends SimpleXpathMetadatumContributor {
|
||||
|
||||
/**
|
||||
* Retrieve the metadata associated with the given object.
|
||||
* Depending on the retrieved node (using the query), different types of values will be added to the MetadatumDTO
|
||||
* list
|
||||
*
|
||||
* @param t A class to retrieve metadata from.
|
||||
* @return a collection of import records. Only the identifier of the found records may be put in the record.
|
||||
*/
|
||||
@Override
|
||||
public Collection<MetadatumDTO> contributeMetadata(OMElement t) {
|
||||
Collection<MetadatumDTO> values = super.contributeMetadata(t);
|
||||
parseValue(values);
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArXiv returns a full URL as in the <id> value, e.g. http://arxiv.org/abs/1911.11405v1.
|
||||
* This method parses out the identifier from the end of the URL, e.g. 1911.11405v1.
|
||||
*
|
||||
* @param dtos Metadata which contains the items uri
|
||||
*/
|
||||
private void parseValue(Collection<MetadatumDTO> dtos) {
|
||||
if (dtos != null) {
|
||||
for (MetadatumDTO dto : dtos) {
|
||||
if (dto != null && dto.getValue() != null && dto.getValue().contains("/")) {
|
||||
int startIndex = dto.getValue().lastIndexOf('/') + 1;
|
||||
int endIndex = dto.getValue().length();
|
||||
String id = dto.getValue().substring(startIndex, endIndex);
|
||||
dto.setValue(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.arxiv.service;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.el.MethodNotFoundException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.axiom.om.OMElement;
|
||||
import org.apache.axiom.om.OMXMLBuilderFactory;
|
||||
import org.apache.axiom.om.OMXMLParserWrapper;
|
||||
import org.apache.axiom.om.xpath.AXIOMXPath;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.datamodel.Query;
|
||||
import org.dspace.importer.external.exception.MetadataSourceException;
|
||||
import org.dspace.importer.external.service.AbstractImportMetadataSourceService;
|
||||
import org.dspace.importer.external.service.components.QuerySource;
|
||||
import org.jaxen.JaxenException;
|
||||
|
||||
/**
|
||||
* Implements a data source for querying ArXiv
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4Science dot it)
|
||||
*
|
||||
*/
|
||||
public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<OMElement>
|
||||
implements QuerySource {
|
||||
|
||||
private WebTarget webTarget;
|
||||
private String baseAddress;
|
||||
|
||||
/**
|
||||
* Find the number of records matching the query string in ArXiv. Supports pagination.
|
||||
*
|
||||
* @param query a query string to base the search on.
|
||||
* @param start offset to start at
|
||||
* @param count number of records to retrieve.
|
||||
* @return a set of records. Fully transformed.
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(String query, int start, int count) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query, count, start));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find records based on a object query and convert them to a list metadata mapped in ImportRecord.
|
||||
* The entry with the key "query" of the Query's map will be used as query string value.
|
||||
*
|
||||
* @see org.dspace.importer.external.datamodel.Query
|
||||
* @see org.dspace.importer.external.datamodel.ImportRecord
|
||||
* @param query a query object to base the search on.
|
||||
* @return a set of records. Fully transformed.
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the number of records matching the query string in ArXiv;
|
||||
*
|
||||
* @param query a query object to base the search on.
|
||||
* @return the sum of the matching records over this import source
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public int getRecordsCount(String query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the number of records matching a query;
|
||||
* The entry with the key "query" of the Query's map will be used to get the query string.
|
||||
*
|
||||
* @see org.dspace.importer.external.datamodel.Query
|
||||
* @param query a query string to base the search on.
|
||||
* @return the sum of the matching records over this import source
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public int getRecordsCount(Query query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single record of metadata from the arxiv by ArXiv ID.
|
||||
*
|
||||
* @param id id of the record in ArXiv
|
||||
* @return the first matching record
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(String id) throws MetadataSourceException {
|
||||
List<ImportRecord> records = retry(new SearchByIdCallable(id));
|
||||
return records == null || records.isEmpty() ? null : records.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single record from the ArXiv matching the query.
|
||||
* Field "query" will be used to get data from.
|
||||
*
|
||||
* @see org.dspace.importer.external.datamodel.Query
|
||||
* @param query a query matching a single record
|
||||
* @return the first matching record
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public ImportRecord getRecord(Query query) throws MetadataSourceException {
|
||||
List<ImportRecord> records = retry(new SearchByIdCallable(query));
|
||||
return records == null || records.isEmpty() ? null : records.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the class
|
||||
*
|
||||
* @throws Exception on generic exception
|
||||
*/
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
Client client = ClientBuilder.newClient();
|
||||
webTarget = client.target(baseAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* The string that identifies this import implementation. Preferable a URI
|
||||
*
|
||||
* @return the identifying uri
|
||||
*/
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "arxiv";
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect this method will be not used and erased from the interface soon
|
||||
*/
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
|
||||
// FIXME: we need this method?
|
||||
throw new MethodNotFoundException("This method is not implemented for ArXiv");
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds records based on query object.
|
||||
* Supports search by title and/or author
|
||||
*
|
||||
* @param query a query object to base the search on.
|
||||
* @return a collection of import records.
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
|
||||
return retry(new FindMatchingRecordCallable(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to count the number of entries for an ArXiv
|
||||
* query.
|
||||
* This Callable use as query value to ArXiv the string queryString passed to constructor.
|
||||
* If the object will be construct through Query.class instance, the value of the Query's
|
||||
* map with the key "query" will be used.
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
private class CountByQueryCallable implements Callable<Integer> {
|
||||
private Query query;
|
||||
|
||||
|
||||
private CountByQueryCallable(String queryString) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
}
|
||||
|
||||
private CountByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
String queryString = query.getParameterAsClass("query", String.class);
|
||||
Integer start = query.getParameterAsClass("start", Integer.class);
|
||||
Integer maxResult = query.getParameterAsClass("count", Integer.class);
|
||||
WebTarget local = webTarget.queryParam("search_query", queryString);
|
||||
if (maxResult != null) {
|
||||
local = local.queryParam("max_results", String.valueOf(maxResult));
|
||||
}
|
||||
if (start != null) {
|
||||
local = local.queryParam("start", String.valueOf(start));
|
||||
}
|
||||
Invocation.Builder invocationBuilder = local.request(MediaType.TEXT_PLAIN_TYPE);
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
OMXMLParserWrapper records = OMXMLBuilderFactory.createOMBuilder(new StringReader(responseString));
|
||||
OMElement element = records.getDocumentElement();
|
||||
AXIOMXPath xpath = null;
|
||||
try {
|
||||
xpath = new AXIOMXPath("opensearch:totalResults");
|
||||
xpath.addNamespace("opensearch", "http://a9.com/-/spec/opensearch/1.1/");
|
||||
OMElement count = (OMElement) xpath.selectSingleNode(element);
|
||||
return Integer.parseInt(count.getText());
|
||||
} catch (JaxenException e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get ArXiv entries based on
|
||||
* query object.
|
||||
* This Callable use as query value the string queryString passed to constructor.
|
||||
* If the object will be construct through Query.class instance, a Query's map entry with key "query" will be used.
|
||||
* Pagination is supported too, using the value of the Query's map with keys "start" and "count".
|
||||
*
|
||||
* @see org.dspace.importer.external.datamodel.Query
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
private class SearchByQueryCallable implements Callable<List<ImportRecord>> {
|
||||
private Query query;
|
||||
|
||||
|
||||
private SearchByQueryCallable(String queryString, Integer maxResult, Integer start) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
query.addParameter("start", start);
|
||||
query.addParameter("count", maxResult);
|
||||
}
|
||||
|
||||
private SearchByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
List<ImportRecord> results = new ArrayList<ImportRecord>();
|
||||
String queryString = query.getParameterAsClass("query", String.class);
|
||||
Integer start = query.getParameterAsClass("start", Integer.class);
|
||||
Integer maxResult = query.getParameterAsClass("count", Integer.class);
|
||||
WebTarget local = webTarget.queryParam("search_query", queryString);
|
||||
if (maxResult != null) {
|
||||
local = local.queryParam("max_results", String.valueOf(maxResult));
|
||||
}
|
||||
if (start != null) {
|
||||
local = local.queryParam("start", String.valueOf(start));
|
||||
}
|
||||
Invocation.Builder invocationBuilder = local.request(MediaType.TEXT_PLAIN_TYPE);
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
List<OMElement> omElements = splitToRecords(responseString);
|
||||
for (OMElement record : omElements) {
|
||||
results.add(transformSourceRecords(record));
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get an ArXiv entry using ArXiv ID
|
||||
* The ID to use can be passed through the constructor as a String or as Query's map entry, with the key "id".
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
private class SearchByIdCallable implements Callable<List<ImportRecord>> {
|
||||
private Query query;
|
||||
|
||||
private SearchByIdCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
private SearchByIdCallable(String id) {
|
||||
this.query = new Query();
|
||||
query.addParameter("id", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
List<ImportRecord> results = new ArrayList<ImportRecord>();
|
||||
String arxivid = query.getParameterAsClass("id", String.class);
|
||||
if (StringUtils.isNotBlank(arxivid)) {
|
||||
arxivid = arxivid.trim();
|
||||
if (arxivid.startsWith("http://arxiv.org/abs/")) {
|
||||
arxivid = arxivid.substring("http://arxiv.org/abs/".length());
|
||||
} else if (arxivid.toLowerCase().startsWith("arxiv:")) {
|
||||
arxivid = arxivid.substring("arxiv:".length());
|
||||
}
|
||||
}
|
||||
WebTarget local = webTarget.queryParam("id_list", arxivid);
|
||||
Invocation.Builder invocationBuilder = local.request(MediaType.TEXT_PLAIN_TYPE);
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
List<OMElement> omElements = splitToRecords(responseString);
|
||||
for (OMElement record : omElements) {
|
||||
results.add(transformSourceRecords(record));
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to search ArXiv entries
|
||||
* using author and title.
|
||||
* There are two field in the Query map to pass, with keys "title" and "author"
|
||||
* (at least one must be used).
|
||||
*
|
||||
* @see org.dspace.importer.external.datamodel.Query
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
private class FindMatchingRecordCallable implements Callable<List<ImportRecord>> {
|
||||
|
||||
private Query query;
|
||||
|
||||
private FindMatchingRecordCallable(Query q) {
|
||||
query = q;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
String queryString = getQuery(this.query);
|
||||
List<ImportRecord> results = new ArrayList<ImportRecord>();
|
||||
WebTarget local = webTarget.queryParam("search_query", queryString);
|
||||
Invocation.Builder invocationBuilder = local.request(MediaType.TEXT_PLAIN_TYPE);
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
List<OMElement> omElements = splitToRecords(responseString);
|
||||
for (OMElement record : omElements) {
|
||||
results.add(transformSourceRecords(record));
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getQuery(Query query) {
|
||||
String title = query.getParameterAsClass("title", String.class);
|
||||
String author = query.getParameterAsClass("author", String.class);
|
||||
StringBuffer queryString = new StringBuffer();
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
queryString.append("ti:\"").append(title).append("\"");
|
||||
}
|
||||
if (StringUtils.isNotBlank(author)) {
|
||||
// [FAU]
|
||||
if (queryString.length() > 0) {
|
||||
queryString.append(" AND ");
|
||||
}
|
||||
queryString.append("au:\"").append(author).append("\"");
|
||||
}
|
||||
return queryString.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private List<OMElement> splitToRecords(String recordsSrc) {
|
||||
OMXMLParserWrapper records = OMXMLBuilderFactory.createOMBuilder(new StringReader(recordsSrc));
|
||||
OMElement element = records.getDocumentElement();
|
||||
AXIOMXPath xpath = null;
|
||||
try {
|
||||
xpath = new AXIOMXPath("ns:entry");
|
||||
xpath.addNamespace("ns", "http://www.w3.org/2005/Atom");
|
||||
List<OMElement> recordsList = xpath.selectNodes(element);
|
||||
return recordsList;
|
||||
} catch (JaxenException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the baseAddress set to this object
|
||||
*
|
||||
* @return The String object that represents the baseAddress of this object
|
||||
*/
|
||||
public String getBaseAddress() {
|
||||
return baseAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the baseAddress to this object
|
||||
*
|
||||
* @param baseAddress The String object that represents the baseAddress of this object
|
||||
*/
|
||||
public void setBaseAddress(String baseAddress) {
|
||||
this.baseAddress = baseAddress;
|
||||
}
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.csv.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import au.com.bytecode.opencsv.CSVReader;
|
||||
import org.dspace.importer.external.exception.FileSourceException;
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
||||
import org.dspace.importer.external.metadatamapping.contributor.MetadataContributor;
|
||||
import org.dspace.importer.external.service.components.AbstractPlainMetadataSource;
|
||||
import org.dspace.importer.external.service.components.MetadataSource;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataKeyValueItem;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataSourceDto;
|
||||
|
||||
|
||||
/**
|
||||
* This class is an implementation of {@link MetadataSource} which extends {@link AbstractPlainMetadataSource}
|
||||
* in order to parse "character separated" files like csv, tsv, etc using the Live Import framework.
|
||||
*
|
||||
* @author Pasquale Cavallo
|
||||
*
|
||||
*/
|
||||
public class CharacterSeparatedImportMetadataSourceServiceImpl extends AbstractPlainMetadataSource {
|
||||
|
||||
private char separator = ',';
|
||||
|
||||
private char escapeCharacter = '"';
|
||||
|
||||
private Integer skipLines = 1;
|
||||
|
||||
private String importSource = "CsvMetadataSource";
|
||||
|
||||
/**
|
||||
* Set the number of lines to skip at the start of the file. This method is suitable,
|
||||
* for example, to skip file headers.
|
||||
*
|
||||
* @param skipLines number of the line at the start of the file to skip.
|
||||
*/
|
||||
public void setSkipLines(Integer skipLines) {
|
||||
this.skipLines = skipLines;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the number of the lines to skip
|
||||
*/
|
||||
public Integer getSkipLines() {
|
||||
return skipLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to inject the separator
|
||||
* This must be the ASCII integer
|
||||
* related to the char.
|
||||
* In example, 9 for tab, 44 for comma
|
||||
*/
|
||||
public void setSeparator(char separator) {
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return importSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the name of the source
|
||||
*/
|
||||
public void setImportSource(String importSource) {
|
||||
this.importSource = importSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to inject the escape character. This must be the ASCII integer
|
||||
* related to the char.
|
||||
* In example, 9 for tab, 44 for comma
|
||||
*
|
||||
*/
|
||||
public void setEscapeCharacter(char escapeCharacter) {
|
||||
this.escapeCharacter = escapeCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method process any kind of "character separated" files, like CSV, TSV, and so on.
|
||||
* It return a List of PlainMetadataSourceDto.
|
||||
* Using the superclass methods AbstractPlainMetadataSource.getRecord(s), any of this
|
||||
* element will then be converted in an {@link org.dspace.importer.external.datamodel.ImportRecord}.
|
||||
|
||||
* Columns will be identified by their position, zero based notation.
|
||||
* Separator character and escape character MUST be defined at class level. Number of lines to skip (headers)
|
||||
* could also be defined in the field skipLines.
|
||||
*
|
||||
* @param InputStream The inputStream of the file
|
||||
* @return A list of PlainMetadataSourceDto
|
||||
* @throws FileSourceException if, for any reason, the file is not parsable
|
||||
|
||||
*/
|
||||
@Override
|
||||
protected List<PlainMetadataSourceDto> readData(InputStream inputStream) throws FileSourceException {
|
||||
List<PlainMetadataSourceDto> plainMetadataList = new ArrayList<>();
|
||||
try (CSVReader csvReader = new CSVReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8),
|
||||
separator, escapeCharacter);) {
|
||||
// read all row
|
||||
List<String[]> lines = csvReader.readAll();
|
||||
int listSize = lines == null ? 0 : lines.size();
|
||||
int count = skipLines;
|
||||
// iterate over row (skipping the first skipLines)
|
||||
while (count < listSize) {
|
||||
String [] items = lines.get(count);
|
||||
List<PlainMetadataKeyValueItem> keyValueList = new ArrayList<>();
|
||||
if (items != null) {
|
||||
int size = items.length;
|
||||
int index = 0;
|
||||
//iterate over column in the selected row
|
||||
while (index < size) {
|
||||
//create key/value item for the specifics row/column
|
||||
PlainMetadataKeyValueItem keyValueItem = new PlainMetadataKeyValueItem();
|
||||
keyValueItem.setKey(String.valueOf(index));
|
||||
keyValueItem.setValue(items[index]);
|
||||
keyValueList.add(keyValueItem);
|
||||
index++;
|
||||
}
|
||||
//save all column key/value for the given row
|
||||
PlainMetadataSourceDto dto = new PlainMetadataSourceDto();
|
||||
dto.setMetadata(keyValueList);
|
||||
plainMetadataList.add(dto);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FileSourceException("Error reading file", e);
|
||||
}
|
||||
return plainMetadataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMetadataFieldMap(Map<MetadataFieldConfig,
|
||||
MetadataContributor<PlainMetadataSourceDto>> metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -71,7 +71,7 @@ public class Query {
|
||||
return null;
|
||||
} else {
|
||||
Object o = c.iterator().next();
|
||||
if (clazz.isAssignableFrom(o.getClass())) {
|
||||
if (o != null && clazz.isAssignableFrom(o.getClass())) {
|
||||
return (T) o;
|
||||
} else {
|
||||
return null;
|
||||
|
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.endnote.service;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.dspace.importer.external.exception.FileSourceException;
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
||||
import org.dspace.importer.external.metadatamapping.contributor.MetadataContributor;
|
||||
import org.dspace.importer.external.service.components.AbstractPlainMetadataSource;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataKeyValueItem;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataSourceDto;
|
||||
|
||||
/**
|
||||
* Implements a metadata importer for Endnote files
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*/
|
||||
public class EndnoteImportMetadataSourceServiceImpl extends AbstractPlainMetadataSource {
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "EndnoteMetadataSource";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method map the data present in the inputStream, then return a list PlainMetadataSourceDto.
|
||||
* Any PlainMetadataSourceDto will be used to create a single {@link org.dspace.importer.external.datamodel.ImportRecord}
|
||||
*
|
||||
* @param inputStream the inputStream of the Endnote file
|
||||
* @return List of {@link org.dspace.importer.external.service.components.dto.PlainMetadataSourceDto}
|
||||
* @throws FileSourceException
|
||||
* @see org.dspace.importer.external.service.components.AbstractPlainMetadataSource
|
||||
*/
|
||||
@Override
|
||||
protected List<PlainMetadataSourceDto> readData(InputStream fileInpuStream) throws FileSourceException {
|
||||
List<PlainMetadataSourceDto> list = new ArrayList<>();
|
||||
try {
|
||||
// row start from 3, because the first 2 (FN and VR) will be removed by tokenize
|
||||
int lineForDebug = 3;
|
||||
List<PlainMetadataKeyValueItem> tokenized = tokenize(fileInpuStream);
|
||||
List<PlainMetadataKeyValueItem> tmpList = new ArrayList<>();
|
||||
// iterate over key/value pairs, create a new PlainMetadataSourceDto on "ER" rows (which means "new record)
|
||||
// and stop on EF (end of file).
|
||||
for (PlainMetadataKeyValueItem item : tokenized) {
|
||||
if (item.getKey() == null || item.getKey().isEmpty()) {
|
||||
throw new FileSourceException("Null or empty key expected on line "
|
||||
+ lineForDebug + ". Keys cannot be null nor empty");
|
||||
}
|
||||
if ("EF".equals(item.getKey())) {
|
||||
// end of file
|
||||
break;
|
||||
}
|
||||
if ("ER".equals(item.getKey())) {
|
||||
// new ImportRecord start from here (ER is a content delimiter)
|
||||
// save the previous, then create a new one
|
||||
PlainMetadataSourceDto dto = new PlainMetadataSourceDto();
|
||||
dto.setMetadata(new ArrayList<>(tmpList));
|
||||
list.add(dto);
|
||||
tmpList = new ArrayList<>();
|
||||
} else {
|
||||
if (item.getValue() == null || item.getValue().isEmpty()) {
|
||||
throw new FileSourceException("Null or empty value expected on line "
|
||||
+ lineForDebug + ". Value expected");
|
||||
}
|
||||
tmpList.add(item);
|
||||
}
|
||||
lineForDebug++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new FileSourceException("Error reading file", e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method iterate over file rows, split content in a list of key/value items through RexExp
|
||||
* and save the content sequentially.
|
||||
* Key "FN" and "VR", which is a preamble in Endnote, will be checked but not saved.
|
||||
*
|
||||
* @param fileInpuStream the inputStream of the Endnote file
|
||||
* @return A list of key/value items which map the file's row sequentially
|
||||
* @throws IOException
|
||||
* @throws FileSourceException
|
||||
*/
|
||||
private List<PlainMetadataKeyValueItem> tokenize(InputStream fileInpuStream)
|
||||
throws IOException, FileSourceException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(fileInpuStream));
|
||||
String line;
|
||||
line = reader.readLine();
|
||||
// FN and VR works as preamble, just check and skip them
|
||||
if (line == null || !line.startsWith("FN")) {
|
||||
throw new FileSourceException("Invalid endNote file");
|
||||
}
|
||||
line = reader.readLine();
|
||||
if (line == null || !line.startsWith("VR")) {
|
||||
throw new FileSourceException("Invalid endNote file");
|
||||
}
|
||||
// split any row into first part ^[A-Z]{2} used as key (the meaning of the data)
|
||||
// and second part ?(.*) used as value (the data)
|
||||
Pattern pattern = Pattern.compile("(^[A-Z]{2}) ?(.*)$");
|
||||
List<PlainMetadataKeyValueItem> list = new ArrayList<PlainMetadataKeyValueItem>();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
// skip empty lines
|
||||
if (line.isEmpty() || line.equals("")) {
|
||||
continue;
|
||||
}
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
if (matcher.matches()) {
|
||||
PlainMetadataKeyValueItem item = new PlainMetadataKeyValueItem();
|
||||
item.setKey(matcher.group(1));
|
||||
item.setValue(matcher.group(2));
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMetadataFieldMap(Map<MetadataFieldConfig,
|
||||
MetadataContributor<PlainMetadataSourceDto>> metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.metadatamapping.contributor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import au.com.bytecode.opencsv.CSVReader;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataKeyValueItem;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataSourceDto;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements functionalities to handle common situation regarding plain metadata.
|
||||
* In some scenario, like csv or tsv, the format don't allow lists.
|
||||
* We can use this MetadataContribut to parse a given plain metadata and split it into
|
||||
* related list, based on the delimiter. No escape character is present.
|
||||
* Default values are comma (,) for delimiter, and double quote (") for escape character
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class EnhancedSimpleMetadataContributor extends SimpleMetadataContributor {
|
||||
|
||||
private char delimiter = ',';
|
||||
|
||||
private char escape = '"';
|
||||
|
||||
/**
|
||||
* This method could be used to set the delimiter used during parse
|
||||
* If no delimiter is set, comma will be used
|
||||
*/
|
||||
public void setDelimiter(char delimiter) {
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method could be used to get the delimiter used in this class
|
||||
*/
|
||||
public char getDelimiter() {
|
||||
return delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to inject the escape character.
|
||||
* This must be the ASCII integer
|
||||
* related to the char.
|
||||
* In example, 9 for tab, 44 for comma
|
||||
* If no escape is set, double quote will be used
|
||||
*/
|
||||
public void setEscape(char escape) {
|
||||
this.escape = escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the escape character.
|
||||
*
|
||||
*/
|
||||
public char getEscape() {
|
||||
return escape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MetadatumDTO> contributeMetadata(PlainMetadataSourceDto t) {
|
||||
Collection<MetadatumDTO> values = null;
|
||||
values = new LinkedList<>();
|
||||
for (PlainMetadataKeyValueItem metadatum : t.getMetadata()) {
|
||||
if (getKey().equals(metadatum.getKey())) {
|
||||
String[] splitted = splitToRecord(metadatum.getValue());
|
||||
for (String value : splitted) {
|
||||
MetadatumDTO dcValue = new MetadatumDTO();
|
||||
dcValue.setValue(value);
|
||||
dcValue.setElement(getField().getElement());
|
||||
dcValue.setQualifier(getField().getQualifier());
|
||||
dcValue.setSchema(getField().getSchema());
|
||||
values.add(dcValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private String[] splitToRecord(String value) {
|
||||
List<String[]> rows;
|
||||
// For example, list of author must be: Author 1, author 2, author 3
|
||||
// if author name contains comma, is important to escape its in
|
||||
// this way: Author 1, \"Author 2, something\", Author 3
|
||||
try (CSVReader csvReader = new CSVReader(new StringReader(value),
|
||||
delimiter, escape);) {
|
||||
rows = csvReader.readAll();
|
||||
} catch (IOException e) {
|
||||
//fallback, use the inpu as value
|
||||
return new String[] { value };
|
||||
}
|
||||
//must be one row
|
||||
return rows.get(0);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.metadatamapping.contributor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldMapping;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
|
||||
/**
|
||||
* This Contributor is helpful to avoid the limit of the Live Import Framework.
|
||||
* In Live Import, one dc schema/element/qualifier could be associate with one and
|
||||
* only one MetadataContributor, because the map they're saved in use dc entity as key.
|
||||
*
|
||||
* In fact, in this implementation we use the MetadataFieldConfig present in this MultipleMetadataContributor
|
||||
* contributor, but the data (values of the dc metadatum) will be loaded using any of the contributor defined
|
||||
* in the List metadatumContributors, by iterating over them.
|
||||
*
|
||||
* @see org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping<RecordType>
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class MultipleMetadataContributor<T> implements MetadataContributor<T> {
|
||||
|
||||
private MetadataFieldConfig field;
|
||||
|
||||
private List<MetadataContributor> metadatumContributors;
|
||||
|
||||
/**
|
||||
* Empty constructor
|
||||
*/
|
||||
public MultipleMetadataContributor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param field {@link org.dspace.importer.external.metadatamapping.MetadataFieldConfig} used in
|
||||
* mapping
|
||||
* @param metadatumContributors A list of MetadataContributor
|
||||
*/
|
||||
public MultipleMetadataContributor(MetadataFieldConfig field, List<MetadataContributor> metadatumContributors) {
|
||||
this.field = field;
|
||||
this.metadatumContributors = (LinkedList<MetadataContributor>) metadatumContributors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the metadatafieldMapping used in the transforming of a record to actual metadata
|
||||
*
|
||||
* @param metadataFieldMapping the new mapping.
|
||||
*/
|
||||
@Override
|
||||
public void setMetadataFieldMapping(MetadataFieldMapping<T, MetadataContributor<T>> metadataFieldMapping) {
|
||||
for (MetadataContributor metadatumContributor : metadatumContributors) {
|
||||
metadatumContributor.setMetadataFieldMapping(metadataFieldMapping);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* a separate Metadatum object is created for each index of Metadatum returned from the calls to
|
||||
* MetadatumContributor.contributeMetadata(t) for each MetadatumContributor in the metadatumContributors list.
|
||||
* All of them have as dc schema/element/qualifier the values defined in MetadataFieldConfig.
|
||||
*
|
||||
* @param t the object we are trying to translate
|
||||
* @return a collection of metadata got from each MetadataContributor
|
||||
*/
|
||||
@Override
|
||||
public Collection<MetadatumDTO> contributeMetadata(T t) {
|
||||
Collection<MetadatumDTO> values = new ArrayList<>();
|
||||
for (MetadataContributor metadatumContributor : metadatumContributors) {
|
||||
Collection<MetadatumDTO> metadata = metadatumContributor.contributeMetadata(t);
|
||||
values.addAll(metadata);
|
||||
}
|
||||
changeDC(values);
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does the trick of this implementation.
|
||||
* It changes the DC schema/element/qualifier of the given Metadatum into
|
||||
* the ones present in this contributor.
|
||||
* In this way, the contributors in metadatumContributors could have any dc values,
|
||||
* because this method remap them all.
|
||||
*
|
||||
* @param the list of metadata we want to remap
|
||||
*/
|
||||
private void changeDC(Collection<MetadatumDTO> values) {
|
||||
for (MetadatumDTO dto : values) {
|
||||
dto.setElement(field.getElement());
|
||||
dto.setQualifier(field.getQualifier());
|
||||
dto.setSchema(field.getSchema());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MetadataFieldConfig used while retrieving MetadatumDTO
|
||||
*
|
||||
* @return MetadataFieldConfig
|
||||
*/
|
||||
public MetadataFieldConfig getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting the MetadataFieldConfig
|
||||
*
|
||||
* @param field MetadataFieldConfig used while retrieving MetadatumDTO
|
||||
*/
|
||||
public void setField(MetadataFieldConfig field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the List of MetadataContributor objects set to this class
|
||||
*
|
||||
* @return metadatumContributors, list of MetadataContributor
|
||||
*/
|
||||
public List<MetadataContributor> getMetadatumContributors() {
|
||||
return metadatumContributors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the List of MetadataContributor objects set to this class
|
||||
*
|
||||
* @param metadatumContributors A list of MetadatumContributor classes
|
||||
*/
|
||||
public void setMetadatumContributors(List<MetadataContributor> metadatumContributors) {
|
||||
this.metadatumContributors = metadatumContributors;
|
||||
}
|
||||
}
|
@@ -77,18 +77,33 @@ public class SimpleMetadataContributor implements MetadataContributor<PlainMetad
|
||||
return values;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setter to inject field item
|
||||
/**
|
||||
* Method to inject field item
|
||||
*
|
||||
* @param field the {@link MetadataFieldConfig} to use in this contributor
|
||||
*/
|
||||
public void setField(MetadataFieldConfig field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setter to inject key value
|
||||
/**
|
||||
* Method to inject key value
|
||||
*/
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve field item
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve the {@link MetadataFieldConfig} used in this contributor
|
||||
*/
|
||||
public MetadataFieldConfig getField() {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldMapping;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.jaxen.JaxenException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
/**
|
||||
@@ -31,6 +33,8 @@ import org.springframework.beans.factory.annotation.Required;
|
||||
public class SimpleXpathMetadatumContributor implements MetadataContributor<OMElement> {
|
||||
private MetadataFieldConfig field;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleXpathMetadatumContributor.class);
|
||||
|
||||
/**
|
||||
* Return prefixToNamespaceMapping
|
||||
*
|
||||
@@ -157,12 +161,12 @@ public class SimpleXpathMetadatumContributor implements MetadataContributor<OMEl
|
||||
} else if (el instanceof OMText) {
|
||||
values.add(metadataFieldMapping.toDCValue(field, ((OMText) el).getText()));
|
||||
} else {
|
||||
System.err.println("node of type: " + el.getClass());
|
||||
log.error("node of type: " + el.getClass());
|
||||
}
|
||||
}
|
||||
return values;
|
||||
} catch (JaxenException e) {
|
||||
System.err.println(query);
|
||||
log.error(query, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,7 @@ import org.jaxen.JaxenException;
|
||||
* Implements a data source for querying PubMed Central
|
||||
*
|
||||
* @author Roeland Dillen (roeland at atmire dot com)
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*/
|
||||
public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<OMElement>
|
||||
implements QuerySource, FileSource {
|
||||
|
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.ris.service;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.dspace.importer.external.exception.FileSourceException;
|
||||
import org.dspace.importer.external.service.components.AbstractPlainMetadataSource;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataKeyValueItem;
|
||||
import org.dspace.importer.external.service.components.dto.PlainMetadataSourceDto;
|
||||
|
||||
/**
|
||||
* Implements a metadata importer for RIS files
|
||||
* Implementations insprider by BTE DataLoader {@link https://github.com/EKT/Biblio-Transformation-Engine/blob/master/bte-io/src/main/java/gr/ekt/bteio/loaders/RISDataLoader.java}
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*/
|
||||
public class RisImportMetadataSourceServiceImpl extends AbstractPlainMetadataSource {
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "RISMetadataSource";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PlainMetadataSourceDto> readData(InputStream inputStream) throws FileSourceException {
|
||||
return aggregateData(inputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method map the data present in the inputStream, then return a list PlainMetadataSourceDto.
|
||||
* Any PlainMetadataSourceDto will be used to create a single {@link org.dspace.importer.external.datamodel.ImportRecord}
|
||||
*
|
||||
* @see org.dspace.importer.external.service.components.AbstractPlainMetadataSource
|
||||
*
|
||||
* @param inputStream the inputStream of the RIS file
|
||||
* @return List of {@link org.dspace.importer.external.service.components.dto.PlainMetadataSourceDto}
|
||||
* @throws FileSourceException
|
||||
*/
|
||||
private List<PlainMetadataSourceDto> aggregateData(InputStream inputStream) throws FileSourceException {
|
||||
List<PlainMetadataSourceDto> metadata = new ArrayList<>();
|
||||
//map any line of the field to a key/value pair
|
||||
List<PlainMetadataKeyValueItem> notAggregatedItems = notAggregatedData(inputStream);
|
||||
List<PlainMetadataKeyValueItem> aggregatedTmpList = null;
|
||||
Iterator<PlainMetadataKeyValueItem> itr = notAggregatedItems.iterator();
|
||||
// iterate over the list of key/value items
|
||||
// create a new PlainMetadataSourceDto (which map and ImportRecord)
|
||||
// any times the key is "TY" (content separator in RIS)
|
||||
while (itr.hasNext()) {
|
||||
PlainMetadataKeyValueItem item = itr.next();
|
||||
if ("TY".equals(item.getKey())) {
|
||||
if (aggregatedTmpList != null) {
|
||||
PlainMetadataSourceDto dto = new PlainMetadataSourceDto();
|
||||
dto.setMetadata(new ArrayList<>(aggregatedTmpList));
|
||||
metadata.add(dto);
|
||||
}
|
||||
aggregatedTmpList = new ArrayList<>();
|
||||
aggregatedTmpList.add(item);
|
||||
} else {
|
||||
if (aggregatedTmpList != null) {
|
||||
aggregatedTmpList.add(item);
|
||||
// save last iteration metadata
|
||||
if (!itr.hasNext()) {
|
||||
PlainMetadataSourceDto dto = new PlainMetadataSourceDto();
|
||||
dto.setMetadata(new ArrayList<>(aggregatedTmpList));
|
||||
metadata.add(dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method transform any row of the RIS file into a PlainMetadataKeyValueItem,
|
||||
* splitting the row sequentially through a RegExp without take care of the means of the data.
|
||||
* In this way, all entries present in the file are mapped in the resulting list.
|
||||
*
|
||||
* @param inputStream the inputStrem of the file
|
||||
* @return A list
|
||||
* @throws FileSourceException
|
||||
*/
|
||||
private List<PlainMetadataKeyValueItem> notAggregatedData(InputStream inputStream) throws FileSourceException {
|
||||
LinkedList<PlainMetadataKeyValueItem> items = new LinkedList<>();
|
||||
BufferedReader reader;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.isEmpty() || line.equals("") || line.matches("^\\s*$")) {
|
||||
continue;
|
||||
}
|
||||
//match valid RIS entry
|
||||
Pattern risPattern = Pattern.compile("^([A-Z][A-Z0-9]) - (.*)$");
|
||||
Matcher risMatcher = risPattern.matcher(line);
|
||||
if (risMatcher.matches()) {
|
||||
PlainMetadataKeyValueItem keyValueItem = new PlainMetadataKeyValueItem();
|
||||
keyValueItem.setValue(risMatcher.group(2));
|
||||
keyValueItem.setKey(risMatcher.group(1));
|
||||
items.add(keyValueItem);
|
||||
} else {
|
||||
if (!items.isEmpty()) {
|
||||
items.getLast().setValue(items.getLast().getValue().concat(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new FileSourceException("Cannot parse RIS file", e);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the MetadataFieldMapping containing the mapping between RecordType
|
||||
* (in this case PlainMetadataSourceDto.class) and Metadata
|
||||
*
|
||||
* @return The configured MetadataFieldMapping
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Resource(name = "risMetadataFieldMap")
|
||||
public void setMetadataFieldMap(@SuppressWarnings("rawtypes") Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -16,7 +16,6 @@ import org.dspace.importer.external.metadatamapping.contributor.MetadataContribu
|
||||
import org.dspace.importer.external.metadatamapping.transform.GenerateQueryService;
|
||||
import org.dspace.importer.external.service.components.AbstractRemoteMetadataSource;
|
||||
import org.dspace.importer.external.service.components.MetadataSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
/**
|
||||
@@ -49,7 +48,6 @@ public abstract class AbstractImportMetadataSourceService<RecordType> extends Ab
|
||||
*
|
||||
* @param generateQueryForItem the query generator to be used.
|
||||
*/
|
||||
@Autowired
|
||||
public void setGenerateQueryForItem(GenerateQueryService generateQueryForItem) {
|
||||
this.generateQueryForItem = generateQueryForItem;
|
||||
}
|
||||
|
@@ -22,6 +22,11 @@ import org.dspace.importer.external.exception.FileSourceException;
|
||||
*/
|
||||
public interface FileSource extends MetadataSource {
|
||||
|
||||
/**
|
||||
* Get the file extensions (xml, csv, txt, ...) supported by the FileSource
|
||||
*/
|
||||
public List<String> getSupportedExtensions();
|
||||
|
||||
/**
|
||||
* Return a list of ImportRecord constructed from input file.
|
||||
*
|
||||
@@ -62,9 +67,4 @@ public interface FileSource extends MetadataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extensions (xml, csv, txt, ...) supported by the FileSource implementation
|
||||
*/
|
||||
public List<String> getSupportedExtensions();
|
||||
|
||||
}
|
||||
|
@@ -82,6 +82,7 @@ public class Process implements ReloadableEntity<Integer> {
|
||||
private Date creationTime;
|
||||
|
||||
public static final String BITSTREAM_TYPE_METADATAFIELD = "dspace.process.filetype";
|
||||
public static final String OUTPUT_TYPE = "script_output";
|
||||
|
||||
protected Process() {
|
||||
}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public enum ProcessLogLevel {
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.scripts;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a container class in which the variables can be stored that a {@link Process} must adhere to when being
|
||||
* retrieved from the DB through the search methods
|
||||
*/
|
||||
public class ProcessQueryParameterContainer {
|
||||
|
||||
|
||||
private Map<String, Object> queryParameterMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Generic getter for the queryParameterMap
|
||||
* @return the queryParameterMap value of this ProcessQueryParameterContainer
|
||||
*/
|
||||
public Map<String, Object> getQueryParameterMap() {
|
||||
return queryParameterMap;
|
||||
}
|
||||
|
||||
private String sortProperty = "startTime";
|
||||
private String sortOrder = "desc";
|
||||
/**
|
||||
* Generic setter for the queryParameterMap
|
||||
* @param queryParameterMap The queryParameterMap to be set on this ProcessQueryParameterContainer
|
||||
*/
|
||||
public void setQueryParameterMap(Map<String, Object> queryParameterMap) {
|
||||
this.queryParameterMap = queryParameterMap;
|
||||
}
|
||||
|
||||
public void addToQueryParameterMap(String key, Object object) {
|
||||
if (queryParameterMap == null) {
|
||||
queryParameterMap = new HashMap<>();
|
||||
}
|
||||
queryParameterMap.put(key, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic getter for the sortProperty
|
||||
* @return the sortProperty value of this ProcessQueryParameterContainer
|
||||
*/
|
||||
public String getSortProperty() {
|
||||
return sortProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic setter for the sortProperty
|
||||
* @param sortProperty The sortProperty to be set on this ProcessQueryParameterContainer
|
||||
*/
|
||||
public void setSortProperty(String sortProperty) {
|
||||
this.sortProperty = sortProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic getter for the sortOrder
|
||||
* @return the sortOrder value of this ProcessQueryParameterContainer
|
||||
*/
|
||||
public String getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic setter for the sortOrder
|
||||
* @param sortOrder The sortOrder to be set on this ProcessQueryParameterContainer
|
||||
*/
|
||||
public void setSortOrder(String sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
}
|
@@ -7,9 +7,14 @@
|
||||
*/
|
||||
package org.dspace.scripts;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -20,6 +25,7 @@ import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
@@ -37,6 +43,7 @@ import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogManager;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.scripts.service.ProcessService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@@ -62,6 +69,9 @@ public class ProcessServiceImpl implements ProcessService {
|
||||
@Autowired
|
||||
private MetadataFieldService metadataFieldService;
|
||||
|
||||
@Autowired
|
||||
private EPersonService ePersonService;
|
||||
|
||||
@Override
|
||||
public Process create(Context context, EPerson ePerson, String scriptName,
|
||||
List<DSpaceCommandLineParameter> parameters) throws SQLException {
|
||||
@@ -245,4 +255,59 @@ public class ProcessServiceImpl implements ProcessService {
|
||||
return new ArrayList<>(fileTypesSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer,
|
||||
int limit, int offset) throws SQLException {
|
||||
return processDAO.search(context, processQueryParameterContainer, limit, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSearch(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
|
||||
throws SQLException {
|
||||
return processDAO.countTotalWithParameters(context, processQueryParameterContainer);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void appendLog(int processId, String scriptName, String output, ProcessLogLevel processLogLevel)
|
||||
throws IOException {
|
||||
File tmpDir = FileUtils.getTempDirectory();
|
||||
File tempFile = new File(tmpDir, scriptName + processId + ".log");
|
||||
FileWriter out = new FileWriter(tempFile, true);
|
||||
try {
|
||||
try (BufferedWriter writer = new BufferedWriter(out)) {
|
||||
writer.append(formatLogLine(processId, scriptName, output, processLogLevel));
|
||||
writer.newLine();
|
||||
}
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLogBitstream(Context context, Process process)
|
||||
throws IOException, SQLException, AuthorizeException {
|
||||
File tmpDir = FileUtils.getTempDirectory();
|
||||
File tempFile = new File(tmpDir, process.getName() + process.getID() + ".log");
|
||||
FileInputStream inputStream = FileUtils.openInputStream(tempFile);
|
||||
appendFile(context, process, inputStream, Process.OUTPUT_TYPE, process.getName() + process.getID() + ".log");
|
||||
inputStream.close();
|
||||
tempFile.delete();
|
||||
}
|
||||
|
||||
private String formatLogLine(int processId, String scriptName, String output, ProcessLogLevel processLogLevel) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(sdf.format(new Date()));
|
||||
sb.append(" ");
|
||||
sb.append(processLogLevel);
|
||||
sb.append(" ");
|
||||
sb.append(scriptName);
|
||||
sb.append(" - ");
|
||||
sb.append(processId);
|
||||
sb.append(" @ ");
|
||||
sb.append(output);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user