mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge remote-tracking branch 'origin/coar-notify-7' into coar-notify-7-part-two
# Conflicts: # dspace-api/src/main/java/org/dspace/app/ldn/service/LDNMessageService.java # dspace-api/src/main/java/org/dspace/app/ldn/service/impl/LDNMessageServiceImpl.java
This commit is contained in:
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -21,11 +21,11 @@ jobs:
|
||||
# Also specify version of Java to use (this can allow us to optionally run tests on multiple JDKs in future)
|
||||
matrix:
|
||||
include:
|
||||
# NOTE: Unit Tests include deprecated REST API v6 (as it has unit tests)
|
||||
# NOTE: Unit Tests include a retry for occasionally failing tests
|
||||
# - surefire.rerunFailingTestsCount => try again for flakey tests, and keep track of/report on number of retries
|
||||
- type: "Unit Tests"
|
||||
java: 11
|
||||
mvnflags: "-DskipUnitTests=false -Pdspace-rest -Dsurefire.rerunFailingTestsCount=2"
|
||||
mvnflags: "-DskipUnitTests=false -Dsurefire.rerunFailingTestsCount=2"
|
||||
resultsdir: "**/target/surefire-reports/**"
|
||||
# NOTE: ITs skip all code validation checks, as they are already done by Unit Test job.
|
||||
# - enforcer.skip => Skip maven-enforcer-plugin rules
|
||||
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -102,6 +102,8 @@ jobs:
|
||||
build_id: dspace-solr
|
||||
image_name: dspace/dspace-solr
|
||||
dockerfile_path: ./dspace/src/main/docker/dspace-solr/Dockerfile
|
||||
# Must pass solrconfigs to the Dockerfile so that it can find the required Solr config files
|
||||
dockerfile_additional_contexts: 'solrconfigs=./dspace/solr/'
|
||||
secrets:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
10
.github/workflows/reusable-docker-build.yml
vendored
10
.github/workflows/reusable-docker-build.yml
vendored
@@ -24,6 +24,12 @@ on:
|
||||
dockerfile_context:
|
||||
required: false
|
||||
type: string
|
||||
default: '.'
|
||||
# Optionally a list of "additional_contexts" to pass to Dockerfile. Defaults to empty
|
||||
dockerfile_additional_contexts:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
# If Docker image should have additional tag flavor details (e.g. a suffix), it may be passed in.
|
||||
tags_flavor:
|
||||
required: false
|
||||
@@ -123,7 +129,9 @@ jobs:
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ${{ inputs.dockerfile_context || '.' }}
|
||||
build-contexts: |
|
||||
${{ inputs.dockerfile_additional_contexts }}
|
||||
context: ${{ inputs.dockerfile_context }}
|
||||
file: ${{ inputs.dockerfile_path }}
|
||||
platforms: ${{ matrix.arch }}
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
|
@@ -19,7 +19,7 @@ RUN mkdir /install \
|
||||
USER dspace
|
||||
# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents)
|
||||
ADD --chown=dspace . /app/
|
||||
# Build DSpace (note: this build doesn't include the optional, deprecated "dspace-rest" webapp)
|
||||
# Build DSpace
|
||||
# Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small
|
||||
# Maven flags here ensure that we skip building test environment and skip all code verification checks.
|
||||
# These flags speed up this compilation as much as reasonably possible.
|
||||
|
@@ -21,9 +21,9 @@ RUN mkdir /install \
|
||||
USER dspace
|
||||
# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents)
|
||||
ADD --chown=dspace . /app/
|
||||
# Build DSpace (INCLUDING the optional, deprecated "dspace-rest" webapp)
|
||||
# Build DSpace
|
||||
# Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small
|
||||
RUN mvn --no-transfer-progress package -Pdspace-rest && \
|
||||
RUN mvn --no-transfer-progress package && \
|
||||
mv /app/dspace/target/${TARGET_DIR}/* /install && \
|
||||
mvn clean
|
||||
|
||||
@@ -67,17 +67,10 @@ ENV CATALINA_OPTS=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:800
|
||||
|
||||
# Link the DSpace 'server' webapp into Tomcat's webapps directory.
|
||||
# This ensures that when we start Tomcat, it runs from /server path (e.g. http://localhost:8080/server/)
|
||||
# Also link the v6.x (deprecated) REST API off the "/rest" path
|
||||
RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server && \
|
||||
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest
|
||||
RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server
|
||||
# If you wish to run "server" webapp off the ROOT path, then comment out the above RUN, and uncomment the below RUN.
|
||||
# You also MUST update the 'dspace.server.url' configuration to match.
|
||||
# Please note that server webapp should only run on one path at a time.
|
||||
#RUN mv /usr/local/tomcat/webapps/ROOT /usr/local/tomcat/webapps/ROOT.bk && \
|
||||
# ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/ROOT && \
|
||||
# ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest
|
||||
# ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/ROOT
|
||||
|
||||
# Overwrite the v6.x (deprecated) REST API's web.xml, so that we can run it on HTTP (defaults to requiring HTTPS)
|
||||
# WARNING: THIS IS OBVIOUSLY INSECURE. NEVER DO THIS IN PRODUCTION.
|
||||
COPY dspace/src/main/docker/test/rest_web.xml $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
|
||||
RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
|
||||
|
@@ -1,5 +1,10 @@
|
||||
version: "3.7"
|
||||
|
||||
networks:
|
||||
# Default to using network named 'dspacenet' from docker-compose.yml.
|
||||
# Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet")
|
||||
default:
|
||||
name: ${COMPOSE_PROJECT_NAME}_dspacenet
|
||||
external: true
|
||||
services:
|
||||
dspace-cli:
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}"
|
||||
@@ -26,13 +31,8 @@ services:
|
||||
- ./dspace/config:/dspace/config
|
||||
entrypoint: /dspace/bin/dspace
|
||||
command: help
|
||||
networks:
|
||||
- dspacenet
|
||||
tty: true
|
||||
stdin_open: true
|
||||
|
||||
volumes:
|
||||
assetstore:
|
||||
|
||||
networks:
|
||||
dspacenet:
|
||||
|
@@ -36,7 +36,7 @@ services:
|
||||
depends_on:
|
||||
- dspacedb
|
||||
networks:
|
||||
dspacenet:
|
||||
- dspacenet
|
||||
ports:
|
||||
- published: 8080
|
||||
target: 8080
|
||||
@@ -89,8 +89,10 @@ services:
|
||||
container_name: dspacesolr
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./dspace/src/main/docker/dspace-solr/Dockerfile
|
||||
context: ./dspace/src/main/docker/dspace-solr/
|
||||
# Provide path to Solr configs necessary to build Docker image
|
||||
additional_contexts:
|
||||
solrconfigs: ./dspace/solr/
|
||||
args:
|
||||
SOLR_VERSION: "${SOLR_VER:-8.11}"
|
||||
networks:
|
||||
@@ -123,6 +125,8 @@ services:
|
||||
cp -r /opt/solr/server/solr/configsets/statistics/* statistics
|
||||
precreate-core qaevent /opt/solr/server/solr/configsets/qaevent
|
||||
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
|
||||
precreate-core suggestion /opt/solr/server/solr/configsets/suggestion
|
||||
cp -r /opt/solr/server/solr/configsets/suggestion/* suggestion
|
||||
exec solr -f
|
||||
volumes:
|
||||
assetstore:
|
||||
|
@@ -379,22 +379,6 @@
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.13.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<exclusions>
|
||||
<!-- Later version provided by dspace-api -->
|
||||
<exclusion>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.cache</groupId>
|
||||
<artifactId>cache-api</artifactId>
|
||||
@@ -549,7 +533,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -641,7 +625,7 @@
|
||||
<dependency>
|
||||
<groupId>com.maxmind.geoip2</groupId>
|
||||
<artifactId>geoip2</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
@@ -805,7 +789,7 @@
|
||||
<dependency>
|
||||
<groupId>com.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>5.7.1</version>
|
||||
<version>5.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Email templating -->
|
||||
@@ -892,32 +876,32 @@
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
@@ -927,7 +911,7 @@
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>2.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@@ -116,6 +116,17 @@ public final class CreateAdministrator {
|
||||
protected CreateAdministrator()
|
||||
throws Exception {
|
||||
context = new Context();
|
||||
try {
|
||||
context.getDBConfig();
|
||||
} catch (NullPointerException npr) {
|
||||
// if database is null, there is no point in continuing. Prior to this exception and catch,
|
||||
// NullPointerException was thrown, that wasn't very helpful.
|
||||
throw new IllegalStateException("Problem connecting to database. This " +
|
||||
"indicates issue with either network or version (or possibly some other). " +
|
||||
"If you are running this in docker-compose, please make sure dspace-cli was " +
|
||||
"built from the same sources as running dspace container AND that they are in " +
|
||||
"the same project/network.");
|
||||
}
|
||||
groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
public enum QueueStatus {
|
||||
public enum LDNMessageQueueStatus {
|
||||
|
||||
/**
|
||||
* Resulting processing status of an LDN Message (aka queue management)
|
@@ -8,7 +8,9 @@
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
/**
|
||||
*
|
||||
* Constants for LDN metadata fields
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public final class LDNMetadataFields {
|
||||
|
||||
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -32,17 +31,23 @@ public class LDNQueueExtractor {
|
||||
private LDNQueueExtractor() {
|
||||
}
|
||||
|
||||
public static int extractMessageFromQueue() throws IOException, SQLException {
|
||||
log.info("START LDNQueueExtractor.extractMessageFromQueue()");
|
||||
/**
|
||||
* invokes
|
||||
* @see org.dspace.app.ldn.service.impl.LDNMessageServiceImpl#extractAndProcessMessageFromQueue(Context)
|
||||
* to process the oldest ldn messages from the queue. An LdnMessage is processed when is routed to a
|
||||
* @see org.dspace.app.ldn.processor.LDNProcessor
|
||||
* Also a +1 is added to the ldnMessage entity
|
||||
* @see org.dspace.app.ldn.LDNMessageEntity#getQueueAttempts()
|
||||
* @return the number of processed ldnMessages.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int extractMessageFromQueue() throws SQLException {
|
||||
Context context = new Context(Context.Mode.READ_WRITE);
|
||||
int processed_messages = ldnMessageService.extractAndProcessMessageFromQueue(context);
|
||||
if (processed_messages >= 0) {
|
||||
if (processed_messages > 0) {
|
||||
log.info("Processed Messages x" + processed_messages);
|
||||
} else {
|
||||
log.error("Errors happened during the extract operations. Check the log above!");
|
||||
}
|
||||
context.complete();
|
||||
log.info("END LDNQueueExtractor.extractMessageFromQueue()");
|
||||
return processed_messages;
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -32,17 +31,22 @@ public class LDNQueueTimeoutChecker {
|
||||
private LDNQueueTimeoutChecker() {
|
||||
}
|
||||
|
||||
public static int checkQueueMessageTimeout() throws IOException, SQLException {
|
||||
log.info("START LDNQueueTimeoutChecker.checkQueueMessageTimeout()");
|
||||
/**
|
||||
* invokes
|
||||
* @see org.dspace.app.ldn.service.impl.LDNMessageServiceImpl#checkQueueMessageTimeout(Context)
|
||||
* to refresh the queue status of timed-out and in progressing status ldn messages:
|
||||
* according to their attempts put them back in queue or set their status as failed if maxAttempts
|
||||
* reached.
|
||||
* @return the number of managed ldnMessages.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int checkQueueMessageTimeout() throws SQLException {
|
||||
Context context = new Context(Context.Mode.READ_WRITE);
|
||||
int fixed_messages = 0;
|
||||
fixed_messages = ldnMessageService.checkQueueMessageTimeout(context);
|
||||
if (fixed_messages >= 0) {
|
||||
if (fixed_messages > 0) {
|
||||
log.info("Managed Messages x" + fixed_messages);
|
||||
} else {
|
||||
log.error("Errors happened during the check operation. Check the log above!");
|
||||
}
|
||||
log.info("END LDNQueueTimeoutChecker.checkQueueMessageTimeout()");
|
||||
context.complete();
|
||||
return fixed_messages;
|
||||
}
|
||||
|
@@ -32,11 +32,11 @@ public class LDNRouter {
|
||||
*/
|
||||
public LDNProcessor route(LDNMessageEntity ldnMessage) {
|
||||
if (ldnMessage == null) {
|
||||
log.warn("an null LDNMessage is received for routing!");
|
||||
log.warn("A null LDNMessage was received and could not be routed.");
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isEmpty(ldnMessage.getType())) {
|
||||
log.warn("LDNMessage " + ldnMessage + " has no type!");
|
||||
log.warn("LDNMessage " + ldnMessage + " was received. It has no type, so it couldn't be routed.");
|
||||
return null;
|
||||
}
|
||||
Set<String> ldnMessageTypeSet = new HashSet<String>();
|
||||
|
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
import static java.nio.charset.Charset.defaultCharset;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RdfMediaType {
|
||||
|
||||
public final static MediaType APPLICATION_JSON_LD = new MediaType("application", "ld+json", defaultCharset());
|
||||
public final static MediaType APPLICATION_N_TRIPLES = new MediaType("application", "n-triples", defaultCharset());
|
||||
public final static MediaType APPLICATION_RDF_XML = new MediaType("application", "rdf+xml", defaultCharset());
|
||||
public final static MediaType APPLICATION_RDF_JSON = new MediaType("application", "rdf+json", defaultCharset());
|
||||
public final static MediaType APPLICATION_X_TURTLE = new MediaType("application", "x-turtle", defaultCharset());
|
||||
|
||||
public final static MediaType TEXT_TURTLE = new MediaType("text", "turtle", defaultCharset());
|
||||
public final static MediaType TEXT_N3 = new MediaType("text", "n3", defaultCharset());
|
||||
public final static MediaType TEXT_RDF_N3 = new MediaType("text", "rdf+n3", defaultCharset());
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private RdfMediaType() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -26,6 +26,6 @@ public interface LDNAction {
|
||||
* @return ActionStatus the resulting status of the action
|
||||
* @throws Exception general exception that can be thrown while executing action
|
||||
*/
|
||||
public ActionStatus execute(Context context, Notification notification, Item item) throws Exception;
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception;
|
||||
|
||||
}
|
@@ -10,6 +10,6 @@ package org.dspace.app.ldn.action;
|
||||
/**
|
||||
* Resulting status of an execution of an action.
|
||||
*/
|
||||
public enum ActionStatus {
|
||||
public enum LDNActionStatus {
|
||||
CONTINUE, ABORT;
|
||||
}
|
@@ -11,7 +11,7 @@ import java.math.BigDecimal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.github.jsonldjava.utils.JsonUtils;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
@@ -51,8 +51,8 @@ public class LDNCorrectionAction implements LDNAction {
|
||||
private HandleService handleService;
|
||||
|
||||
@Override
|
||||
public ActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
ActionStatus result = ActionStatus.ABORT;
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
String itemName = itemService.getName(item);
|
||||
QAEvent qaEvent = null;
|
||||
if (notification.getObject() != null) {
|
||||
@@ -69,15 +69,14 @@ public class LDNCorrectionAction implements LDNAction {
|
||||
}
|
||||
BigDecimal score = getScore(context, notification);
|
||||
double doubleScoreValue = score != null ? score.doubleValue() : 0d;
|
||||
/* String fullHandleUrl = configurationService.getProperty("dspace.ui.url") + "/handle/"
|
||||
+ handleService.findHandle(context, item); */
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
qaEvent = new QAEvent(QAEvent.COAR_NOTIFY_SOURCE,
|
||||
handleService.findHandle(context, item), item.getID().toString(), itemName,
|
||||
this.getQaEventTopic(), doubleScoreValue,
|
||||
JsonUtils.toString(message)
|
||||
, new Date());
|
||||
mapper.writeValueAsString(message),
|
||||
new Date());
|
||||
qaEventService.store(context, qaEvent);
|
||||
result = ActionStatus.CONTINUE;
|
||||
result = LDNActionStatus.CONTINUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@@ -70,7 +70,7 @@ public class LDNEmailAction implements LDNAction {
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public ActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
try {
|
||||
Locale supportedLocale = I18nUtil.getEPersonLocale(context.getCurrentUser());
|
||||
Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, actionSendEmailTextFile));
|
||||
@@ -85,7 +85,8 @@ public class LDNEmailAction implements LDNAction {
|
||||
email.addArgument(notification.getActor().getName());
|
||||
email.addArgument(item.getName());
|
||||
email.addArgument(notification.getActor().getId());
|
||||
email.addArgument(notification.getContext().getId());
|
||||
email.addArgument(notification.getContext() != null ?
|
||||
notification.getContext().getId() : notification.getObject().getId());
|
||||
email.addArgument(item.getSubmitter().getFullName());
|
||||
email.addArgument(date);
|
||||
email.addArgument(notification);
|
||||
@@ -96,7 +97,7 @@ public class LDNEmailAction implements LDNAction {
|
||||
log.error("An Error Occurred while sending a notification email", e);
|
||||
}
|
||||
|
||||
return ActionStatus.CONTINUE;
|
||||
return LDNActionStatus.CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -11,7 +11,7 @@ import java.math.BigDecimal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.github.jsonldjava.utils.JsonUtils;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
@@ -51,14 +51,12 @@ public class LDNRelationCorrectionAction implements LDNAction {
|
||||
private HandleService handleService;
|
||||
|
||||
@Override
|
||||
public ActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
ActionStatus result = ActionStatus.ABORT;
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
String itemName = itemService.getName(item);
|
||||
QAEvent qaEvent = null;
|
||||
if (notification.getObject() != null) {
|
||||
NotifyMessageDTO message = new NotifyMessageDTO();
|
||||
/*relationFormat.replace("[0]", notification.getObject().getAsRelationship());
|
||||
hrefValue = hrefValue.replace("[1]", notification.getObject().getAsSubject());*/
|
||||
message.setHref(notification.getObject().getAsSubject());
|
||||
message.setRelationship(notification.getObject().getAsRelationship());
|
||||
if (notification.getOrigin() != null) {
|
||||
@@ -67,13 +65,14 @@ public class LDNRelationCorrectionAction implements LDNAction {
|
||||
}
|
||||
BigDecimal score = getScore(context, notification);
|
||||
double doubleScoreValue = score != null ? score.doubleValue() : 0d;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
qaEvent = new QAEvent(QAEvent.COAR_NOTIFY_SOURCE,
|
||||
handleService.findHandle(context, item), item.getID().toString(), itemName,
|
||||
this.getQaEventTopic(), doubleScoreValue,
|
||||
JsonUtils.toString(message)
|
||||
, new Date());
|
||||
mapper.writeValueAsString(message),
|
||||
new Date());
|
||||
qaEventService.store(context, qaEvent);
|
||||
result = ActionStatus.CONTINUE;
|
||||
result = LDNActionStatus.CONTINUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@@ -7,23 +7,23 @@
|
||||
*/
|
||||
package org.dspace.app.ldn.action;
|
||||
|
||||
import static org.dspace.app.ldn.RdfMediaType.APPLICATION_JSON_LD;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.net.URI;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.converter.JsonLdHttpMessageConverter;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Action to send LDN Message
|
||||
@@ -34,75 +34,85 @@ public class SendLDNMessageAction implements LDNAction {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(SendLDNMessageAction.class);
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private CloseableHttpClient client = null;
|
||||
|
||||
public SendLDNMessageAction() {
|
||||
restTemplate = new RestTemplate();
|
||||
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
messageConverters.add(new JsonLdHttpMessageConverter());
|
||||
messageConverters.addAll(restTemplate.getMessageConverters());
|
||||
restTemplate.setMessageConverters(messageConverters);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
client = builder
|
||||
.disableAutomaticRetries()
|
||||
.setMaxConnTotal(5)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
//TODO authorization with Bearer token should be supported.
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Content-Type", APPLICATION_JSON_LD.toString());
|
||||
|
||||
HttpEntity<Notification> request = new HttpEntity<>(notification, headers);
|
||||
|
||||
log.info("Announcing notification {}", request);
|
||||
|
||||
try {
|
||||
//Server-side request forgery Critical check gitHub failure is a false positive,
|
||||
//because the LDN Service URL is configured by the user from DSpace
|
||||
//frontend configuration at /admin/ldn/services
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(
|
||||
notification.getTarget().getInbox(),
|
||||
request,
|
||||
String.class);
|
||||
|
||||
if (isSuccessful(response.getStatusCode())) {
|
||||
return ActionStatus.CONTINUE;
|
||||
} else if (isRedirect(response.getStatusCode())) {
|
||||
return handleRedirect(response, request);
|
||||
} else {
|
||||
return ActionStatus.ABORT;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
return ActionStatus.ABORT;
|
||||
public SendLDNMessageAction(CloseableHttpClient client) {
|
||||
this();
|
||||
if (client != null) {
|
||||
this.client = client;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSuccessful(HttpStatus statusCode) {
|
||||
return statusCode == HttpStatus.ACCEPTED ||
|
||||
statusCode == HttpStatus.CREATED;
|
||||
@Override
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
//TODO authorization with Bearer token should be supported.
|
||||
|
||||
String url = notification.getTarget().getInbox();
|
||||
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.addHeader("Content-Type", "application, ld+json");
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
httpPost.setEntity(new StringEntity(mapper.writeValueAsString(notification), "UTF-8"));
|
||||
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
// NOTE: Github believes there is a "Potential server-side request forgery due to a user-provided value"
|
||||
// This is a false positive because the LDN Service URL is configured by the user from DSpace.
|
||||
// See the frontend configuration at [dspace.ui.url]/admin/ldn/services
|
||||
try (
|
||||
CloseableHttpResponse response = client.execute(httpPost);
|
||||
) {
|
||||
if (isSuccessful(response.getStatusLine().getStatusCode())) {
|
||||
result = LDNActionStatus.CONTINUE;
|
||||
} else if (isRedirect(response.getStatusLine().getStatusCode())) {
|
||||
result = handleRedirect(response, httpPost);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isRedirect(HttpStatus statusCode) {
|
||||
return statusCode == HttpStatus.PERMANENT_REDIRECT ||
|
||||
statusCode == HttpStatus.TEMPORARY_REDIRECT;
|
||||
private boolean isSuccessful(int statusCode) {
|
||||
return statusCode == HttpStatus.SC_ACCEPTED ||
|
||||
statusCode == HttpStatus.SC_CREATED;
|
||||
}
|
||||
|
||||
private ActionStatus handleRedirect(ResponseEntity<String> response,
|
||||
HttpEntity<Notification> request) {
|
||||
|
||||
String url = response.getHeaders().getFirst(HttpHeaders.LOCATION);
|
||||
private boolean isRedirect(int statusCode) {
|
||||
//org.apache.http.HttpStatus has no enum value for 308!
|
||||
return statusCode == (HttpStatus.SC_TEMPORARY_REDIRECT + 1) ||
|
||||
statusCode == HttpStatus.SC_TEMPORARY_REDIRECT;
|
||||
}
|
||||
|
||||
private LDNActionStatus handleRedirect(CloseableHttpResponse oldresponse,
|
||||
HttpPost request) throws HttpException {
|
||||
Header[] urls = oldresponse.getHeaders(HttpHeaders.LOCATION);
|
||||
String url = urls.length > 0 && urls[0] != null ? urls[0].getValue() : null;
|
||||
if (url == null) {
|
||||
throw new HttpException("Error following redirect, unable to reach"
|
||||
+ " the correct url.");
|
||||
}
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
try {
|
||||
ResponseEntity<String> responseEntity =
|
||||
restTemplate.postForEntity(url, request, String.class);
|
||||
|
||||
if (isSuccessful(responseEntity.getStatusCode())) {
|
||||
return ActionStatus.CONTINUE;
|
||||
request.setURI(new URI(url));
|
||||
try (
|
||||
CloseableHttpResponse response = client.execute(request);
|
||||
) {
|
||||
if (isSuccessful(response.getStatusLine().getStatusCode())) {
|
||||
return LDNActionStatus.CONTINUE;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error following redirect:", e);
|
||||
}
|
||||
|
||||
return ActionStatus.ABORT;
|
||||
return LDNActionStatus.ABORT;
|
||||
}
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.converter;
|
||||
|
||||
import static org.dspace.app.ldn.RdfMediaType.APPLICATION_JSON_LD;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.github.jsonldjava.utils.JsonUtils;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
|
||||
/**
|
||||
* Message converter for JSON-LD notification request body.
|
||||
*/
|
||||
public class JsonLdHttpMessageConverter extends AbstractHttpMessageConverter<Notification> {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Initialize object mapper to normalize arrays on
|
||||
* serialization/deserialization.
|
||||
*/
|
||||
public JsonLdHttpMessageConverter() {
|
||||
super(APPLICATION_JSON_LD);
|
||||
objectMapper = new ObjectMapper();
|
||||
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
objectMapper.enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
|
||||
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct message converter to convert notification object.
|
||||
*
|
||||
* @param clazz current class pending conversion
|
||||
* @return boolean whether to convert the object with this converter
|
||||
*/
|
||||
@Override
|
||||
protected boolean supports(Class<?> clazz) {
|
||||
return Notification.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert input stream, primarily request body, to notification.
|
||||
*
|
||||
* @param clazz notification class
|
||||
* @param inputMessage input message with body to convert
|
||||
*
|
||||
* @return Notification deserialized notification
|
||||
*
|
||||
* @throws IOException failed to convert input stream
|
||||
* @throws HttpMessageNotReadableException something wrong with the input
|
||||
* message
|
||||
*/
|
||||
@Override
|
||||
protected Notification readInternal(Class<? extends Notification> clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
try (InputStream in = inputMessage.getBody()) {
|
||||
return objectMapper.convertValue(JsonUtils.fromInputStream(in), Notification.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert notification to output stream, primarily response body.
|
||||
*
|
||||
* @param notification notification to convert
|
||||
* @param outputMessage output message with serialized notification
|
||||
* @throws IOException failed to convert notification
|
||||
* @throws HttpMessageNotWritableException something wrong with the output
|
||||
* message
|
||||
*/
|
||||
@Override
|
||||
protected void writeInternal(Notification notification, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
try (OutputStreamWriter out = new OutputStreamWriter(outputMessage.getBody())) {
|
||||
JsonUtils.write(out, notification);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getRdfType() {
|
||||
return "JSON-LD";
|
||||
}
|
||||
|
||||
}
|
@@ -25,13 +25,44 @@ import org.dspace.core.GenericDAO;
|
||||
*/
|
||||
public interface LDNMessageDao extends GenericDAO<LDNMessageEntity> {
|
||||
|
||||
/**
|
||||
* load the oldest ldn messages considering their {@link org.dspace.app.ldn.LDNMessageEntity#queueLastStartTime}
|
||||
* @param context
|
||||
* @param max_attempts consider ldn_message entity with queue_attempts <= max_attempts
|
||||
* @return ldn message entities to be routed
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findOldestMessageToProcess(Context context, int max_attempts) throws SQLException;
|
||||
|
||||
/**
|
||||
* find ldn message entties in processing status and already timed out.
|
||||
* @param context
|
||||
* @param max_attempts consider ldn_message entity with queue_attempts <= max_attempts
|
||||
* @return ldn message entities
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findProcessingTimedoutMessages(Context context, int max_attempts) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all ldn messages related to an item
|
||||
* @param context
|
||||
* @param item item related to the returned ldn messages
|
||||
* @param activities involves only this specific group of activities
|
||||
* @return all ldn messages related to the given item
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findAllMessagesByItem(
|
||||
Context context, Item item, String... activities) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all ldn messages related to an item and to a specific ldn message
|
||||
* @param context
|
||||
* @param msg the referring ldn message
|
||||
* @param item the referring repository item
|
||||
* @param relatedTypes filter for @see org.dspace.app.ldn.LDNMessageEntity#activityStreamType
|
||||
* @return all related ldn messages
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findAllRelatedMessagesByItem(
|
||||
Context context, LDNMessageEntity msg, Item item, String... relatedTypes) throws SQLException;
|
||||
|
||||
|
@@ -39,7 +39,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findOldestMessageToProcess(Context context, int max_attempts) throws SQLException {
|
||||
// looking for oldest failed-processed message
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<LDNMessageEntity> criteriaQuery = getCriteriaQuery(criteriaBuilder, LDNMessageEntity.class);
|
||||
Root<LDNMessageEntity> root = criteriaQuery.from(LDNMessageEntity.class);
|
||||
@@ -54,7 +53,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
// setHint("org.hibernate.cacheable", Boolean.FALSE);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found to be processed");
|
||||
@@ -102,7 +100,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
// setHint("org.hibernate.cacheable", Boolean.FALSE);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found to be processed");
|
||||
@@ -121,8 +118,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
Predicate relatedtypePredicate = null;
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus), LDNMessageEntity.QUEUE_STATUS_PROCESSED));
|
||||
/*andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.object), item));*/
|
||||
andPredicates.add(
|
||||
criteriaBuilder.isNull(root.get(LDNMessageEntity_.target)));
|
||||
andPredicates.add(
|
||||
@@ -136,7 +131,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
// setHint("org.hibernate.cacheable", Boolean.FALSE);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages ACK found to be processed");
|
||||
@@ -157,8 +151,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus), LDNMessageEntity.QUEUE_STATUS_PROCESSED));
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.object), item));
|
||||
andPredicates.add(
|
||||
criteriaBuilder.isNull(root.get(LDNMessageEntity_.origin)));
|
||||
if (activities != null && activities.length > 0) {
|
||||
activityPredicate = root.get(LDNMessageEntity_.activityStreamType).in(activities);
|
||||
andPredicates.add(activityPredicate);
|
||||
@@ -168,7 +160,6 @@ public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> im
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
// setHint("org.hibernate.cacheable", Boolean.FALSE);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found");
|
||||
|
@@ -10,7 +10,7 @@ package org.dspace.app.ldn.model;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Actor extends Base {
|
||||
|
||||
|
@@ -17,7 +17,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
@@ -9,8 +9,9 @@ package org.dspace.app.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Citation extends Base {
|
||||
|
||||
|
@@ -11,8 +11,9 @@ import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Context extends Citation {
|
||||
|
||||
|
@@ -11,7 +11,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
*
|
||||
* the json object from witch @see org.dspace.app.ldn.LDNMessageEntity are created.
|
||||
* <a href="https://notify.coar-repositories.org/patterns/">see official coar doc</a>
|
||||
*/
|
||||
@JsonPropertyOrder(value = {
|
||||
"@context",
|
||||
|
@@ -10,7 +10,7 @@ package org.dspace.app.ldn.model;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Object extends Citation {
|
||||
|
||||
|
@@ -10,7 +10,7 @@ package org.dspace.app.ldn.model;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Service extends Base {
|
||||
|
||||
|
@@ -10,7 +10,7 @@ package org.dspace.app.ldn.model;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* used to map @see org.dspace.app.ldn.model.Citation
|
||||
*/
|
||||
public class Url extends Base {
|
||||
|
||||
|
@@ -50,7 +50,7 @@ public class LDNContextRepeater {
|
||||
|
||||
/**
|
||||
* @param notification
|
||||
* @return Iterator<Notification>
|
||||
* @return Iterator<Notification>
|
||||
*/
|
||||
public Iterator<Notification> iterator(Notification notification) {
|
||||
return new NotificationIterator(notification, repeatOver);
|
||||
@@ -79,7 +79,7 @@ public class LDNContextRepeater {
|
||||
|
||||
JsonNode notificationNode = objectMapper.valueToTree(notification);
|
||||
|
||||
log.info("Notification {}", notificationNode);
|
||||
log.debug("Notification {}", notificationNode);
|
||||
|
||||
JsonNode topContextNode = notificationNode.get(CONTEXT);
|
||||
if (topContextNode.isNull()) {
|
||||
|
@@ -15,10 +15,12 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.action.ActionStatus;
|
||||
import org.dspace.app.ldn.action.LDNAction;
|
||||
import org.dspace.app.ldn.action.LDNActionStatus;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.utility.LDNUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
@@ -28,8 +30,6 @@ import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
/**
|
||||
* Linked Data Notification metadata processor for consuming notifications. The
|
||||
@@ -79,8 +79,8 @@ public class LDNMetadataProcessor implements LDNProcessor {
|
||||
*
|
||||
* @throws Exception failed execute the action
|
||||
*/
|
||||
private ActionStatus runActions(Context context, Notification notification, Item item) throws Exception {
|
||||
ActionStatus operation = ActionStatus.CONTINUE;
|
||||
private LDNActionStatus runActions(Context context, Notification notification, Item item) throws Exception {
|
||||
LDNActionStatus operation = LDNActionStatus.CONTINUE;
|
||||
for (LDNAction action : actions) {
|
||||
log.info("Running action {} for notification {} {}",
|
||||
action.getClass().getSimpleName(),
|
||||
@@ -88,7 +88,7 @@ public class LDNMetadataProcessor implements LDNProcessor {
|
||||
notification.getType());
|
||||
|
||||
operation = action.execute(context, notification, item);
|
||||
if (operation == ActionStatus.ABORT) {
|
||||
if (operation == LDNActionStatus.ABORT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -134,8 +134,9 @@ public class LDNMetadataProcessor implements LDNProcessor {
|
||||
* @return Item associated item
|
||||
*
|
||||
* @throws SQLException failed to lookup item
|
||||
* @throws HttpResponseException redirect failure
|
||||
*/
|
||||
private Item lookupItem(Context context, Notification notification) throws SQLException {
|
||||
private Item lookupItem(Context context, Notification notification) throws SQLException, HttpResponseException {
|
||||
Item item = null;
|
||||
|
||||
String url = null;
|
||||
@@ -153,7 +154,7 @@ public class LDNMetadataProcessor implements LDNProcessor {
|
||||
item = itemService.find(context, uuid);
|
||||
|
||||
if (Objects.isNull(item)) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
throw new HttpResponseException(HttpStatus.SC_NOT_FOUND,
|
||||
format("Item with uuid %s not found", uuid));
|
||||
}
|
||||
|
||||
@@ -161,21 +162,21 @@ public class LDNMetadataProcessor implements LDNProcessor {
|
||||
String handle = handleService.resolveUrlToHandle(context, url);
|
||||
|
||||
if (Objects.isNull(handle)) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
throw new HttpResponseException(HttpStatus.SC_NOT_FOUND,
|
||||
format("Handle not found for %s", url));
|
||||
}
|
||||
|
||||
DSpaceObject object = handleService.resolveToObject(context, handle);
|
||||
|
||||
if (Objects.isNull(object)) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
throw new HttpResponseException(HttpStatus.SC_NOT_FOUND,
|
||||
format("Item with handle %s not found", handle));
|
||||
}
|
||||
|
||||
if (object.getType() == Constants.ITEM) {
|
||||
item = (Item) object;
|
||||
} else {
|
||||
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
throw new HttpResponseException(HttpStatus.SC_UNPROCESSABLE_ENTITY,
|
||||
format("Handle %s does not resolve to an item", handle));
|
||||
}
|
||||
}
|
||||
|
@@ -128,6 +128,7 @@ public interface LDNMessageService {
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public NotifyRequestStatus findRequestsByItem(Context context, Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* delete the provided ldn message
|
||||
*
|
||||
@@ -144,4 +145,13 @@ public interface LDNMessageService {
|
||||
* @throws SQLException if something goes wrong
|
||||
*/
|
||||
public List<LDNMessageEntity> findMessagesToBeReprocessed(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* check if IP number is included in the configured ip-range on the Notify
|
||||
* Service
|
||||
*
|
||||
* @param origin the Notify Service entity
|
||||
* @param sourceIp the ip to evaluate
|
||||
*/
|
||||
public boolean isValidIp(NotifyServiceEntity origin, String sourceIp);
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import java.util.UUID;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -93,6 +94,10 @@ public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
|
||||
@Override
|
||||
public LDNMessageEntity create(Context context, String id) throws SQLException {
|
||||
LDNMessageEntity result = ldnMessageDao.findByID(context, LDNMessageEntity.class, id);
|
||||
if (result != null) {
|
||||
throw new SQLException("Duplicate LDN Message ID [" + id + "] detected. This message is rejected.");
|
||||
}
|
||||
return ldnMessageDao.create(context, new LDNMessageEntity(id));
|
||||
}
|
||||
|
||||
@@ -104,7 +109,6 @@ public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
ldnMessage.setContext(findDspaceObjectByUrl(context, notification.getContext().getId()));
|
||||
}
|
||||
ldnMessage.setOrigin(findNotifyService(context, notification.getOrigin()));
|
||||
ldnMessage.setTarget(findNotifyService(context, notification.getTarget()));
|
||||
ldnMessage.setInReplyTo(find(context, notification.getInReplyTo()));
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String message = null;
|
||||
@@ -130,38 +134,30 @@ public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
ldnMessage.setCoarNotifyType(notificationTypeArrayList.get(1));
|
||||
}
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED);
|
||||
//CST-12126 if source is untrusted, set the queue_status of the
|
||||
//ldnMsgEntity to UNTRUSTED
|
||||
ldnMessage.setSourceIp(sourceIp);
|
||||
if (ldnMessage.getOrigin() == null && !"Offer".equalsIgnoreCase(ldnMessage.getActivityStreamType())) {
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_UNTRUSTED);
|
||||
} else {
|
||||
|
||||
boolean ipCheckRangeEnabled = configurationService.getBooleanProperty("ldn.ip-range.enabled", true);
|
||||
if (ipCheckRangeEnabled && !isValidIp(ldnMessage.getOrigin(), sourceIp)) {
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_UNTRUSTED_IP);
|
||||
}
|
||||
}
|
||||
ldnMessage.setQueueTimeout(new Date());
|
||||
ldnMessage.setSourceIp(sourceIp);
|
||||
|
||||
if (!isValidIp(ldnMessage)) {
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_UNTRUSTED_IP);
|
||||
}
|
||||
|
||||
update(context, ldnMessage);
|
||||
return ldnMessage;
|
||||
}
|
||||
|
||||
private boolean isValidIp(LDNMessageEntity message) {
|
||||
@Override
|
||||
public boolean isValidIp(NotifyServiceEntity origin, String sourceIp) {
|
||||
|
||||
boolean enabled = configurationService.getBooleanProperty("coar-notify.ip-range.enabled", true);
|
||||
|
||||
if (!enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
NotifyServiceEntity notifyService =
|
||||
message.getOrigin() == null ? message.getTarget() : message.getOrigin();
|
||||
|
||||
String lowerIp = notifyService.getLowerIp();
|
||||
String upperIp = notifyService.getUpperIp();
|
||||
String lowerIp = origin.getLowerIp();
|
||||
String upperIp = origin.getUpperIp();
|
||||
|
||||
try {
|
||||
InetAddress ip = InetAddress.getByName(message.getSourceIp());
|
||||
InetAddress ip = InetAddress.getByName(sourceIp);
|
||||
InetAddress lowerBoundAddress = InetAddress.getByName(lowerIp);
|
||||
InetAddress upperBoundAddress = InetAddress.getByName(upperIp);
|
||||
|
||||
@@ -322,7 +318,7 @@ public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
}
|
||||
log.debug("Using parameters: [timeoutInMinutes]=" + timeoutInMinutes + ",[maxAttempts]=" + maxAttempts);
|
||||
/*
|
||||
* CST-10631 put failed on processing messages with timed-out timeout and
|
||||
* put failed on processing messages with timed-out timeout and
|
||||
* attempts >= configured_max_attempts put queue on processing messages with
|
||||
* timed-out timeout and attempts < configured_max_attempts
|
||||
*/
|
||||
@@ -335,7 +331,6 @@ public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
return result;
|
||||
}
|
||||
if (msgsToCheck == null || msgsToCheck.isEmpty()) {
|
||||
log.info("No timedout LDN messages found in queue.");
|
||||
return result;
|
||||
}
|
||||
for (int i = 0; i < msgsToCheck.size() && msgsToCheck.get(i) != null; i++) {
|
||||
@@ -365,8 +360,8 @@ public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
if (msgs != null && !msgs.isEmpty()) {
|
||||
for (LDNMessageEntity msg : msgs) {
|
||||
RequestStatus offer = new RequestStatus();
|
||||
offer.setServiceName(msg.getTarget() == null ? "Unknown Service" : msg.getTarget().getName());
|
||||
offer.setServiceUrl(msg.getTarget() == null ? "" : msg.getTarget().getUrl());
|
||||
offer.setServiceName(msg.getOrigin() == null ? "Unknown Service" : msg.getOrigin().getName());
|
||||
offer.setServiceUrl(msg.getOrigin() == null ? "" : msg.getOrigin().getUrl());
|
||||
offer.setOfferType(LDNUtils.getNotifyType(msg.getCoarNotifyType()));
|
||||
List<LDNMessageEntity> acks = ldnMessageDao.findAllRelatedMessagesByItem(
|
||||
context, msg, item, "Accept", "TentativeReject", "TentativeAccept", "Announce");
|
||||
|
@@ -129,7 +129,7 @@ public abstract class SolrSuggestionProvider implements SuggestionProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* check if the externalDataObject may have suggestion
|
||||
* @param context
|
||||
* @param externalDataObject
|
||||
* @return true if the externalDataObject could be suggested by this provider
|
||||
|
@@ -10,18 +10,23 @@ package org.dspace.app.suggestion;
|
||||
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.apache.solr.client.solrj.SolrQuery;
|
||||
import org.apache.solr.client.solrj.SolrQuery.SortClause;
|
||||
@@ -33,6 +38,7 @@ import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.apache.solr.common.params.FacetParams;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
import org.dspace.content.service.ItemService;
|
||||
@@ -41,6 +47,7 @@ import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.util.UUIDUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
||||
/**
|
||||
* Service to deal with the local suggestion solr core used by the
|
||||
* SolrSuggestionProvider(s)
|
||||
@@ -50,6 +57,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
*/
|
||||
public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageService {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(SolrSuggestionStorageServiceImpl.class);
|
||||
|
||||
protected SolrClient solrSuggestionClient;
|
||||
|
||||
@Autowired
|
||||
@@ -73,9 +82,12 @@ public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageSe
|
||||
public void addSuggestion(Suggestion suggestion, boolean force, boolean commit)
|
||||
throws SolrServerException, IOException {
|
||||
if (force || !exist(suggestion)) {
|
||||
Gson gson = new Gson();
|
||||
ObjectMapper jsonMapper = new JsonMapper();
|
||||
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
SolrInputDocument document = new SolrInputDocument();
|
||||
document.addField(SOURCE, suggestion.getSource());
|
||||
// suggestion id is written as concatenation of
|
||||
// source + ":" + targetID + ":" + idPart (of externalDataObj)
|
||||
String suggestionFullID = suggestion.getID();
|
||||
document.addField(SUGGESTION_FULLID, suggestionFullID);
|
||||
document.addField(SUGGESTION_ID, suggestionFullID.split(":", 3)[2]);
|
||||
@@ -89,7 +101,7 @@ public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageSe
|
||||
document.addField(EXTERNAL_URI, suggestion.getExternalSourceUri());
|
||||
document.addField(SCORE, suggestion.getScore());
|
||||
document.addField(PROCESSED, false);
|
||||
document.addField(EVIDENCES, gson.toJson(suggestion.getEvidences()));
|
||||
document.addField(EVIDENCES, jsonMapper.writeValueAsString(suggestion.getEvidences()));
|
||||
getSolr().add(document);
|
||||
if (commit) {
|
||||
getSolr().commit();
|
||||
@@ -240,16 +252,13 @@ public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageSe
|
||||
solrQuery.setFacet(true);
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.addFacetField(TARGET_ID);
|
||||
solrQuery.setFacetLimit((int) (pageSize + offset));
|
||||
solrQuery.setParam(FacetParams.FACET_OFFSET, String.valueOf(offset));
|
||||
solrQuery.setFacetLimit((int) (pageSize));
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
FacetField facetField = response.getFacetField(TARGET_ID);
|
||||
List<SuggestionTarget> suggestionTargets = new ArrayList<SuggestionTarget>();
|
||||
int idx = 0;
|
||||
for (Count c : facetField.getValues()) {
|
||||
if (idx < offset) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
SuggestionTarget target = new SuggestionTarget();
|
||||
target.setSource(source);
|
||||
target.setTotal((int) c.getCount());
|
||||
@@ -326,9 +335,14 @@ public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageSe
|
||||
}
|
||||
}
|
||||
String evidencesJson = (String) solrDoc.getFieldValue(EVIDENCES);
|
||||
Type listType = new TypeToken<ArrayList<SuggestionEvidence>>() {
|
||||
}.getType();
|
||||
List<SuggestionEvidence> evidences = new Gson().fromJson(evidencesJson, listType);
|
||||
ObjectMapper jsonMapper = new JsonMapper();
|
||||
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
List<SuggestionEvidence> evidences = new LinkedList<SuggestionEvidence>();
|
||||
try {
|
||||
evidences = jsonMapper.readValue(evidencesJson, new TypeReference<List<SuggestionEvidence>>() {});
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error(e);
|
||||
}
|
||||
suggestion.getEvidences().addAll(evidences);
|
||||
return suggestion;
|
||||
}
|
||||
|
@@ -14,26 +14,36 @@ import org.dspace.content.Item;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
|
||||
/**
|
||||
*
|
||||
* This entity contains metadatas that should be added to the targeted Item
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class Suggestion {
|
||||
|
||||
/** id of the suggestion */
|
||||
private String id;
|
||||
|
||||
/** the dc.title of the item */
|
||||
private String display;
|
||||
|
||||
/** the external source name the suggestion comes from */
|
||||
private String source;
|
||||
|
||||
/** external uri of the item */
|
||||
private String externalSourceUri;
|
||||
|
||||
/** item targeted by this suggestion */
|
||||
private Item target;
|
||||
|
||||
private List<SuggestionEvidence> evidences = new LinkedList<SuggestionEvidence>();
|
||||
|
||||
private List<MetadataValueDTO> metadata = new LinkedList<MetadataValueDTO>();
|
||||
|
||||
/** suggestion creation
|
||||
* @param source name of the external source
|
||||
* @param target the targeted item in repository
|
||||
* @param idPart external item id, used mainly for suggestion @see #id creation
|
||||
* */
|
||||
public Suggestion(String source, Item target, String idPart) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
|
@@ -8,16 +8,21 @@
|
||||
package org.dspace.app.suggestion;
|
||||
|
||||
/**
|
||||
*
|
||||
* This DTO class is returned by an {@link org.dspace.app.suggestion.openaire.EvidenceScorer} to model the concept of
|
||||
* an evidence / fact that has been used to evaluate the precision of a suggestion increasing or decreasing the score
|
||||
* of the suggestion.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionEvidence {
|
||||
|
||||
/** name of the evidence */
|
||||
private String name;
|
||||
|
||||
/** positive or negative value to influence the score of the suggestion */
|
||||
private double score;
|
||||
|
||||
/** additional notes */
|
||||
private String notes;
|
||||
|
||||
public SuggestionEvidence() {
|
||||
|
@@ -13,22 +13,42 @@ import java.util.UUID;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Interface for suggestion management like finding and counting.
|
||||
* @see org.dspace.app.suggestion.SuggestionTarget
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*
|
||||
*/
|
||||
public interface SuggestionProvider {
|
||||
|
||||
/** find all suggestion targets
|
||||
* @see org.dspace.app.suggestion.SuggestionTarget
|
||||
* */
|
||||
public List<SuggestionTarget> findAllTargets(Context context, int pageSize, long offset);
|
||||
|
||||
/** count all suggestion targets */
|
||||
public long countAllTargets(Context context);
|
||||
|
||||
/** find a suggestion target by UUID */
|
||||
public SuggestionTarget findTarget(Context context, UUID target);
|
||||
|
||||
/** find unprocessed suggestions (paged) by target UUID
|
||||
* @see org.dspace.app.suggestion.Suggestion
|
||||
* */
|
||||
public List<Suggestion> findAllUnprocessedSuggestions(Context context, UUID target, int pageSize, long offset,
|
||||
boolean ascending);
|
||||
|
||||
/** find unprocessed suggestions by target UUID */
|
||||
public long countUnprocessedSuggestionByTarget(Context context, UUID target);
|
||||
|
||||
/** find an unprocessed suggestion by target UUID and suggestion id */
|
||||
public Suggestion findUnprocessedSuggestion(Context context, UUID target, String id);
|
||||
|
||||
/** reject a specific suggestion by target @param target and by suggestion id @param idPart */
|
||||
public void rejectSuggestion(Context context, UUID target, String idPart);
|
||||
|
||||
/** flag a suggestion as processed */
|
||||
public void flagRelatedSuggestionsAsProcessed(Context context, ExternalDataObject externalDataObject);
|
||||
|
||||
}
|
||||
|
@@ -13,36 +13,49 @@ import java.util.UUID;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
*
|
||||
* Service that handles {@link Suggestion}.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public interface SuggestionService {
|
||||
|
||||
/** find a {@link SuggestionTarget } by source name and suggestion id */
|
||||
public SuggestionTarget find(Context context, String source, UUID id);
|
||||
|
||||
/** count all suggetion targets by suggestion source */
|
||||
public long countAll(Context context, String source);
|
||||
|
||||
/** find all suggestion targets by source (paged) */
|
||||
public List<SuggestionTarget> findAllTargets(Context context, String source, int pageSize, long offset);
|
||||
|
||||
/** count all (unprocessed) suggestions by the given target uuid */
|
||||
public long countAllByTarget(Context context, UUID target);
|
||||
|
||||
/** find suggestion target by targeted item (paged) */
|
||||
public List<SuggestionTarget> findByTarget(Context context, UUID target, int pageSize, long offset);
|
||||
|
||||
/** find suggestion source by source name */
|
||||
public SuggestionSource findSource(Context context, String source);
|
||||
|
||||
/** count all suggestion sources */
|
||||
public long countSources(Context context);
|
||||
|
||||
/** find all suggestion sources (paged) */
|
||||
public List<SuggestionSource> findAllSources(Context context, int pageSize, long offset);
|
||||
|
||||
/** find unprocessed suggestion by id */
|
||||
public Suggestion findUnprocessedSuggestion(Context context, String id);
|
||||
|
||||
/** reject a specific suggestion by its id */
|
||||
public void rejectSuggestion(Context context, String id);
|
||||
|
||||
/** find all suggestions by targeted item and external source */
|
||||
public List<Suggestion> findByTargetAndSource(Context context, UUID target, String source, int pageSize,
|
||||
long offset, boolean ascending);
|
||||
|
||||
/** count all suggestions by targeted item id and source name */
|
||||
public long countAllByTargetAndSource(Context context, String source, UUID target);
|
||||
|
||||
/** returns all suggestion providers */
|
||||
public List<SuggestionProvider> getSuggestionProviders();
|
||||
}
|
||||
|
@@ -76,6 +76,7 @@ public class SuggestionServiceImpl implements SuggestionService {
|
||||
public List<SuggestionTarget> findByTarget(Context context, UUID target, int pageSize, long offset) {
|
||||
List<SuggestionTarget> fullSourceTargets = new ArrayList<SuggestionTarget>();
|
||||
for (String source : providersMap.keySet()) {
|
||||
// all the suggestion target will be related to the same target (i.e. the same researcher - person item)
|
||||
SuggestionTarget sTarget = providersMap.get(source).findTarget(context, target);
|
||||
if (sTarget != null && sTarget.getTotal() > 0) {
|
||||
fullSourceTargets.add(sTarget);
|
||||
@@ -88,6 +89,8 @@ public class SuggestionServiceImpl implements SuggestionService {
|
||||
}
|
||||
}
|
||||
);
|
||||
// this list will be as large as the number of sources available in the repository so it is unlikely that
|
||||
// real pagination will occur
|
||||
return fullSourceTargets.stream().skip(offset).limit(pageSize).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
@@ -8,14 +8,17 @@
|
||||
package org.dspace.app.suggestion;
|
||||
|
||||
/**
|
||||
*
|
||||
* This DTO class is used to pass around the number of items interested by suggestion provided by a specific source
|
||||
* (i.e. openaire)
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionSource {
|
||||
|
||||
/** source name of the suggestion */
|
||||
private String name;
|
||||
|
||||
/** number of targeted items */
|
||||
private int total;
|
||||
|
||||
public SuggestionSource() {
|
||||
|
@@ -10,23 +10,27 @@ package org.dspace.app.suggestion;
|
||||
import org.dspace.content.Item;
|
||||
|
||||
/**
|
||||
*
|
||||
* This DTO class is used to pass around the number of suggestions available from a specific source for a target
|
||||
* repository item
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionTarget {
|
||||
|
||||
/** the item targeted */
|
||||
private Item target;
|
||||
|
||||
/** source name of the suggestion */
|
||||
private String source;
|
||||
|
||||
/** total count of suggestions for same target and source */
|
||||
private int total;
|
||||
|
||||
public SuggestionTarget() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a target person into a suggestion target.
|
||||
* Wrap a target repository item (usually a person item) into a suggestion target.
|
||||
*
|
||||
* @param item must be not null
|
||||
*/
|
||||
@@ -41,11 +45,7 @@ public class SuggestionTarget {
|
||||
* @return the source:uuid of the wrapped item
|
||||
*/
|
||||
public String getID() {
|
||||
if (target != null) {
|
||||
return source + ":" + target.getID();
|
||||
} else {
|
||||
return source + ":null";
|
||||
}
|
||||
return source + ":" + (target != null ? target.getID() : "");
|
||||
}
|
||||
|
||||
public Item getTarget() {
|
||||
|
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion.oaire;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getAllEntriesByMetadatum;
|
||||
|
||||
@@ -130,6 +130,11 @@ public class AuthorNamesScorer implements EvidenceScorer {
|
||||
return authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* cleans up undesired characters
|
||||
* @param value the string to clean up
|
||||
* @return cleaned up string
|
||||
* */
|
||||
private String normalize(String value) {
|
||||
String norm = Normalizer.normalize(value, Normalizer.NFD);
|
||||
CharsetDetector cd = new CharsetDetector();
|
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion.oaire;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
@@ -31,23 +31,50 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
*/
|
||||
public class DateScorer implements EvidenceScorer {
|
||||
|
||||
/**
|
||||
* if available it should contains the metadata field key in the form (schema.element[.qualifier]) that contains
|
||||
* the birth date of the researcher
|
||||
*/
|
||||
private String birthDateMetadata;
|
||||
|
||||
/**
|
||||
* if available it should contains the metadata field key in the form (schema.element[.qualifier]) that contains
|
||||
* the date of graduation of the researcher. If the metadata has multiple values the min will be used
|
||||
*/
|
||||
private String educationDateMetadata;
|
||||
|
||||
private String minDateMetadata;
|
||||
|
||||
private String maxDateMetadata;
|
||||
|
||||
/**
|
||||
* The minimal age that is expected for a researcher to be a potential author of a scholarly contribution
|
||||
* (i.e. the minimum delta from the publication date and the birth date)
|
||||
*/
|
||||
private int birthDateDelta = 20;
|
||||
|
||||
/**
|
||||
* The maximum age that is expected for a researcher to be a potential author of a scholarly contribution
|
||||
* (i.e. the maximum delta from the publication date and the birth date)
|
||||
*/
|
||||
private int birthDateRange = 50;
|
||||
|
||||
/**
|
||||
* The number of year from/before the graduation that is expected for a researcher to be a potential
|
||||
* author of a scholarly contribution (i.e. the minimum delta from the publication date and the first
|
||||
* graduation date)
|
||||
*/
|
||||
private int educationDateDelta = -3;
|
||||
|
||||
/**
|
||||
* The maximum scientific longevity that is expected for a researcher from its graduation to be a potential
|
||||
* author of a scholarly contribution (i.e. the maximum delta from the publication date and the first
|
||||
* graduation date)
|
||||
*/
|
||||
private int educationDateRange = 50;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
/**
|
||||
* the metadata used in the publication to track the publication date (i.e. dc.date.issued)
|
||||
*/
|
||||
private String publicationDateMetadata;
|
||||
|
||||
public void setItemService(ItemService itemService) {
|
||||
@@ -86,14 +113,6 @@ public class DateScorer implements EvidenceScorer {
|
||||
this.educationDateRange = educationDateRange;
|
||||
}
|
||||
|
||||
public void setMaxDateMetadata(String maxDateMetadata) {
|
||||
this.maxDateMetadata = maxDateMetadata;
|
||||
}
|
||||
|
||||
public void setMinDateMetadata(String minDateMetadata) {
|
||||
this.minDateMetadata = minDateMetadata;
|
||||
}
|
||||
|
||||
public void setPublicationDateMetadata(String publicationDateMetadata) {
|
||||
this.publicationDateMetadata = publicationDateMetadata;
|
||||
}
|
||||
@@ -101,6 +120,7 @@ public class DateScorer implements EvidenceScorer {
|
||||
/**
|
||||
* Method which is responsible to evaluate ImportRecord based on the publication date.
|
||||
* ImportRecords which have a date outside the defined or calculated expected range will be discarded.
|
||||
* {@link DateScorer#birthDateMetadata}, {@link DateScorer#educationDateMetadata}
|
||||
*
|
||||
* @param importRecord the ExternalDataObject to check
|
||||
* @param researcher DSpace item
|
||||
@@ -113,8 +133,7 @@ public class DateScorer implements EvidenceScorer {
|
||||
return new SuggestionEvidence(this.getClass().getSimpleName(),
|
||||
0,
|
||||
"No assumption was possible about the publication year range. "
|
||||
+ "Please consider to set a min/max date in the profile, specify the birthday "
|
||||
+ "or education achievements");
|
||||
+ "Please consider setting your birthday in your profile.");
|
||||
} else {
|
||||
String optDate = SuggestionUtils.getFirstEntryByMetadatum(importRecord, publicationDateMetadata);
|
||||
int year = getYear(optDate);
|
||||
@@ -139,34 +158,29 @@ public class DateScorer implements EvidenceScorer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns min and max year interval in between it's probably that the researcher
|
||||
* actually contributed to the suggested item
|
||||
* @param researcher
|
||||
* @return
|
||||
*/
|
||||
private Integer[] calculateRange(Item researcher) {
|
||||
String minDateStr = getSingleValue(researcher, minDateMetadata);
|
||||
int minYear = getYear(minDateStr);
|
||||
String maxDateStr = getSingleValue(researcher, maxDateMetadata);
|
||||
int maxYear = getYear(maxDateStr);
|
||||
if (minYear > 0 && maxYear > 0) {
|
||||
return new Integer[] { minYear, maxYear };
|
||||
String birthDateStr = getSingleValue(researcher, birthDateMetadata);
|
||||
int birthDateYear = getYear(birthDateStr);
|
||||
int educationDateYear = getListMetadataValues(researcher, educationDateMetadata).stream()
|
||||
.mapToInt(x -> getYear(x.getValue())).filter(d -> d > 0).min().orElse(-1);
|
||||
if (educationDateYear > 0) {
|
||||
return new Integer[] {
|
||||
educationDateYear + educationDateDelta,
|
||||
educationDateYear + educationDateDelta + educationDateRange
|
||||
};
|
||||
} else if (birthDateYear > 0) {
|
||||
return new Integer[] {
|
||||
birthDateYear + birthDateDelta,
|
||||
birthDateYear + birthDateDelta + birthDateRange
|
||||
};
|
||||
} else {
|
||||
String birthDateStr = getSingleValue(researcher, birthDateMetadata);
|
||||
int birthDateYear = getYear(birthDateStr);
|
||||
int educationDateYear = getListMetadataValues(researcher, educationDateMetadata)
|
||||
.stream()
|
||||
.mapToInt(x -> getYear(x.getValue()))
|
||||
.filter(d -> d > 0)
|
||||
.min().orElse(-1);
|
||||
if (educationDateYear > 0) {
|
||||
return new Integer[] {
|
||||
minYear > 0 ? minYear : educationDateYear + educationDateDelta,
|
||||
maxYear > 0 ? maxYear : educationDateYear + educationDateDelta + educationDateRange
|
||||
};
|
||||
} else if (birthDateYear > 0) {
|
||||
return new Integer[] {
|
||||
minYear > 0 ? minYear : birthDateYear + birthDateDelta,
|
||||
maxYear > 0 ? maxYear : birthDateYear + birthDateDelta + birthDateRange
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -5,14 +5,14 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion.oaire;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import org.dspace.app.suggestion.SuggestionEvidence;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
|
||||
/**
|
||||
* Interface used in {@see org.dspace.app.suggestion.oaire.OAIREPublicationApproverServiceImpl}
|
||||
* Interface used in {@see org.dspace.app.suggestion.oaire.PublicationApproverServiceImpl}
|
||||
* to construct filtering pipeline.
|
||||
*
|
||||
* For each EvidenceScorer, the service call computeEvidence method.
|
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion.oaire;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getAllEntriesByMetadatum;
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getFirstEntryByMetadatum;
|
||||
@@ -33,7 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class OAIREPublicationLoader extends SolrSuggestionProvider {
|
||||
public class PublicationLoader extends SolrSuggestionProvider {
|
||||
|
||||
private List<String> names;
|
||||
|
||||
@@ -66,7 +66,7 @@ public class OAIREPublicationLoader extends SolrSuggestionProvider {
|
||||
* This method filter a list of ImportRecords using a pipeline of AuthorNamesApprover
|
||||
* and return a filtered list of ImportRecords.
|
||||
*
|
||||
* @see org.dspace.app.suggestion.oaire.AuthorNamesScorer
|
||||
* @see org.dspace.app.suggestion.openaire.AuthorNamesScorer
|
||||
* @param researcher the researcher Item
|
||||
* @param importRecords List of import record
|
||||
* @return a list of filtered import records
|
||||
@@ -105,10 +105,22 @@ public class OAIREPublicationLoader extends SolrSuggestionProvider {
|
||||
*/
|
||||
public void importAuthorRecords(Context context, Item researcher)
|
||||
throws SolrServerException, IOException {
|
||||
List<ExternalDataObject> metadata = getImportRecords(researcher);
|
||||
List<Suggestion> records = reduceAndTransform(researcher, metadata);
|
||||
for (Suggestion record : records) {
|
||||
solrSuggestionStorageService.addSuggestion(record, false, false);
|
||||
int offset = 0;
|
||||
int limit = 10;
|
||||
int loaded = limit;
|
||||
List<String> searchValues = searchMetadataValues(researcher);
|
||||
while (loaded > 0) {
|
||||
List<ExternalDataObject> metadata = getImportRecords(searchValues, researcher, offset, limit);
|
||||
if (metadata.isEmpty()) {
|
||||
loaded = 0;
|
||||
continue;
|
||||
}
|
||||
offset += limit;
|
||||
loaded = metadata.size();
|
||||
List<Suggestion> records = reduceAndTransform(researcher, metadata);
|
||||
for (Suggestion record : records) {
|
||||
solrSuggestionStorageService.addSuggestion(record, false, false);
|
||||
}
|
||||
}
|
||||
solrSuggestionStorageService.commit();
|
||||
}
|
||||
@@ -154,14 +166,18 @@ public class OAIREPublicationLoader extends SolrSuggestionProvider {
|
||||
* get from metadata key defined in class level variable names as author to query OpenAIRE.
|
||||
*
|
||||
* @see org.dspace.importer.external.openaire.service.OpenAireImportMetadataSourceServiceImpl
|
||||
* @param searchValues query
|
||||
* @param researcher item to extract metadata from
|
||||
* @param limit for pagination purpose
|
||||
* @param offset for pagination purpose
|
||||
* @return list of ImportRecord
|
||||
*/
|
||||
private List<ExternalDataObject> getImportRecords(Item researcher) {
|
||||
List<String> searchValues = searchMetadataValues(researcher);
|
||||
private List<ExternalDataObject> getImportRecords(List<String> searchValues,
|
||||
Item researcher, int offset, int limit) {
|
||||
List<ExternalDataObject> matchingRecords = new ArrayList<>();
|
||||
for (String searchValue : searchValues) {
|
||||
matchingRecords.addAll(primaryProvider.searchExternalDataObjects(searchValue, 0, 9999));
|
||||
matchingRecords.addAll(
|
||||
primaryProvider.searchExternalDataObjects(searchValue, offset, limit));
|
||||
}
|
||||
List<ExternalDataObject> toReturn = removeDuplicates(matchingRecords);
|
||||
return toReturn;
|
@@ -5,17 +5,17 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
|
||||
/**
|
||||
* Extension of {@link OAIREPublicationLoaderScriptConfiguration} for CLI.
|
||||
* Extension of {@link PublicationLoaderScriptConfiguration} for CLI.
|
||||
*
|
||||
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||
*/
|
||||
public class OAIREPublicationLoaderCliScriptConfiguration<T extends OAIREPublicationLoaderRunnable>
|
||||
extends OAIREPublicationLoaderScriptConfiguration<T> {
|
||||
public class PublicationLoaderCliScriptConfiguration<T extends PublicationLoaderRunnable>
|
||||
extends PublicationLoaderScriptConfiguration<T> {
|
||||
|
||||
@Override
|
||||
public Options getOptions() {
|
@@ -5,19 +5,23 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.dspace.app.suggestion.oaire.OAIREPublicationLoader;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
import org.dspace.discovery.DiscoverQuery;
|
||||
import org.dspace.discovery.SearchService;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.dspace.discovery.SearchUtils;
|
||||
import org.dspace.discovery.utils.DiscoverQueryBuilder;
|
||||
import org.dspace.discovery.utils.parameter.QueryBuilderSearchFilter;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.sort.SortOption;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -32,12 +36,12 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||
*/
|
||||
public class OAIREPublicationLoaderRunnable
|
||||
extends DSpaceRunnable<OAIREPublicationLoaderScriptConfiguration<OAIREPublicationLoaderRunnable>> {
|
||||
public class PublicationLoaderRunnable
|
||||
extends DSpaceRunnable<PublicationLoaderScriptConfiguration<PublicationLoaderRunnable>> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(OAIREPublicationLoaderRunnable.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PublicationLoaderRunnable.class);
|
||||
|
||||
private OAIREPublicationLoader oairePublicationLoader = null;
|
||||
private PublicationLoader oairePublicationLoader = null;
|
||||
|
||||
protected Context context;
|
||||
|
||||
@@ -45,9 +49,9 @@ public class OAIREPublicationLoaderRunnable
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public OAIREPublicationLoaderScriptConfiguration<OAIREPublicationLoaderRunnable> getScriptConfiguration() {
|
||||
OAIREPublicationLoaderScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||
.getServiceByName("import-oaire-suggestions", OAIREPublicationLoaderScriptConfiguration.class);
|
||||
public PublicationLoaderScriptConfiguration<PublicationLoaderRunnable> getScriptConfiguration() {
|
||||
PublicationLoaderScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||
.getServiceByName("import-openaire-suggestions", PublicationLoaderScriptConfiguration.class);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@@ -55,7 +59,7 @@ public class OAIREPublicationLoaderRunnable
|
||||
public void setup() throws ParseException {
|
||||
|
||||
oairePublicationLoader = new DSpace().getServiceManager().getServiceByName(
|
||||
"OAIREPublicationLoader", OAIREPublicationLoader.class);
|
||||
"OpenairePublicationLoader", PublicationLoader.class);
|
||||
|
||||
profile = commandLine.getOptionValue("s");
|
||||
if (profile == null) {
|
||||
@@ -70,10 +74,9 @@ public class OAIREPublicationLoaderRunnable
|
||||
|
||||
context = new Context();
|
||||
|
||||
List<Item> researchers = getResearchers(profile);
|
||||
|
||||
for (Item researcher : researchers) {
|
||||
|
||||
Iterator<Item> researchers = getResearchers(profile);
|
||||
while (researchers.hasNext()) {
|
||||
Item researcher = researchers.next();
|
||||
oairePublicationLoader.importAuthorRecords(context, researcher);
|
||||
}
|
||||
|
||||
@@ -90,24 +93,23 @@ public class OAIREPublicationLoaderRunnable
|
||||
* @return the researcher with specified UUID or all researchers
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private List<Item> getResearchers(String profileUUID) {
|
||||
final UUID uuid = profileUUID != null ? UUID.fromString(profileUUID) : null;
|
||||
private Iterator<Item> getResearchers(String profileUUID) {
|
||||
SearchService searchService = new DSpace().getSingletonService(SearchService.class);
|
||||
List<IndexableObject> objects = null;
|
||||
if (uuid != null) {
|
||||
objects = searchService.search(context, "search.resourceid:" + uuid.toString(),
|
||||
"lastModified", false, 0, 1000, "search.resourcetype:Item", "dspace.entity.type:Person");
|
||||
} else {
|
||||
objects = searchService.search(context, "*:*", "lastModified", false, 0, 1000, "search.resourcetype:Item",
|
||||
"dspace.entity.type:Person");
|
||||
DiscoverQueryBuilder queryBuilder = SearchUtils.getQueryBuilder();
|
||||
List<QueryBuilderSearchFilter> filters = new ArrayList<QueryBuilderSearchFilter>();
|
||||
String query = "*:*";
|
||||
if (profileUUID != null) {
|
||||
query = "search.resourceid:" + profileUUID.toString();
|
||||
}
|
||||
List<Item> items = new ArrayList<Item>();
|
||||
if (objects != null) {
|
||||
for (IndexableObject o : objects) {
|
||||
items.add((Item) o.getIndexedObject());
|
||||
}
|
||||
try {
|
||||
DiscoverQuery discoverQuery = queryBuilder.buildQuery(context, null,
|
||||
SearchUtils.getDiscoveryConfigurationByName("person"),
|
||||
query, filters,
|
||||
"Item", 10, Long.getLong("0"), null, SortOption.DESCENDING);
|
||||
return searchService.iteratorSearch(context, null, discoverQuery);
|
||||
} catch (SearchServiceException e) {
|
||||
LOGGER.error("Unable to read researcher on solr", e);
|
||||
}
|
||||
LOGGER.info("Found " + items.size() + " researcher(s)");
|
||||
return items;
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -5,19 +5,19 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
public class OAIREPublicationLoaderRunnableCli extends OAIREPublicationLoaderRunnable {
|
||||
public class PublicationLoaderRunnableCli extends PublicationLoaderRunnable {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public OAIREPublicationLoaderCliScriptConfiguration getScriptConfiguration() {
|
||||
OAIREPublicationLoaderCliScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||
.getServiceByName("import-oaire-suggestions", OAIREPublicationLoaderCliScriptConfiguration.class);
|
||||
public PublicationLoaderCliScriptConfiguration getScriptConfiguration() {
|
||||
PublicationLoaderCliScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||
.getServiceByName("import-openaire-suggestions", PublicationLoaderCliScriptConfiguration.class);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class OAIREPublicationLoaderRunnableCli extends OAIREPublicationLoaderRun
|
||||
// in case of CLI we show the help prompt
|
||||
if (commandLine.hasOption('h')) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("Import Readearchers Suggestions", getScriptConfiguration().getOptions());
|
||||
formatter.printHelp("Import Researchers Suggestions", getScriptConfiguration().getOptions());
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
@@ -5,12 +5,12 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||
|
||||
public class OAIREPublicationLoaderScriptConfiguration<T extends OAIREPublicationLoaderRunnable>
|
||||
public class PublicationLoaderScriptConfiguration<T extends PublicationLoaderRunnable>
|
||||
extends ScriptConfiguration<T> {
|
||||
|
||||
private Class<T> dspaceRunnableClass;
|
||||
@@ -22,7 +22,7 @@ public class OAIREPublicationLoaderScriptConfiguration<T extends OAIREPublicatio
|
||||
|
||||
/**
|
||||
* Generic setter for the dspaceRunnableClass
|
||||
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this OAIREPublicationLoaderScriptConfiguration
|
||||
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this PublicationLoaderScriptConfiguration
|
||||
*/
|
||||
@Override
|
||||
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
|
@@ -628,12 +628,23 @@ public class AuthorizeUtil {
|
||||
// actually expected to be returning true.
|
||||
// For example the LDAP canSelfRegister will return true due to auto-register, while that
|
||||
// does not imply a new user can register explicitly
|
||||
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
|
||||
.allowSetPassword(context, request, null);
|
||||
return authorizePasswordChange(context, request);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a boolean indicating whether the current user is allowed to reset the password
|
||||
* or not
|
||||
*
|
||||
* @return A boolean indicating whether the current user can reset its password or not
|
||||
* @throws SQLException If something goes wrong
|
||||
*/
|
||||
public static boolean authorizeForgotPassword() {
|
||||
return DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getBooleanProperty("user.forgot-password", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a boolean indicating whether it's allowed to update the password for the EPerson
|
||||
* with the given email and canLogin property
|
||||
@@ -647,8 +658,7 @@ public class AuthorizeUtil {
|
||||
if (eperson != null && eperson.canLogIn()) {
|
||||
HttpServletRequest request = new DSpace().getRequestService().getCurrentRequest()
|
||||
.getHttpServletRequest();
|
||||
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
|
||||
.allowSetPassword(context, request, null);
|
||||
return authorizePasswordChange(context, request);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Something went wrong trying to retrieve EPerson for email: " + email, e);
|
||||
@@ -656,6 +666,19 @@ public class AuthorizeUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current configuration has at least one password based authentication method
|
||||
*
|
||||
* @param context Dspace Context
|
||||
* @param request Current Request
|
||||
* @return True if the password change is enabled
|
||||
* @throws SQLException
|
||||
*/
|
||||
protected static boolean authorizePasswordChange(Context context, HttpServletRequest request) throws SQLException {
|
||||
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
|
||||
.allowSetPassword(context, request, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the community Admin can manage accounts
|
||||
*
|
||||
|
@@ -14,7 +14,6 @@ import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.FactoryConfigurationError;
|
||||
@@ -150,17 +149,16 @@ public class DCInputsReader {
|
||||
* Returns the set of DC inputs used for a particular collection, or the
|
||||
* default set if no inputs defined for the collection
|
||||
*
|
||||
* @param collectionHandle collection's unique Handle
|
||||
* @param collection collection for which search the set of DC inputs
|
||||
* @return DC input set
|
||||
* @throws DCInputsReaderException if no default set defined
|
||||
* @throws ServletException
|
||||
*/
|
||||
public List<DCInputSet> getInputsByCollectionHandle(String collectionHandle)
|
||||
public List<DCInputSet> getInputsByCollection(Collection collection)
|
||||
throws DCInputsReaderException {
|
||||
SubmissionConfig config;
|
||||
try {
|
||||
config = SubmissionServiceFactory.getInstance().getSubmissionConfigService()
|
||||
.getSubmissionConfigByCollection(collectionHandle);
|
||||
.getSubmissionConfigByCollection(collection);
|
||||
String formName = config.getSubmissionName();
|
||||
if (formName == null) {
|
||||
throw new DCInputsReaderException("No form designated as default");
|
||||
@@ -691,7 +689,7 @@ public class DCInputsReader {
|
||||
|
||||
public String getInputFormNameByCollectionAndField(Collection collection, String field)
|
||||
throws DCInputsReaderException {
|
||||
List<DCInputSet> inputSets = getInputsByCollectionHandle(collection.getHandle());
|
||||
List<DCInputSet> inputSets = getInputsByCollection(collection);
|
||||
for (DCInputSet inputSet : inputSets) {
|
||||
String[] tokenized = Utils.tokenize(field);
|
||||
String schema = tokenized[0];
|
||||
|
@@ -11,6 +11,7 @@ import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -21,6 +22,7 @@ import javax.xml.parsers.FactoryConfigurationError;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
@@ -90,6 +92,13 @@ public class SubmissionConfigReader {
|
||||
*/
|
||||
private Map<String, String> collectionToSubmissionConfig = null;
|
||||
|
||||
/**
|
||||
* Hashmap which stores which submission process configuration is used by
|
||||
* which community, computed from the item submission config file
|
||||
* (specifically, the 'submission-map' tag)
|
||||
*/
|
||||
private Map<String, String> communityToSubmissionConfig = null;
|
||||
|
||||
/**
|
||||
* Reference to the global submission step definitions defined in the
|
||||
* "step-definitions" section
|
||||
@@ -127,6 +136,7 @@ public class SubmissionConfigReader {
|
||||
|
||||
public void reload() throws SubmissionConfigReaderException {
|
||||
collectionToSubmissionConfig = null;
|
||||
communityToSubmissionConfig = null;
|
||||
stepDefns = null;
|
||||
submitDefns = null;
|
||||
buildInputs(configDir + SUBMIT_DEF_FILE_PREFIX + SUBMIT_DEF_FILE_SUFFIX);
|
||||
@@ -145,7 +155,8 @@ public class SubmissionConfigReader {
|
||||
*/
|
||||
private void buildInputs(String fileName) throws SubmissionConfigReaderException {
|
||||
collectionToSubmissionConfig = new HashMap<String, String>();
|
||||
submitDefns = new HashMap<String, List<Map<String, String>>>();
|
||||
communityToSubmissionConfig = new HashMap<String, String>();
|
||||
submitDefns = new LinkedHashMap<String, List<Map<String, String>>>();
|
||||
|
||||
String uri = "file:" + new File(fileName).getAbsolutePath();
|
||||
|
||||
@@ -210,18 +221,41 @@ public class SubmissionConfigReader {
|
||||
* Returns the Item Submission process config used for a particular
|
||||
* collection, or the default if none is defined for the collection
|
||||
*
|
||||
* @param collectionHandle collection's unique Handle
|
||||
* @param col collection for which search Submission process config
|
||||
* @return the SubmissionConfig representing the item submission config
|
||||
* @throws SubmissionConfigReaderException if no default submission process configuration defined
|
||||
* @throws IllegalStateException if no default submission process configuration defined
|
||||
*/
|
||||
public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) {
|
||||
// get the name of the submission process config for this collection
|
||||
String submitName = collectionToSubmissionConfig
|
||||
.get(collectionHandle);
|
||||
if (submitName == null) {
|
||||
public SubmissionConfig getSubmissionConfigByCollection(Collection col) {
|
||||
|
||||
String submitName;
|
||||
|
||||
if (col != null) {
|
||||
|
||||
// get the name of the submission process config for this collection
|
||||
submitName = collectionToSubmissionConfig
|
||||
.get(DEFAULT_COLLECTION);
|
||||
.get(col.getHandle());
|
||||
if (submitName != null) {
|
||||
return getSubmissionConfigByName(submitName);
|
||||
}
|
||||
|
||||
if (!communityToSubmissionConfig.isEmpty()) {
|
||||
try {
|
||||
List<Community> communities = col.getCommunities();
|
||||
for (Community com : communities) {
|
||||
submitName = getSubmissionConfigByCommunity(com);
|
||||
if (submitName != null) {
|
||||
return getSubmissionConfigByName(submitName);
|
||||
}
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
throw new IllegalStateException("Error occurred while getting item submission configured " +
|
||||
"by community", sqle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submitName = collectionToSubmissionConfig.get(DEFAULT_COLLECTION);
|
||||
|
||||
if (submitName == null) {
|
||||
throw new IllegalStateException(
|
||||
"No item submission process configuration designated as 'default' in 'submission-map' section of " +
|
||||
@@ -230,6 +264,30 @@ public class SubmissionConfigReader {
|
||||
return getSubmissionConfigByName(submitName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function to return the Item Submission process config
|
||||
* used for a community or the closest community parent, or null
|
||||
* if none is defined
|
||||
*
|
||||
* @param com community for which search Submission process config
|
||||
* @return the SubmissionConfig representing the item submission config
|
||||
*/
|
||||
private String getSubmissionConfigByCommunity(Community com) {
|
||||
String submitName = communityToSubmissionConfig
|
||||
.get(com.getHandle());
|
||||
if (submitName != null) {
|
||||
return submitName;
|
||||
}
|
||||
List<Community> communities = com.getParentCommunities();
|
||||
for (Community parentCom : communities) {
|
||||
submitName = getSubmissionConfigByCommunity(parentCom);
|
||||
if (submitName != null) {
|
||||
return submitName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Item Submission process config
|
||||
*
|
||||
@@ -357,13 +415,14 @@ public class SubmissionConfigReader {
|
||||
Node nd = nl.item(i);
|
||||
if (nd.getNodeName().equals("name-map")) {
|
||||
String id = getAttribute(nd, "collection-handle");
|
||||
String communityId = getAttribute(nd, "community-handle");
|
||||
String entityType = getAttribute(nd, "collection-entity-type");
|
||||
String value = getAttribute(nd, "submission-name");
|
||||
String content = getValue(nd);
|
||||
if (id == null && entityType == null) {
|
||||
if (id == null && communityId == null && entityType == null) {
|
||||
throw new SAXException(
|
||||
"name-map element is missing collection-handle or collection-entity-type attribute " +
|
||||
"in 'item-submission.xml'");
|
||||
"name-map element is missing collection-handle or community-handle or collection-entity-type " +
|
||||
"attribute in 'item-submission.xml'");
|
||||
}
|
||||
if (value == null) {
|
||||
throw new SAXException(
|
||||
@@ -375,7 +434,8 @@ public class SubmissionConfigReader {
|
||||
}
|
||||
if (id != null) {
|
||||
collectionToSubmissionConfig.put(id, value);
|
||||
|
||||
} else if (communityId != null) {
|
||||
communityToSubmissionConfig.put(communityId, value);
|
||||
} else {
|
||||
// get all collections for this entity-type
|
||||
List<Collection> collections = collectionService.findAllCollectionsByEntityType( context,
|
||||
|
@@ -405,21 +405,13 @@ public class Util {
|
||||
DCInput myInputs = null;
|
||||
boolean myInputsFound = false;
|
||||
String formFileName = I18nUtil.getInputFormsFileName(locale);
|
||||
String col_handle = "";
|
||||
|
||||
Collection collection = item.getOwningCollection();
|
||||
|
||||
if (collection == null) {
|
||||
// set an empty handle so to get the default input set
|
||||
col_handle = "";
|
||||
} else {
|
||||
col_handle = collection.getHandle();
|
||||
}
|
||||
|
||||
// Read the input form file for the specific collection
|
||||
DCInputsReader inputsReader = new DCInputsReader(formFileName);
|
||||
|
||||
List<DCInputSet> inputSets = inputsReader.getInputsByCollectionHandle(col_handle);
|
||||
List<DCInputSet> inputSets = inputsReader.getInputsByCollection(collection);
|
||||
|
||||
// Replace the values of Metadatum[] with the correct ones in case
|
||||
// of
|
||||
@@ -500,8 +492,8 @@ public class Util {
|
||||
public static List<String> differenceInSubmissionFields(Collection fromCollection, Collection toCollection)
|
||||
throws DCInputsReaderException {
|
||||
DCInputsReader reader = new DCInputsReader();
|
||||
List<DCInputSet> from = reader.getInputsByCollectionHandle(fromCollection.getHandle());
|
||||
List<DCInputSet> to = reader.getInputsByCollectionHandle(toCollection.getHandle());
|
||||
List<DCInputSet> from = reader.getInputsByCollection(fromCollection);
|
||||
List<DCInputSet> to = reader.getInputsByCollection(toCollection);
|
||||
|
||||
Set<String> fromFieldName = new HashSet<>();
|
||||
Set<String> toFieldName = new HashSet<>();
|
||||
|
@@ -80,7 +80,15 @@ public class DSpaceAuthorityIndexer implements AuthorityIndexerInterface, Initia
|
||||
throws SQLException, AuthorizeException {
|
||||
List<AuthorityValue> values = new ArrayList<>();
|
||||
for (String metadataField : metadataFields) {
|
||||
List<MetadataValue> metadataValues = itemService.getMetadataByMetadataString(item, metadataField);
|
||||
|
||||
String[] fieldParts = metadataField.split("\\.");
|
||||
String schema = (fieldParts.length > 0 ? fieldParts[0] : null);
|
||||
String element = (fieldParts.length > 1 ? fieldParts[1] : null);
|
||||
String qualifier = (fieldParts.length > 2 ? fieldParts[2] : null);
|
||||
|
||||
// Get metadata values without virtual metadata
|
||||
List<MetadataValue> metadataValues = itemService.getMetadata(item, schema, element, qualifier, Item.ANY,
|
||||
false);
|
||||
for (MetadataValue metadataValue : metadataValues) {
|
||||
String content = metadataValue.getValue();
|
||||
String authorityKey = metadataValue.getAuthority();
|
||||
|
@@ -895,7 +895,7 @@ public class AuthorizeServiceImpl implements AuthorizeService {
|
||||
return true;
|
||||
}
|
||||
} catch (SearchServiceException e) {
|
||||
log.error("Failed getting getting community/collection admin status for "
|
||||
log.error("Failed getting community/collection admin status for "
|
||||
+ context.getCurrentUser().getEmail() + " The search error is: " + e.getMessage()
|
||||
+ " The search resourceType filter was: " + query);
|
||||
}
|
||||
|
@@ -15,19 +15,19 @@ import java.util.Map;
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class COARNotifyConfigurationService {
|
||||
public class NotifyConfigurationService {
|
||||
|
||||
/**
|
||||
* Mapping the submission step process identifier with the configuration
|
||||
* (see configuration at coar-notify.xml)
|
||||
*/
|
||||
private Map<String, List<LDNPattern>> patterns;
|
||||
private Map<String, List<NotifyPattern>> patterns;
|
||||
|
||||
public Map<String, List<LDNPattern>> getPatterns() {
|
||||
public Map<String, List<NotifyPattern>> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
public void setPatterns(Map<String, List<LDNPattern>> patterns) {
|
||||
public void setPatterns(Map<String, List<NotifyPattern>> patterns) {
|
||||
this.patterns = patterns;
|
||||
}
|
||||
|
@@ -12,16 +12,16 @@ package org.dspace.coarnotify;
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class LDNPattern {
|
||||
public class NotifyPattern {
|
||||
|
||||
private String pattern;
|
||||
private boolean multipleRequest;
|
||||
|
||||
public LDNPattern() {
|
||||
public NotifyPattern() {
|
||||
|
||||
}
|
||||
|
||||
public LDNPattern(String pattern, boolean multipleRequest) {
|
||||
public NotifyPattern(String pattern, boolean multipleRequest) {
|
||||
this.pattern = pattern;
|
||||
this.multipleRequest = multipleRequest;
|
||||
}
|
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class COARNotifySubmissionConfiguration {
|
||||
public class NotifySubmissionConfiguration {
|
||||
|
||||
/**
|
||||
* the map key of configured bean of COARNotifyConfigurationService
|
||||
@@ -26,13 +26,13 @@ public class COARNotifySubmissionConfiguration {
|
||||
* the map values of configured bean of COARNotifyConfigurationService
|
||||
* in coar-notify.xml
|
||||
*/
|
||||
private List<LDNPattern> patterns;
|
||||
private List<NotifyPattern> patterns;
|
||||
|
||||
public COARNotifySubmissionConfiguration() {
|
||||
public NotifySubmissionConfiguration() {
|
||||
|
||||
}
|
||||
|
||||
public COARNotifySubmissionConfiguration(String id, List<LDNPattern> patterns) {
|
||||
public NotifySubmissionConfiguration(String id, List<NotifyPattern> patterns) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.patterns = patterns;
|
||||
@@ -51,7 +51,7 @@ public class COARNotifySubmissionConfiguration {
|
||||
*
|
||||
* @return the list of configured COAR Notify Patterns
|
||||
*/
|
||||
public List<LDNPattern> getPatterns() {
|
||||
public List<NotifyPattern> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class COARNotifySubmissionConfiguration {
|
||||
* Sets the list of configured COAR Notify Patterns
|
||||
* @param patterns
|
||||
*/
|
||||
public void setPatterns(final List<LDNPattern> patterns) {
|
||||
public void setPatterns(final List<NotifyPattern> patterns) {
|
||||
this.patterns = patterns;
|
||||
}
|
||||
}
|
@@ -10,41 +10,41 @@ package org.dspace.coarnotify;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.coarnotify.service.SubmissionCOARNotifyService;
|
||||
import org.dspace.coarnotify.service.SubmissionNotifyService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Service implementation of {@link SubmissionCOARNotifyService}
|
||||
* Service implementation of {@link SubmissionNotifyService}
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class SubmissionCOARNotifyServiceImpl implements SubmissionCOARNotifyService {
|
||||
public class SubmissionNotifyServiceImpl implements SubmissionNotifyService {
|
||||
|
||||
@Autowired(required = true)
|
||||
private COARNotifyConfigurationService coarNotifyConfigurationService;
|
||||
private NotifyConfigurationService coarNotifyConfigurationService;
|
||||
|
||||
protected SubmissionCOARNotifyServiceImpl() {
|
||||
protected SubmissionNotifyServiceImpl() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public COARNotifySubmissionConfiguration findOne(String id) {
|
||||
List<LDNPattern> patterns =
|
||||
public NotifySubmissionConfiguration findOne(String id) {
|
||||
List<NotifyPattern> patterns =
|
||||
coarNotifyConfigurationService.getPatterns().get(id);
|
||||
|
||||
if (patterns == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new COARNotifySubmissionConfiguration(id, patterns);
|
||||
return new NotifySubmissionConfiguration(id, patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<COARNotifySubmissionConfiguration> findAll() {
|
||||
List<COARNotifySubmissionConfiguration> coarNotifies = new ArrayList<>();
|
||||
public List<NotifySubmissionConfiguration> findAll() {
|
||||
List<NotifySubmissionConfiguration> coarNotifies = new ArrayList<>();
|
||||
|
||||
coarNotifyConfigurationService.getPatterns().forEach((id, patterns) ->
|
||||
coarNotifies.add(new COARNotifySubmissionConfiguration(id, patterns)
|
||||
coarNotifies.add(new NotifySubmissionConfiguration(id, patterns)
|
||||
));
|
||||
|
||||
return coarNotifies;
|
@@ -9,7 +9,7 @@ package org.dspace.coarnotify.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.coarnotify.COARNotifySubmissionConfiguration;
|
||||
import org.dspace.coarnotify.NotifySubmissionConfiguration;
|
||||
|
||||
/**
|
||||
* Service interface class for the Creative Submission COAR Notify.
|
||||
@@ -18,7 +18,7 @@ import org.dspace.coarnotify.COARNotifySubmissionConfiguration;
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface SubmissionCOARNotifyService {
|
||||
public interface SubmissionNotifyService {
|
||||
|
||||
/**
|
||||
* Find the COARE Notify corresponding to the provided ID
|
||||
@@ -27,13 +27,13 @@ public interface SubmissionCOARNotifyService {
|
||||
* @param id - the ID of the COAR Notify to be found
|
||||
* @return the corresponding COAR Notify if found or null when not found
|
||||
*/
|
||||
public COARNotifySubmissionConfiguration findOne(String id);
|
||||
public NotifySubmissionConfiguration findOne(String id);
|
||||
|
||||
/**
|
||||
* Find all configured COAR Notifies
|
||||
*
|
||||
* @return all configured COAR Notifies
|
||||
*/
|
||||
public List<COARNotifySubmissionConfiguration> findAll();
|
||||
public List<NotifySubmissionConfiguration> findAll();
|
||||
|
||||
}
|
@@ -307,10 +307,18 @@ public class Bitstream extends DSpaceObject implements DSpaceObjectLegacySupport
|
||||
return collection;
|
||||
}
|
||||
|
||||
public void setCollection(Collection collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public Community getCommunity() {
|
||||
return community;
|
||||
}
|
||||
|
||||
public void setCommunity(Community community) {
|
||||
this.community = community;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset store number where this bitstream is stored
|
||||
*
|
||||
|
@@ -458,10 +458,15 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl<Bitstream> imp
|
||||
|
||||
@Override
|
||||
public Bitstream findByIdOrLegacyId(Context context, String id) throws SQLException {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
try {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not a valid legacy ID or valid UUID
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -562,10 +562,15 @@ public class BundleServiceImpl extends DSpaceObjectServiceImpl<Bundle> implement
|
||||
|
||||
@Override
|
||||
public Bundle findByIdOrLegacyId(Context context, String id) throws SQLException {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
try {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not a valid legacy ID or valid UUID
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -135,6 +135,9 @@ public class Collection extends DSpaceObject implements DSpaceObjectLegacySuppor
|
||||
|
||||
protected void setLogo(Bitstream logo) {
|
||||
this.logo = logo;
|
||||
if (logo != null) {
|
||||
logo.setCollection(this);
|
||||
}
|
||||
setModified();
|
||||
}
|
||||
|
||||
|
@@ -895,10 +895,15 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
|
||||
|
||||
@Override
|
||||
public Collection findByIdOrLegacyId(Context context, String id) throws SQLException {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
try {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not a valid legacy ID or valid UUID
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -123,6 +123,9 @@ public class Community extends DSpaceObject implements DSpaceObjectLegacySupport
|
||||
|
||||
void setLogo(Bitstream logo) {
|
||||
this.logo = logo;
|
||||
if (logo != null) {
|
||||
logo.setCommunity(this);
|
||||
}
|
||||
setModified();
|
||||
}
|
||||
|
||||
|
@@ -694,10 +694,15 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
|
||||
|
||||
@Override
|
||||
public Community findByIdOrLegacyId(Context context, String id) throws SQLException {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
try {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
return find(context, UUID.fromString(id));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not a valid legacy ID or valid UUID
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -150,7 +150,6 @@ public class InstallItemServiceImpl implements InstallItemService {
|
||||
return finishItem(c, item, is);
|
||||
}
|
||||
|
||||
|
||||
protected void populateMetadata(Context c, Item item)
|
||||
throws SQLException, AuthorizeException {
|
||||
// create accession date
|
||||
@@ -158,15 +157,6 @@ public class InstallItemServiceImpl implements InstallItemService {
|
||||
itemService.addMetadata(c, item, MetadataSchemaEnum.DC.getName(),
|
||||
"date", "accessioned", null, now.toString());
|
||||
|
||||
// add date available if not under embargo, otherwise it will
|
||||
// be set when the embargo is lifted.
|
||||
// this will flush out fatal embargo metadata
|
||||
// problems before we set inArchive.
|
||||
if (embargoService.getEmbargoTermsAsDate(c, item) == null) {
|
||||
itemService.addMetadata(c, item, MetadataSchemaEnum.DC.getName(),
|
||||
"date", "available", null, now.toString());
|
||||
}
|
||||
|
||||
// If issue date is set as "today" (literal string), then set it to current date
|
||||
// In the below loop, we temporarily clear all issued dates and re-add, one-by-one,
|
||||
// replacing "today" with today's date.
|
||||
|
@@ -8,6 +8,7 @@
|
||||
package org.dspace.content;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.dspace.app.ldn.ItemFilter;
|
||||
@@ -37,12 +38,21 @@ public class ItemFilterServiceImpl implements ItemFilterService {
|
||||
|
||||
@Override
|
||||
public List<ItemFilter> findAll() {
|
||||
return serviceManager.getServicesWithNamesByType(LogicalStatement.class)
|
||||
.keySet()
|
||||
.stream()
|
||||
.sorted()
|
||||
.map(ItemFilter::new)
|
||||
.collect(Collectors.toList());
|
||||
Map<String, LogicalStatement> ldnFilters =
|
||||
serviceManager.getServiceByName("ldnItemFilters", Map.class);
|
||||
return ldnFilters.keySet()
|
||||
.stream()
|
||||
.sorted()
|
||||
.map(ItemFilter::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ServiceManager getServiceManager() {
|
||||
return serviceManager;
|
||||
}
|
||||
|
||||
public void setServiceManager(ServiceManager serviceManager) {
|
||||
this.serviceManager = serviceManager;
|
||||
}
|
||||
|
||||
}
|
@@ -49,6 +49,7 @@ import org.dspace.content.service.MetadataSchemaService;
|
||||
import org.dspace.content.service.RelationshipService;
|
||||
import org.dspace.content.service.WorkspaceItemService;
|
||||
import org.dspace.content.virtual.VirtualMetadataPopulator;
|
||||
import org.dspace.contentreport.QueryPredicate;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogHelper;
|
||||
@@ -175,7 +176,6 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
|
||||
private QAEventsDAO qaEventsDao;
|
||||
|
||||
protected ItemServiceImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -275,9 +275,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
|
||||
+ template.getID()));
|
||||
|
||||
return template;
|
||||
} else {
|
||||
return collection.getTemplateItem();
|
||||
}
|
||||
return collection.getTemplateItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1190,9 +1189,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
|
||||
if (item.getOwningCollection() == null) {
|
||||
if (!isInProgressSubmission(context, item)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return collectionService.canEditBoolean(context, item.getOwningCollection(), false);
|
||||
@@ -1284,8 +1282,8 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
if (!authorizeService
|
||||
.isAnIdenticalPolicyAlreadyInPlace(context, dso, defaultPolicy.getGroup(), Constants.READ,
|
||||
defaultPolicy.getID()) &&
|
||||
(!appendMode && this.isNotAlreadyACustomRPOfThisTypeOnDSO(context, dso) ||
|
||||
appendMode && this.shouldBeAppended(context, dso, defaultPolicy))) {
|
||||
(!appendMode && isNotAlreadyACustomRPOfThisTypeOnDSO(context, dso) ||
|
||||
appendMode && shouldBeAppended(context, dso, defaultPolicy))) {
|
||||
ResourcePolicy newPolicy = resourcePolicyService.clone(context, defaultPolicy);
|
||||
newPolicy.setdSpaceObject(dso);
|
||||
newPolicy.setAction(Constants.READ);
|
||||
@@ -1384,9 +1382,8 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
|
||||
if (Item.ANY.equals(value)) {
|
||||
return itemDAO.findByMetadataField(context, mdf, null, true);
|
||||
} else {
|
||||
return itemDAO.findByMetadataField(context, mdf, value, true);
|
||||
}
|
||||
return itemDAO.findByMetadataField(context, mdf, value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1430,19 +1427,24 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
|
||||
if (Item.ANY.equals(value)) {
|
||||
return itemDAO.findByMetadataField(context, mdf, null, true);
|
||||
} else {
|
||||
return itemDAO.findByMetadataField(context, mdf, value, true);
|
||||
}
|
||||
return itemDAO.findByMetadataField(context, mdf, value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
return itemDAO
|
||||
.findByMetadataQuery(context, listFieldList, query_op, query_val, collectionUuids, regexClause, offset,
|
||||
limit);
|
||||
public List<Item> findByMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, long offset, int limit)
|
||||
throws SQLException {
|
||||
return itemDAO.findByMetadataQuery(context, queryPredicates, collectionUuids, "text_value ~ ?",
|
||||
offset, limit);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long countForMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids)
|
||||
throws SQLException {
|
||||
return itemDAO.countForMetadataQuery(context, queryPredicates, collectionUuids, "text_value ~ ?");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1508,20 +1510,19 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
Collection ownCollection = item.getOwningCollection();
|
||||
if (ownCollection != null) {
|
||||
return ownCollection;
|
||||
} else {
|
||||
InProgressSubmission inprogress = ContentServiceFactory.getInstance().getWorkspaceItemService()
|
||||
.findByItem(context,
|
||||
item);
|
||||
if (inprogress == null) {
|
||||
inprogress = WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(context, item);
|
||||
}
|
||||
|
||||
if (inprogress != null) {
|
||||
return inprogress.getCollection();
|
||||
}
|
||||
// is a template item?
|
||||
return item.getTemplateItemOf();
|
||||
}
|
||||
InProgressSubmission inprogress = ContentServiceFactory.getInstance().getWorkspaceItemService()
|
||||
.findByItem(context,
|
||||
item);
|
||||
if (inprogress == null) {
|
||||
inprogress = WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(context, item);
|
||||
}
|
||||
|
||||
if (inprogress != null) {
|
||||
return inprogress.getCollection();
|
||||
}
|
||||
// is a template item?
|
||||
return item.getTemplateItemOf();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1618,10 +1619,14 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
|
||||
@Override
|
||||
public Item findByIdOrLegacyId(Context context, String id) throws SQLException {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
} else {
|
||||
try {
|
||||
if (StringUtils.isNumeric(id)) {
|
||||
return findByLegacyId(context, Integer.parseInt(id));
|
||||
}
|
||||
return find(context, UUID.fromString(id));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not a valid legacy ID or valid UUID
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.NotifyMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
@@ -32,6 +33,7 @@ public class QAEvent {
|
||||
public static final String DISCARDED = "discarded";
|
||||
|
||||
public static final String OPENAIRE_SOURCE = "openaire";
|
||||
public static final String DSPACE_USERS_SOURCE = "DSpaceUsers";
|
||||
public static final String COAR_NOTIFY_SOURCE = "coar-notify";
|
||||
|
||||
private String source;
|
||||
@@ -65,8 +67,7 @@ public class QAEvent {
|
||||
|
||||
private String status = "PENDING";
|
||||
|
||||
public QAEvent() {
|
||||
}
|
||||
public QAEvent() {}
|
||||
|
||||
public QAEvent(String source, String originalId, String target, String title,
|
||||
String topic, double trust, String message, Date lastUpdate) {
|
||||
@@ -84,7 +85,6 @@ public class QAEvent {
|
||||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getOriginalId() {
|
||||
@@ -205,18 +205,16 @@ public class QAEvent {
|
||||
}
|
||||
|
||||
public Class<? extends QAMessageDTO> getMessageDtoClass() {
|
||||
Class<? extends QAMessageDTO> result = null;
|
||||
switch (getSource()) {
|
||||
case OPENAIRE_SOURCE:
|
||||
result = OpenaireMessageDTO.class;
|
||||
break;
|
||||
return OpenaireMessageDTO.class;
|
||||
case COAR_NOTIFY_SOURCE:
|
||||
result = NotifyMessageDTO.class;
|
||||
break;
|
||||
return NotifyMessageDTO.class;
|
||||
case DSPACE_USERS_SOURCE:
|
||||
return CorrectionTypeMessageDTO.class;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown event's source: " + getSource());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -242,7 +242,7 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
// check if it is the requested collection
|
||||
Map<String, ChoiceAuthority> controllerFormDef = controllerFormDefinitions.get(fieldKey);
|
||||
SubmissionConfig submissionConfig = submissionConfigService
|
||||
.getSubmissionConfigByCollection(collection.getHandle());
|
||||
.getSubmissionConfigByCollection(collection);
|
||||
String submissionName = submissionConfig.getSubmissionName();
|
||||
// check if the requested collection has a submission definition that use an authority for the metadata
|
||||
if (controllerFormDef.containsKey(submissionName)) {
|
||||
@@ -495,7 +495,7 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
try {
|
||||
configReaderService = SubmissionServiceFactory.getInstance().getSubmissionConfigService();
|
||||
SubmissionConfig submissionName = configReaderService
|
||||
.getSubmissionConfigByCollection(collection.getHandle());
|
||||
.getSubmissionConfigByCollection(collection);
|
||||
ma = controllerFormDefinitions.get(fieldKey).get(submissionName.getSubmissionName());
|
||||
} catch (SubmissionConfigReaderException e) {
|
||||
// the system is in an illegal state as the submission definition is not valid
|
||||
|
@@ -156,7 +156,8 @@ public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority
|
||||
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)) {
|
||||
// In a DCInputAuthority context, a user will want to query the labels, not the values
|
||||
if (query == null || StringUtils.containsIgnoreCase(labelsLocale[i], query)) {
|
||||
if (found >= start && v.size() < limit) {
|
||||
v.add(new Choice(null, valuesLocale[i], labelsLocale[i]));
|
||||
if (valuesLocale[i].equalsIgnoreCase(query)) {
|
||||
|
@@ -59,7 +59,37 @@ public class SHERPARoMEOJournalTitle implements ChoiceAuthority {
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
return getMatches(text, 0, 1, locale);
|
||||
// punt if there is no query text
|
||||
if (text == null || text.trim().length() == 0) {
|
||||
return new Choices(true);
|
||||
}
|
||||
int limit = 10;
|
||||
SHERPAService sherpaService = new DSpace().getSingletonService(SHERPAService.class);
|
||||
SHERPAResponse sherpaResponse = sherpaService.performRequest("publication", "title",
|
||||
"equals", text, 0, limit);
|
||||
Choices result;
|
||||
if (CollectionUtils.isNotEmpty(sherpaResponse.getJournals())) {
|
||||
List<Choice> list = sherpaResponse
|
||||
.getJournals().stream()
|
||||
.map(sherpaJournal -> new Choice(sherpaJournal.getIssns().get(0),
|
||||
sherpaJournal.getTitles().get(0), sherpaJournal.getTitles().get(0)))
|
||||
.collect(Collectors.toList());
|
||||
int total = sherpaResponse.getJournals().size();
|
||||
|
||||
int confidence;
|
||||
if (list.isEmpty()) {
|
||||
confidence = Choices.CF_NOTFOUND;
|
||||
} else if (list.size() == 1) {
|
||||
confidence = Choices.CF_UNCERTAIN;
|
||||
} else {
|
||||
confidence = Choices.CF_AMBIGUOUS;
|
||||
}
|
||||
result = new Choices(list.toArray(new Choice[list.size()]), 0, total, confidence,
|
||||
total > limit);
|
||||
} else {
|
||||
result = new Choices(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -60,7 +60,38 @@ public class SHERPARoMEOPublisher implements ChoiceAuthority {
|
||||
|
||||
@Override
|
||||
public Choices getBestMatch(String text, String locale) {
|
||||
return getMatches(text, 0, 1, locale);
|
||||
// punt if there is no query text
|
||||
if (text == null || text.trim().length() == 0) {
|
||||
return new Choices(true);
|
||||
}
|
||||
int limit = 10;
|
||||
SHERPAService sherpaService = new DSpace().getSingletonService(SHERPAService.class);
|
||||
SHERPAPublisherResponse sherpaResponse = sherpaService.performPublisherRequest("publisher", "name",
|
||||
"equals", text, 0, limit);
|
||||
Choices result;
|
||||
if (CollectionUtils.isNotEmpty(sherpaResponse.getPublishers())) {
|
||||
List<Choice> list = sherpaResponse
|
||||
.getPublishers().stream()
|
||||
.map(sherpaPublisher ->
|
||||
new Choice(sherpaPublisher.getIdentifier(),
|
||||
sherpaPublisher.getName(), sherpaPublisher.getName()))
|
||||
.collect(Collectors.toList());
|
||||
int total = sherpaResponse.getPublishers().size();
|
||||
|
||||
int confidence;
|
||||
if (list.isEmpty()) {
|
||||
confidence = Choices.CF_NOTFOUND;
|
||||
} else if (list.size() == 1) {
|
||||
confidence = Choices.CF_UNCERTAIN;
|
||||
} else {
|
||||
confidence = Choices.CF_AMBIGUOUS;
|
||||
}
|
||||
result = new Choices(list.toArray(new Choice[list.size()]), 0, total, confidence,
|
||||
total > limit);
|
||||
} else {
|
||||
result = new Choices(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -17,6 +17,7 @@ import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.contentreport.QueryPredicate;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
|
||||
@@ -28,12 +29,11 @@ import org.dspace.eperson.EPerson;
|
||||
* @author kevinvandevelde at atmire.com
|
||||
*/
|
||||
public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
public Iterator<Item> findAll(Context context, boolean archived) throws SQLException;
|
||||
Iterator<Item> findAll(Context context, boolean archived) throws SQLException;
|
||||
|
||||
public Iterator<Item> findAll(Context context, boolean archived, int limit, int offset) throws SQLException;
|
||||
Iterator<Item> findAll(Context context, boolean archived, int limit, int offset) throws SQLException;
|
||||
|
||||
@Deprecated
|
||||
public Iterator<Item> findAll(Context context, boolean archived, boolean withdrawn) throws SQLException;
|
||||
@Deprecated Iterator<Item> findAll(Context context, boolean archived, boolean withdrawn) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all items that are:
|
||||
@@ -46,7 +46,7 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return iterator over all regular items.
|
||||
* @throws SQLException if database error.
|
||||
*/
|
||||
public Iterator<Item> findAllRegularItems(Context context) throws SQLException;
|
||||
Iterator<Item> findAllRegularItems(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all Items modified since a Date.
|
||||
@@ -56,10 +56,10 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return iterator over items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findByLastModifiedSince(Context context, Date since)
|
||||
Iterator<Item> findByLastModifiedSince(Context context, Date since)
|
||||
throws SQLException;
|
||||
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson) throws SQLException;
|
||||
Iterator<Item> findBySubmitter(Context context, EPerson eperson) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all the items by a given submitter. The order is
|
||||
@@ -71,23 +71,40 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return an iterator over the items submitted by eperson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
throws SQLException;
|
||||
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, MetadataField metadataField, int limit)
|
||||
Iterator<Item> findBySubmitter(Context context, EPerson eperson, MetadataField metadataField, int limit)
|
||||
throws SQLException;
|
||||
|
||||
public Iterator<Item> findByMetadataField(Context context, MetadataField metadataField, String value,
|
||||
Iterator<Item> findByMetadataField(Context context, MetadataField metadataField, String value,
|
||||
boolean inArchive) throws SQLException;
|
||||
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit) throws SQLException;
|
||||
/**
|
||||
* Returns all the Items that belong to the specified aollections (if any)
|
||||
* and match the provided predicates.
|
||||
* @param context The relevant DSpace context
|
||||
* @param queryPredicates List of predicates that returned items are required to match
|
||||
* @param collectionUuids UUIDs of the collections to search.
|
||||
* If none are provided, the entire repository will be searched.
|
||||
* @param regexClause Syntactic expression used to query the database using a regular expression
|
||||
* (e.g.: "text_value ~ ?")
|
||||
* @param offset The offset for the query
|
||||
* @param limit Maximum number of items to return
|
||||
* @return A list containing the items that match the provided criteria
|
||||
* @throws SQLException if something goes wrong
|
||||
*/
|
||||
List<Item> findByMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, String regexClause,
|
||||
long offset, int limit) throws SQLException;
|
||||
|
||||
public Iterator<Item> findByAuthorityValue(Context context, MetadataField metadataField, String authority,
|
||||
long countForMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, String regexClause) throws SQLException;
|
||||
|
||||
Iterator<Item> findByAuthorityValue(Context context, MetadataField metadataField, String authority,
|
||||
boolean inArchive) throws SQLException;
|
||||
|
||||
public Iterator<Item> findArchivedByCollection(Context context, Collection collection, Integer limit,
|
||||
Iterator<Item> findArchivedByCollection(Context context, Collection collection, Integer limit,
|
||||
Integer offset) throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -100,7 +117,7 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return An iterator containing the items for which the constraints hold true
|
||||
* @throws SQLException If something goes wrong
|
||||
*/
|
||||
public Iterator<Item> findArchivedByCollectionExcludingOwning(Context context, Collection collection, Integer limit,
|
||||
Iterator<Item> findArchivedByCollectionExcludingOwning(Context context, Collection collection, Integer limit,
|
||||
Integer offset) throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -111,11 +128,11 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return The total amount of items that fit the constraints
|
||||
* @throws SQLException If something goes wrong
|
||||
*/
|
||||
public int countArchivedByCollectionExcludingOwning(Context context, Collection collection) throws SQLException;
|
||||
int countArchivedByCollectionExcludingOwning(Context context, Collection collection) throws SQLException;
|
||||
|
||||
public Iterator<Item> findAllByCollection(Context context, Collection collection) throws SQLException;
|
||||
Iterator<Item> findAllByCollection(Context context, Collection collection) throws SQLException;
|
||||
|
||||
public Iterator<Item> findAllByCollection(Context context, Collection collection, Integer limit, Integer offset)
|
||||
Iterator<Item> findAllByCollection(Context context, Collection collection, Integer limit, Integer offset)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -128,7 +145,7 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return item count
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countItems(Context context, Collection collection, boolean includeArchived, boolean includeWithdrawn)
|
||||
int countItems(Context context, Collection collection, boolean includeArchived, boolean includeWithdrawn)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -144,7 +161,7 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return item count
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countItems(Context context, List<Collection> collections, boolean includeArchived,
|
||||
int countItems(Context context, List<Collection> collections, boolean includeArchived,
|
||||
boolean includeWithdrawn) throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -158,7 +175,7 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return iterator over items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findAll(Context context, boolean archived,
|
||||
Iterator<Item> findAll(Context context, boolean archived,
|
||||
boolean withdrawn, boolean discoverable, Date lastModified)
|
||||
throws SQLException;
|
||||
|
||||
@@ -192,7 +209,7 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
* @return count of items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countItems(Context context, EPerson submitter, boolean includeArchived, boolean includeWithdrawn)
|
||||
int countItems(Context context, EPerson submitter, boolean includeArchived, boolean includeWithdrawn)
|
||||
throws SQLException;
|
||||
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
package org.dspace.content.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
@@ -16,28 +17,27 @@ import java.util.UUID;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaBuilder.In;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.DSpaceObject_;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.Item_;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.MetadataValue_;
|
||||
import org.dspace.content.dao.ItemDAO;
|
||||
import org.dspace.contentreport.QueryOperator;
|
||||
import org.dspace.contentreport.QueryPredicate;
|
||||
import org.dspace.core.AbstractHibernateDSODAO;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.criterion.Criterion;
|
||||
import org.hibernate.criterion.DetachedCriteria;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.criterion.Property;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.criterion.Subqueries;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.dspace.util.JpaCriteriaBuilderKit;
|
||||
|
||||
/**
|
||||
* Hibernate implementation of the Database Access Object interface class for the Item object.
|
||||
@@ -50,7 +50,6 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemDAOImpl.class);
|
||||
|
||||
protected ItemDAOImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,118 +173,103 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
|
||||
return iterate(query);
|
||||
}
|
||||
|
||||
enum OP {
|
||||
equals {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").eq(val);
|
||||
}
|
||||
},
|
||||
not_equals {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.equals.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
like {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").like(val);
|
||||
}
|
||||
},
|
||||
not_like {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.like.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
contains {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").like("%" + val + "%");
|
||||
}
|
||||
},
|
||||
doesnt_contain {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.contains.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
exists {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").isNotNull();
|
||||
}
|
||||
},
|
||||
doesnt_exist {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.exists.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
matches {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Restrictions.sqlRestriction(regexClause, val, StandardBasicTypes.STRING);
|
||||
}
|
||||
},
|
||||
doesnt_match {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.matches.buildPredicate(val, regexClause);
|
||||
}
|
||||
|
||||
};
|
||||
public abstract Criterion buildPredicate(String val, String regexClause);
|
||||
@Override
|
||||
public List<Item> findByMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, String regexClause, long offset, int limit) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<Item> criteriaQuery = getCriteriaQuery(criteriaBuilder, Item.class);
|
||||
Root<Item> itemRoot = criteriaQuery.from(Item.class);
|
||||
criteriaQuery.select(itemRoot);
|
||||
List<Predicate> predicates = toPredicates(criteriaBuilder, criteriaQuery, itemRoot,
|
||||
queryPredicates, collectionUuids, regexClause);
|
||||
criteriaQuery.where(criteriaBuilder.and(predicates.stream().toArray(Predicate[]::new)));
|
||||
criteriaQuery.orderBy(criteriaBuilder.asc(itemRoot.get(DSpaceObject_.id)));
|
||||
criteriaQuery.groupBy(itemRoot.get(DSpaceObject_.id));
|
||||
try {
|
||||
return list(context, criteriaQuery, false, Item.class, limit, (int) offset);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit) throws SQLException {
|
||||
public long countForMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, String regexClause) throws SQLException {
|
||||
// Build the query infrastructure
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<Item> criteriaQuery = getCriteriaQuery(criteriaBuilder, Item.class);
|
||||
// Select
|
||||
Root<Item> itemRoot = criteriaQuery.from(Item.class);
|
||||
// Apply the selected predicates
|
||||
List<Predicate> predicates = toPredicates(criteriaBuilder, criteriaQuery, itemRoot,
|
||||
queryPredicates, collectionUuids, regexClause);
|
||||
criteriaQuery.where(criteriaBuilder.and(predicates.stream().toArray(Predicate[]::new)));
|
||||
// Execute the query
|
||||
return countLong(context, criteriaQuery, criteriaBuilder, itemRoot);
|
||||
}
|
||||
|
||||
Criteria criteria = getHibernateSession(context).createCriteria(Item.class, "item");
|
||||
criteria.setFirstResult(offset);
|
||||
criteria.setMaxResults(limit);
|
||||
private <T> List<Predicate> toPredicates(CriteriaBuilder criteriaBuilder, CriteriaQuery<T> query,
|
||||
Root<Item> root, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, String regexClause) {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (!collectionUuids.isEmpty()) {
|
||||
DetachedCriteria dcollCriteria = DetachedCriteria.forClass(Collection.class, "coll");
|
||||
dcollCriteria.setProjection(Projections.property("coll.id"));
|
||||
dcollCriteria.add(Restrictions.eqProperty("coll.id", "item.owningCollection"));
|
||||
dcollCriteria.add(Restrictions.in("coll.id", collectionUuids));
|
||||
criteria.add(Subqueries.exists(dcollCriteria));
|
||||
Subquery<Collection> scollQuery = query.subquery(Collection.class);
|
||||
Root<Collection> collRoot = scollQuery.from(Collection.class);
|
||||
In<UUID> inColls = criteriaBuilder.in(collRoot.get(DSpaceObject_.ID));
|
||||
collectionUuids.forEach(inColls::value);
|
||||
scollQuery.select(collRoot.get(DSpaceObject_.ID))
|
||||
.where(criteriaBuilder.and(
|
||||
criteriaBuilder.equal(collRoot.get(DSpaceObject_.ID),
|
||||
root.get(Item_.OWNING_COLLECTION).get(DSpaceObject_.ID)),
|
||||
collRoot.get(DSpaceObject_.ID).in(collectionUuids)
|
||||
));
|
||||
predicates.add(criteriaBuilder.exists(scollQuery));
|
||||
}
|
||||
|
||||
int index = Math.min(listFieldList.size(), Math.min(query_op.size(), query_val.size()));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < index; i++) {
|
||||
OP op = OP.valueOf(query_op.get(i));
|
||||
for (int i = 0; i < queryPredicates.size(); i++) {
|
||||
QueryPredicate predicate = queryPredicates.get(i);
|
||||
QueryOperator op = predicate.getOperator();
|
||||
if (op == null) {
|
||||
log.warn("Skipping Invalid Operator: " + query_op.get(i));
|
||||
log.warn("Skipping Invalid Operator: null");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op == OP.matches || op == OP.doesnt_match) {
|
||||
if (op.getUsesRegex()) {
|
||||
if (regexClause.isEmpty()) {
|
||||
log.warn("Skipping Unsupported Regex Operator: " + query_op.get(i));
|
||||
log.warn("Skipping Unsupported Regex Operator: " + op);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DetachedCriteria subcriteria = DetachedCriteria.forClass(MetadataValue.class, "mv");
|
||||
subcriteria.add(Property.forName("mv.dSpaceObject").eqProperty("item.id"));
|
||||
subcriteria.setProjection(Projections.property("mv.dSpaceObject"));
|
||||
List<Predicate> mvPredicates = new ArrayList<>();
|
||||
Subquery<MetadataValue> mvQuery = query.subquery(MetadataValue.class);
|
||||
Root<MetadataValue> mvRoot = mvQuery.from(MetadataValue.class);
|
||||
mvPredicates.add(criteriaBuilder.equal(
|
||||
mvRoot.get(MetadataValue_.D_SPACE_OBJECT), root.get(DSpaceObject_.ID)));
|
||||
|
||||
if (!listFieldList.get(i).isEmpty()) {
|
||||
subcriteria.add(Restrictions.in("metadataField", listFieldList.get(i)));
|
||||
if (!predicate.getFields().isEmpty()) {
|
||||
In<MetadataField> inFields = criteriaBuilder.in(mvRoot.get(MetadataValue_.METADATA_FIELD));
|
||||
predicate.getFields().forEach(inFields::value);
|
||||
mvPredicates.add(inFields);
|
||||
}
|
||||
|
||||
subcriteria.add(op.buildPredicate(query_val.get(i), regexClause));
|
||||
JpaCriteriaBuilderKit<MetadataValue> jpaKit = new JpaCriteriaBuilderKit<>(criteriaBuilder, mvQuery, mvRoot);
|
||||
mvPredicates.add(op.buildJpaPredicate(predicate.getValue(), regexClause, jpaKit));
|
||||
|
||||
if (op == OP.exists || op == OP.equals || op == OP.like || op == OP.contains || op == OP.matches) {
|
||||
criteria.add(Subqueries.exists(subcriteria));
|
||||
mvQuery.select(mvRoot.get(MetadataValue_.D_SPACE_OBJECT))
|
||||
.where(mvPredicates.stream().toArray(Predicate[]::new));
|
||||
|
||||
if (op.getNegate()) {
|
||||
predicates.add(criteriaBuilder.not(criteriaBuilder.exists(mvQuery)));
|
||||
} else {
|
||||
criteria.add(Subqueries.notExists(subcriteria));
|
||||
predicates.add(criteriaBuilder.exists(mvQuery));
|
||||
}
|
||||
}
|
||||
criteria.addOrder(Order.asc("item.id"));
|
||||
|
||||
log.debug(String.format("Running custom query with %d filters", index));
|
||||
|
||||
return ((List<Item>) criteria.list()).iterator();
|
||||
|
||||
log.debug(String.format("Running custom query with %d filters", queryPredicates.size()));
|
||||
return predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -322,22 +306,22 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
|
||||
public Iterator<Item> findArchivedByCollectionExcludingOwning(Context context, Collection collection, Integer limit,
|
||||
Integer offset) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Item.class);
|
||||
CriteriaQuery<Item> criteriaQuery = getCriteriaQuery(criteriaBuilder, Item.class);
|
||||
Root<Item> itemRoot = criteriaQuery.from(Item.class);
|
||||
criteriaQuery.select(itemRoot);
|
||||
criteriaQuery.where(criteriaBuilder.and(
|
||||
criteriaBuilder.notEqual(itemRoot.get(Item_.owningCollection), collection),
|
||||
criteriaBuilder.isMember(collection, itemRoot.get(Item_.collections)),
|
||||
criteriaBuilder.isTrue(itemRoot.get(Item_.inArchive))));
|
||||
criteriaQuery.orderBy(criteriaBuilder.asc(itemRoot.get(Item_.id)));
|
||||
criteriaQuery.groupBy(itemRoot.get(Item_.id));
|
||||
criteriaQuery.orderBy(criteriaBuilder.asc(itemRoot.get(DSpaceObject_.id)));
|
||||
criteriaQuery.groupBy(itemRoot.get(DSpaceObject_.id));
|
||||
return list(context, criteriaQuery, false, Item.class, limit, offset).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countArchivedByCollectionExcludingOwning(Context context, Collection collection) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Item.class);
|
||||
CriteriaQuery<Item> criteriaQuery = getCriteriaQuery(criteriaBuilder, Item.class);
|
||||
Root<Item> itemRoot = criteriaQuery.from(Item.class);
|
||||
criteriaQuery.select(itemRoot);
|
||||
criteriaQuery.where(criteriaBuilder.and(
|
||||
|
@@ -23,10 +23,10 @@ import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.EntityType;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.Thumbnail;
|
||||
import org.dspace.content.WorkspaceItem;
|
||||
import org.dspace.contentreport.QueryPredicate;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.dspace.eperson.EPerson;
|
||||
@@ -42,7 +42,7 @@ import org.dspace.eperson.Group;
|
||||
public interface ItemService
|
||||
extends DSpaceObjectService<Item>, DSpaceObjectLegacySupportService<Item> {
|
||||
|
||||
public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException;
|
||||
Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException;
|
||||
|
||||
/**
|
||||
* Create a new item, with a new internal ID. Authorization is done
|
||||
@@ -54,7 +54,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException;
|
||||
Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Create a new item, with a provided ID. Authorisation is done
|
||||
@@ -67,7 +67,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public Item create(Context context, WorkspaceItem workspaceItem, UUID uuid) throws SQLException, AuthorizeException;
|
||||
Item create(Context context, WorkspaceItem workspaceItem, UUID uuid) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Create an empty template item for this collection. If one already exists,
|
||||
@@ -81,7 +81,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public Item createTemplateItem(Context context, Collection collection) throws SQLException, AuthorizeException;
|
||||
Item createTemplateItem(Context context, Collection collection) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Get all the items in the archive. Only items with the "in archive" flag
|
||||
@@ -91,7 +91,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the archive.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findAll(Context context) throws SQLException;
|
||||
Iterator<Item> findAll(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get all the items in the archive. Only items with the "in archive" flag
|
||||
@@ -103,7 +103,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the archive.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findAll(Context context, Integer limit, Integer offset) throws SQLException;
|
||||
Iterator<Item> findAll(Context context, Integer limit, Integer offset) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get all "final" items in the archive, both archived ("in archive" flag) or
|
||||
@@ -113,8 +113,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the archive.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
@Deprecated
|
||||
public Iterator<Item> findAllUnfiltered(Context context) throws SQLException;
|
||||
@Deprecated Iterator<Item> findAllUnfiltered(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all items that are:
|
||||
@@ -127,7 +126,7 @@ public interface ItemService
|
||||
* @return iterator over all regular items.
|
||||
* @throws SQLException if database error.
|
||||
*/
|
||||
public Iterator<Item> findAllRegularItems(Context context) throws SQLException;
|
||||
Iterator<Item> findAllRegularItems(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all the items in the archive by a given submitter. The order is
|
||||
@@ -138,7 +137,7 @@ public interface ItemService
|
||||
* @return an iterator over the items submitted by eperson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson)
|
||||
Iterator<Item> findBySubmitter(Context context, EPerson eperson)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -153,7 +152,7 @@ public interface ItemService
|
||||
* @return an iterator over the items submitted by eperson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
Iterator<Item> findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -165,7 +164,7 @@ public interface ItemService
|
||||
* @return an iterator over the items submitted by eperson
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findBySubmitterDateSorted(Context context, EPerson eperson, Integer limit)
|
||||
Iterator<Item> findBySubmitterDateSorted(Context context, EPerson eperson, Integer limit)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -176,7 +175,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findByCollection(Context context, Collection collection) throws SQLException;
|
||||
Iterator<Item> findByCollection(Context context, Collection collection) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get all the archived items in this collection. The order is indeterminate.
|
||||
@@ -188,7 +187,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findByCollection(Context context, Collection collection, Integer limit, Integer offset)
|
||||
Iterator<Item> findByCollection(Context context, Collection collection, Integer limit, Integer offset)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -201,7 +200,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findByCollectionMapping(Context context, Collection collection, Integer limit, Integer offset)
|
||||
Iterator<Item> findByCollectionMapping(Context context, Collection collection, Integer limit, Integer offset)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -212,7 +211,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countByCollectionMapping(Context context, Collection collection) throws SQLException;
|
||||
int countByCollectionMapping(Context context, Collection collection) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get all the items (including private and withdrawn) in this collection. The order is indeterminate.
|
||||
@@ -224,7 +223,7 @@ public interface ItemService
|
||||
* @param offset offset value
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findAllByCollection(Context context, Collection collection, Integer limit, Integer offset)
|
||||
Iterator<Item> findAllByCollection(Context context, Collection collection, Integer limit, Integer offset)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -235,7 +234,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findInArchiveOrWithdrawnDiscoverableModifiedSince(Context context, Date since)
|
||||
Iterator<Item> findInArchiveOrWithdrawnDiscoverableModifiedSince(Context context, Date since)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -245,7 +244,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findInArchiveOrWithdrawnNonDiscoverableModifiedSince(Context context, Date since)
|
||||
Iterator<Item> findInArchiveOrWithdrawnNonDiscoverableModifiedSince(Context context, Date since)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -256,7 +255,7 @@ public interface ItemService
|
||||
* @return an iterator over the items in the collection.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findAllByCollection(Context context, Collection collection) throws SQLException;
|
||||
Iterator<Item> findAllByCollection(Context context, Collection collection) throws SQLException;
|
||||
|
||||
/**
|
||||
* See whether this Item is contained by a given Collection.
|
||||
@@ -266,7 +265,7 @@ public interface ItemService
|
||||
* @return true if {@code collection} contains this Item.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public boolean isIn(Item item, Collection collection) throws SQLException;
|
||||
boolean isIn(Item item, Collection collection) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get the communities this item is in. Returns an unordered array of the
|
||||
@@ -278,7 +277,7 @@ public interface ItemService
|
||||
* @return the communities this item is in.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<Community> getCommunities(Context context, Item item) throws SQLException;
|
||||
List<Community> getCommunities(Context context, Item item) throws SQLException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -289,7 +288,7 @@ public interface ItemService
|
||||
* @return the bundles in an unordered array
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<Bundle> getBundles(Item item, String name) throws SQLException;
|
||||
List<Bundle> getBundles(Item item, String name) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add an existing bundle to this item. This has immediate effect.
|
||||
@@ -300,7 +299,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void addBundle(Context context, Item item, Bundle bundle) throws SQLException, AuthorizeException;
|
||||
void addBundle(Context context, Item item, Bundle bundle) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Remove a bundle. This may result in the bundle being deleted, if the
|
||||
@@ -313,7 +312,7 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public void removeBundle(Context context, Item item, Bundle bundle) throws SQLException, AuthorizeException,
|
||||
void removeBundle(Context context, Item item, Bundle bundle) throws SQLException, AuthorizeException,
|
||||
IOException;
|
||||
|
||||
/**
|
||||
@@ -326,7 +325,7 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public void removeAllBundles(Context context, Item item) throws AuthorizeException, SQLException, IOException;
|
||||
void removeAllBundles(Context context, Item item) throws AuthorizeException, SQLException, IOException;
|
||||
|
||||
/**
|
||||
* Create a single bitstream in a new bundle. Provided as a convenience
|
||||
@@ -341,7 +340,7 @@ public interface ItemService
|
||||
* @throws IOException if IO error
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Bitstream createSingleBitstream(Context context, InputStream is, Item item, String name)
|
||||
Bitstream createSingleBitstream(Context context, InputStream is, Item item, String name)
|
||||
throws AuthorizeException, IOException, SQLException;
|
||||
|
||||
/**
|
||||
@@ -355,7 +354,7 @@ public interface ItemService
|
||||
* @throws IOException if IO error
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Bitstream createSingleBitstream(Context context, InputStream is, Item item)
|
||||
Bitstream createSingleBitstream(Context context, InputStream is, Item item)
|
||||
throws AuthorizeException, IOException, SQLException;
|
||||
|
||||
/**
|
||||
@@ -368,7 +367,7 @@ public interface ItemService
|
||||
* @return non-internal bitstreams.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<Bitstream> getNonInternalBitstreams(Context context, Item item) throws SQLException;
|
||||
List<Bitstream> getNonInternalBitstreams(Context context, Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* Remove just the DSpace license from an item This is useful to update the
|
||||
@@ -383,7 +382,7 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public void removeDSpaceLicense(Context context, Item item) throws SQLException, AuthorizeException,
|
||||
void removeDSpaceLicense(Context context, Item item) throws SQLException, AuthorizeException,
|
||||
IOException;
|
||||
|
||||
/**
|
||||
@@ -395,7 +394,7 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public void removeLicenses(Context context, Item item) throws SQLException, AuthorizeException, IOException;
|
||||
void removeLicenses(Context context, Item item) throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
/**
|
||||
* Withdraw the item from the archive. It is kept in place, and the content
|
||||
@@ -406,7 +405,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void withdraw(Context context, Item item) throws SQLException, AuthorizeException;
|
||||
void withdraw(Context context, Item item) throws SQLException, AuthorizeException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -417,7 +416,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void reinstate(Context context, Item item) throws SQLException, AuthorizeException;
|
||||
void reinstate(Context context, Item item) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Return true if this Collection 'owns' this item
|
||||
@@ -426,7 +425,7 @@ public interface ItemService
|
||||
* @param collection Collection
|
||||
* @return true if this Collection owns this item
|
||||
*/
|
||||
public boolean isOwningCollection(Item item, Collection collection);
|
||||
boolean isOwningCollection(Item item, Collection collection);
|
||||
|
||||
/**
|
||||
* remove all of the policies for item and replace them with a new list of
|
||||
@@ -440,7 +439,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void replaceAllItemPolicies(Context context, Item item, List<ResourcePolicy> newpolicies)
|
||||
void replaceAllItemPolicies(Context context, Item item, List<ResourcePolicy> newpolicies)
|
||||
throws SQLException,
|
||||
AuthorizeException;
|
||||
|
||||
@@ -456,7 +455,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void replaceAllBitstreamPolicies(Context context, Item item, List<ResourcePolicy> newpolicies)
|
||||
void replaceAllBitstreamPolicies(Context context, Item item, List<ResourcePolicy> newpolicies)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
|
||||
@@ -470,7 +469,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void removeGroupPolicies(Context context, Item item, Group group) throws SQLException, AuthorizeException;
|
||||
void removeGroupPolicies(Context context, Item item, Group group) throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
* Remove all policies on an item and its contents, and replace them with
|
||||
@@ -485,7 +484,7 @@ public interface ItemService
|
||||
* draconian, but default policies must be enforced.
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection)
|
||||
void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection)
|
||||
throws java.sql.SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
@@ -504,7 +503,7 @@ public interface ItemService
|
||||
* draconian, but default policies must be enforced.
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection,
|
||||
void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection,
|
||||
boolean overrideItemReadPolicies)
|
||||
throws java.sql.SQLException, AuthorizeException;
|
||||
|
||||
@@ -517,14 +516,14 @@ public interface ItemService
|
||||
* already applied to the bundle/bitstream. Collection's policies are inherited
|
||||
* if there are no other policies defined or if the append mode is defined by
|
||||
* the configuration via the core.authorization.installitem.inheritance-read.append-mode property
|
||||
*
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param item Item to adjust policies on
|
||||
* @param collection Collection
|
||||
* @throws SQLException If database error
|
||||
* @throws AuthorizeException If authorization error
|
||||
*/
|
||||
public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection)
|
||||
void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
@@ -545,7 +544,7 @@ public interface ItemService
|
||||
* @throws SQLException If database error
|
||||
* @throws AuthorizeException If authorization error
|
||||
*/
|
||||
public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection,
|
||||
void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection,
|
||||
boolean replaceReadRPWithCollectionRP)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
@@ -566,7 +565,7 @@ public interface ItemService
|
||||
* @throws SQLException If database error
|
||||
* @throws AuthorizeException If authorization error
|
||||
*/
|
||||
public void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream)
|
||||
void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
@@ -588,7 +587,7 @@ public interface ItemService
|
||||
* @throws SQLException If database error
|
||||
* @throws AuthorizeException If authorization error
|
||||
*/
|
||||
public void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream,
|
||||
void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream,
|
||||
boolean replaceReadRPWithCollectionRP)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
@@ -600,14 +599,14 @@ public interface ItemService
|
||||
* inherited as appropriate. Collection's policies are inherited if there are no
|
||||
* other policies defined or if the append mode is defined by the configuration
|
||||
* via the core.authorization.installitem.inheritance-read.append-mode property
|
||||
*
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param item Item to adjust policies on
|
||||
* @param collection Collection
|
||||
* @throws SQLException If database error
|
||||
* @throws AuthorizeException If authorization error
|
||||
*/
|
||||
public void adjustItemPolicies(Context context, Item item, Collection collection)
|
||||
void adjustItemPolicies(Context context, Item item, Collection collection)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
@@ -626,7 +625,7 @@ public interface ItemService
|
||||
* @throws SQLException If database error
|
||||
* @throws AuthorizeException If authorization error
|
||||
*/
|
||||
public void adjustItemPolicies(Context context, Item item, Collection collection,
|
||||
void adjustItemPolicies(Context context, Item item, Collection collection,
|
||||
boolean replaceReadRPWithCollectionRP)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
@@ -641,7 +640,7 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public void move(Context context, Item item, Collection from, Collection to)
|
||||
void move(Context context, Item item, Collection from, Collection to)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
/**
|
||||
@@ -656,7 +655,7 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public void move(Context context, Item item, Collection from, Collection to, boolean inheritDefaultPolicies)
|
||||
void move(Context context, Item item, Collection from, Collection to, boolean inheritDefaultPolicies)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
/**
|
||||
@@ -667,7 +666,7 @@ public interface ItemService
|
||||
* bitstreams inside
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public boolean hasUploadedFiles(Item item) throws SQLException;
|
||||
boolean hasUploadedFiles(Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get the collections this item is not in.
|
||||
@@ -677,7 +676,7 @@ public interface ItemService
|
||||
* @return the collections this item is not in, if any.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<Collection> getCollectionsNotLinked(Context context, Item item) throws SQLException;
|
||||
List<Collection> getCollectionsNotLinked(Context context, Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* return TRUE if context's user can edit item, false otherwise
|
||||
@@ -687,7 +686,7 @@ public interface ItemService
|
||||
* @return boolean true = current user can edit item
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public boolean canEdit(Context context, Item item) throws java.sql.SQLException;
|
||||
boolean canEdit(Context context, Item item) throws java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* return TRUE if context's user can create new version of the item, false
|
||||
@@ -698,7 +697,7 @@ public interface ItemService
|
||||
* @return boolean true = current user can create new version of the item
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public boolean canCreateNewVersion(Context context, Item item) throws SQLException;
|
||||
boolean canCreateNewVersion(Context context, Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns an iterator of in archive items possessing the passed metadata field, or only
|
||||
@@ -713,7 +712,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public Iterator<Item> findArchivedByMetadataField(Context context, String schema,
|
||||
Iterator<Item> findArchivedByMetadataField(Context context, String schema,
|
||||
String element, String qualifier,
|
||||
String value) throws SQLException, AuthorizeException;
|
||||
|
||||
@@ -728,7 +727,7 @@ public interface ItemService
|
||||
* @throws SQLException if database error
|
||||
* @throws AuthorizeException if authorization error
|
||||
*/
|
||||
public Iterator<Item> findArchivedByMetadataField(Context context, String metadataField, String value)
|
||||
Iterator<Item> findArchivedByMetadataField(Context context, String metadataField, String value)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
@@ -745,14 +744,41 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public Iterator<Item> findByMetadataField(Context context,
|
||||
Iterator<Item> findByMetadataField(Context context,
|
||||
String schema, String element, String qualifier, String value)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
/**
|
||||
* Returns a list of items that match the given predicates, within the
|
||||
* specified collections, if any. This querying method is used by the
|
||||
* Filtered Items report functionality.
|
||||
* @param context DSpace context object
|
||||
* @param queryPredicates metadata field predicates
|
||||
* @param collectionUuids UUIDs of the collections to search
|
||||
* @param offset position in the list to start returning items
|
||||
* @param limit maximum number of items to return
|
||||
* @return a list of matching items in the specified collections,
|
||||
* or in any collection if no collection UUIDs are provided
|
||||
* @throws SQLException if a database error occurs
|
||||
*/
|
||||
List<Item> findByMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids, long offset, int limit)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns the total number of items that match the given predicates, within the
|
||||
* specified collections, if any. This querying method is used for pagination by the
|
||||
* Filtered Items report functionality.
|
||||
* @param context DSpace context object
|
||||
* @param queryPredicates metadata field predicates
|
||||
* @param collectionUuids UUIDs of the collections to search
|
||||
* @return the total number of matching items in the specified collections,
|
||||
* or in any collection if no collection UUIDs are provided
|
||||
* @throws SQLException if a database error occurs
|
||||
*/
|
||||
long countForMetadataQuery(Context context, List<QueryPredicate> queryPredicates,
|
||||
List<UUID> collectionUuids)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all the items in the archive with a given authority key value
|
||||
@@ -768,12 +794,12 @@ public interface ItemService
|
||||
* @throws AuthorizeException if authorization error
|
||||
* @throws IOException if IO error
|
||||
*/
|
||||
public Iterator<Item> findByAuthorityValue(Context context,
|
||||
Iterator<Item> findByAuthorityValue(Context context,
|
||||
String schema, String element, String qualifier, String value)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
|
||||
public Iterator<Item> findByMetadataFieldAuthority(Context context, String mdString, String authority)
|
||||
Iterator<Item> findByMetadataFieldAuthority(Context context, String mdString, String authority)
|
||||
throws SQLException, AuthorizeException;
|
||||
|
||||
/**
|
||||
@@ -785,7 +811,7 @@ public interface ItemService
|
||||
* @param item item
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isItemListedForUser(Context context, Item item);
|
||||
boolean isItemListedForUser(Context context, Item item);
|
||||
|
||||
/**
|
||||
* counts items in the given collection
|
||||
@@ -795,7 +821,7 @@ public interface ItemService
|
||||
* @return total items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countItems(Context context, Collection collection) throws SQLException;
|
||||
int countItems(Context context, Collection collection) throws SQLException;
|
||||
|
||||
/**
|
||||
* counts all items in the given collection including withdrawn items
|
||||
@@ -805,7 +831,7 @@ public interface ItemService
|
||||
* @return total items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countAllItems(Context context, Collection collection) throws SQLException;
|
||||
int countAllItems(Context context, Collection collection) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all Items modified since a Date.
|
||||
@@ -815,7 +841,7 @@ public interface ItemService
|
||||
* @return iterator over items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public Iterator<Item> findByLastModifiedSince(Context context, Date last)
|
||||
Iterator<Item> findByLastModifiedSince(Context context, Date last)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -826,7 +852,7 @@ public interface ItemService
|
||||
* @return total items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countItems(Context context, Community community) throws SQLException;
|
||||
int countItems(Context context, Community community) throws SQLException;
|
||||
|
||||
/**
|
||||
* counts all items in the given community including withdrawn
|
||||
@@ -836,7 +862,7 @@ public interface ItemService
|
||||
* @return total items
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public int countAllItems(Context context, Community community) throws SQLException;
|
||||
int countAllItems(Context context, Community community) throws SQLException;
|
||||
|
||||
/**
|
||||
* counts all items
|
||||
@@ -883,7 +909,7 @@ public interface ItemService
|
||||
* @throws SQLException
|
||||
* @throws SearchServiceException
|
||||
*/
|
||||
public List<Item> findItemsWithEdit(Context context, int offset, int limit)
|
||||
List<Item> findItemsWithEdit(Context context, int offset, int limit)
|
||||
throws SQLException, SearchServiceException;
|
||||
|
||||
/**
|
||||
@@ -893,7 +919,7 @@ public interface ItemService
|
||||
* @throws SQLException
|
||||
* @throws SearchServiceException
|
||||
*/
|
||||
public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException;
|
||||
int countItemsWithEdit(Context context) throws SQLException, SearchServiceException;
|
||||
|
||||
/**
|
||||
* Check if the supplied item is an inprogress submission
|
||||
@@ -953,7 +979,7 @@ public interface ItemService
|
||||
* relationships.
|
||||
* @return metadata fields that match the parameters
|
||||
*/
|
||||
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier,
|
||||
List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier,
|
||||
String lang, boolean enableVirtualMetadata);
|
||||
|
||||
/**
|
||||
@@ -961,7 +987,7 @@ public interface ItemService
|
||||
* @param item the item.
|
||||
* @return the label of the entity type, taken from the item metadata, or null if not found.
|
||||
*/
|
||||
public String getEntityTypeLabel(Item item);
|
||||
String getEntityTypeLabel(Item item);
|
||||
|
||||
/**
|
||||
* Retrieve the entity type of the given item.
|
||||
@@ -969,6 +995,6 @@ public interface ItemService
|
||||
* @param item the item.
|
||||
* @return the entity type of the given item, or null if not found.
|
||||
*/
|
||||
public EntityType getEntityType(Context context, Item item) throws SQLException;
|
||||
EntityType getEntityType(Context context, Item item) throws SQLException;
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
import org.dspace.contentreport.service.ContentReportService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class ContentReportServiceImpl implements ContentReportService {
|
||||
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager
|
||||
.getLogger(ContentReportServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
protected ConfigurationService configurationService;
|
||||
@Autowired
|
||||
private CollectionService collectionService;
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
@Autowired
|
||||
private MetadataFieldService metadataFieldService;
|
||||
|
||||
/**
|
||||
* Returns <code>true<</code> if Content Reports are enabled.
|
||||
* @return <code>true<</code> if Content Reports are enabled
|
||||
*/
|
||||
@Override
|
||||
public boolean getEnabled() {
|
||||
return configurationService.getBooleanProperty("contentreport.enable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves item statistics per collection according to a set of Boolean filters.
|
||||
* @param context DSpace context
|
||||
* @param filters Set of filters
|
||||
* @return a list of collections with the requested statistics for each of them
|
||||
*/
|
||||
@Override
|
||||
public List<FilteredCollection> findFilteredCollections(Context context, java.util.Collection<Filter> filters) {
|
||||
List<FilteredCollection> colls = new ArrayList<>();
|
||||
try {
|
||||
List<Collection> collections = collectionService.findAll(context);
|
||||
for (Collection collection : collections) {
|
||||
FilteredCollection coll = new FilteredCollection();
|
||||
coll.setHandle(collection.getHandle());
|
||||
coll.setLabel(collection.getName());
|
||||
Community community = collection.getCommunities().stream()
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (community != null) {
|
||||
coll.setCommunityLabel(community.getName());
|
||||
coll.setCommunityHandle(community.getHandle());
|
||||
}
|
||||
colls.add(coll);
|
||||
|
||||
Iterator<Item> items = itemService.findAllByCollection(context, collection);
|
||||
int nbTotalItems = 0;
|
||||
while (items.hasNext()) {
|
||||
Item item = items.next();
|
||||
nbTotalItems++;
|
||||
boolean matchesAllFilters = true;
|
||||
for (Filter filter : filters) {
|
||||
if (filter.testItem(context, item)) {
|
||||
coll.addValue(filter, 1);
|
||||
} else {
|
||||
// This ensures the requested filter is present in the collection record
|
||||
// even when there are no matching items.
|
||||
coll.addValue(filter, 0);
|
||||
matchesAllFilters = false;
|
||||
}
|
||||
}
|
||||
if (matchesAllFilters) {
|
||||
coll.addAllFiltersValue(1);
|
||||
}
|
||||
}
|
||||
coll.setTotalItems(nbTotalItems);
|
||||
coll.seal();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("SQLException trying to receive filtered collections statistics", e);
|
||||
}
|
||||
return colls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of items according to a set of criteria.
|
||||
* @param context DSpace context
|
||||
* @param query structured query to find items against
|
||||
* @return a list of items filtered according to the provided query
|
||||
*/
|
||||
@Override
|
||||
public FilteredItems findFilteredItems(Context context, FilteredItemsQuery query) {
|
||||
FilteredItems report = new FilteredItems();
|
||||
|
||||
List<QueryPredicate> predicates = query.getQueryPredicates();
|
||||
List<UUID> collectionUuids = getUuidsFromStrings(query.getCollections());
|
||||
Set<Filter> filters = query.getFilters();
|
||||
|
||||
try {
|
||||
List<Item> items = itemService.findByMetadataQuery(context, predicates, collectionUuids,
|
||||
query.getOffset(), query.getPageLimit());
|
||||
items.stream()
|
||||
.filter(item -> filters.stream().allMatch(f -> f.testItem(context, item)))
|
||||
.forEach(report::addItem);
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
try {
|
||||
long count = itemService.countForMetadataQuery(context, predicates, collectionUuids);
|
||||
report.setItemCount(count);
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a metadata field name to a list of {@link MetadataField} instances
|
||||
* (one if no wildcards are used, possibly more otherwise).
|
||||
* @param context DSpace context
|
||||
* @param metadataField field to search for
|
||||
* @return a corresponding list of {@link MetadataField} entries
|
||||
*/
|
||||
@Override
|
||||
public List<MetadataField> getMetadataFields(org.dspace.core.Context context, String metadataField)
|
||||
throws SQLException {
|
||||
List<MetadataField> fields = new ArrayList<>();
|
||||
if ("*".equals(metadataField)) {
|
||||
return fields;
|
||||
}
|
||||
String schema = "";
|
||||
String element = "";
|
||||
String qualifier = null;
|
||||
String[] parts = metadataField.split("\\.");
|
||||
if (parts.length > 0) {
|
||||
schema = parts[0];
|
||||
}
|
||||
if (parts.length > 1) {
|
||||
element = parts[1];
|
||||
}
|
||||
if (parts.length > 2) {
|
||||
qualifier = parts[2];
|
||||
}
|
||||
|
||||
if (Item.ANY.equals(qualifier)) {
|
||||
fields.addAll(metadataFieldService.findFieldsByElementNameUnqualified(context, schema, element));
|
||||
} else {
|
||||
MetadataField mf = metadataFieldService.findByElement(context, schema, element, qualifier);
|
||||
if (mf != null) {
|
||||
fields.add(mf);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static List<UUID> getUuidsFromStrings(List<String> collSel) {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
for (String s: collSel) {
|
||||
try {
|
||||
uuids.add(UUID.fromString(s));
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Invalid collection UUID: " + s);
|
||||
}
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
}
|
399
dspace-api/src/main/java/org/dspace/contentreport/Filter.java
Normal file
399
dspace-api/src/main/java/org/dspace/contentreport/Filter.java
Normal file
@@ -0,0 +1,399 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.contentreport.ItemFilterUtil.BundleName;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Available filters for the Filtered Collections and Filtered Items reports.
|
||||
* In this enum, each item corresponds to a separate property, not values of
|
||||
* a single property, hence the @JsonProperty applied to each of them.
|
||||
* For each item, the annotation value is read through reflection and copied into
|
||||
* the id property, which eliminates repetitions, hence reducing the risk or errors.
|
||||
*
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public enum Filter {
|
||||
|
||||
@JsonProperty("is_item")
|
||||
IS_ITEM(FilterCategory.PROPERTY, (context, item) -> true),
|
||||
@JsonProperty("is_withdrawn")
|
||||
IS_WITHDRAWN(FilterCategory.PROPERTY, (context, item) -> item.isWithdrawn()),
|
||||
@JsonProperty("is_not_withdrawn")
|
||||
IS_NOT_WITHDRAWN(FilterCategory.PROPERTY, (context, item) -> !item.isWithdrawn()),
|
||||
@JsonProperty("is_discoverable")
|
||||
IS_DISCOVERABLE(FilterCategory.PROPERTY, (context, item) -> item.isDiscoverable()),
|
||||
@JsonProperty("is_not_discoverable")
|
||||
IS_NOT_DISCOVERABLE(FilterCategory.PROPERTY, (context, item) -> !item.isDiscoverable()),
|
||||
|
||||
/**
|
||||
* Matches items having multiple original bitstreams.
|
||||
*/
|
||||
@JsonProperty("has_multiple_originals")
|
||||
HAS_MULTIPLE_ORIGINALS(FilterCategory.BITSTREAM, (context, item) ->
|
||||
ItemFilterUtil.countOriginalBitstream(item) > 1),
|
||||
/**
|
||||
* Matches items having no original bitstreams.
|
||||
*/
|
||||
@JsonProperty("has_no_originals")
|
||||
HAS_NO_ORIGINALS(FilterCategory.BITSTREAM, (context, item) -> ItemFilterUtil.countOriginalBitstream(item) == 0),
|
||||
/**
|
||||
* Matches items having exactly one original bitstream.
|
||||
*/
|
||||
@JsonProperty("has_one_original")
|
||||
HAS_ONE_ORIGINAL(FilterCategory.BITSTREAM, (context, item) -> ItemFilterUtil.countOriginalBitstream(item) == 1),
|
||||
|
||||
/**
|
||||
* Matches items having bitstreams with a MIME type that matches one defined in the "rest.report-mime-document"
|
||||
* configuration property.
|
||||
*/
|
||||
@JsonProperty("has_doc_original")
|
||||
HAS_DOC_ORIGINAL(FilterCategory.BITSTREAM_MIME, (context, item) ->
|
||||
ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes()) > 0),
|
||||
/**
|
||||
* Matches items having bitstreams with a MIME type starting with "image" (e.g., image/jpeg, image/png).
|
||||
*/
|
||||
@JsonProperty("has_image_original")
|
||||
HAS_IMAGE_ORIGINAL(FilterCategory.BITSTREAM_MIME, (context, item) ->
|
||||
ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image") > 0),
|
||||
/**
|
||||
* Matches items having bitstreams with a MIME type other than document (cf. HAS_DOCUMENT above) or image
|
||||
* (cf. HAS_IMAGE_ORIGINAL above).
|
||||
*/
|
||||
@JsonProperty("has_unsupp_type")
|
||||
HAS_UNSUPPORTED_TYPE(FilterCategory.BITSTREAM_MIME, (context, item) -> {
|
||||
int bitCount = ItemFilterUtil.countOriginalBitstream(item);
|
||||
if (bitCount == 0) {
|
||||
return false;
|
||||
}
|
||||
int docCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
|
||||
int imgCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image");
|
||||
return (bitCount - docCount - imgCount) > 0;
|
||||
}),
|
||||
/**
|
||||
* Matches items having bitstreams of multiple types (document, image, other).
|
||||
*/
|
||||
@JsonProperty("has_mixed_original")
|
||||
HAS_MIXED_ORIGINAL(FilterCategory.BITSTREAM_MIME, (context, item) -> {
|
||||
int countBit = ItemFilterUtil.countOriginalBitstream(item);
|
||||
if (countBit <= 1) {
|
||||
return false;
|
||||
}
|
||||
int countDoc = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
|
||||
if (countDoc > 0) {
|
||||
return countDoc != countBit;
|
||||
}
|
||||
int countImg = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image");
|
||||
if (countImg > 0) {
|
||||
return countImg != countBit;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
@JsonProperty("has_pdf_original")
|
||||
HAS_PDF_ORIGINAL(FilterCategory.BITSTREAM_MIME, (context, item) ->
|
||||
ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.MIMES_PDF) > 0),
|
||||
@JsonProperty("has_jpg_original")
|
||||
HAS_JPEG_ORIGINAL(FilterCategory.BITSTREAM_MIME, (context, item) ->
|
||||
ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.MIMES_JPG) > 0),
|
||||
/**
|
||||
* Matches items having at least one PDF of size less than 20 kb (configurable in rest.cfg).
|
||||
*/
|
||||
@JsonProperty("has_small_pdf")
|
||||
HAS_SMALL_PDF(FilterCategory.BITSTREAM_MIME, (context, item) ->
|
||||
ItemFilterUtil.countBitstreamSmallerThanMinSize(
|
||||
context, BundleName.ORIGINAL, item, ItemFilterUtil.MIMES_PDF, "rest.report-pdf-min-size") > 0),
|
||||
/**
|
||||
* Matches items having at least one PDF of size more than 25 Mb (configurable in rest.cfg).
|
||||
*/
|
||||
@JsonProperty("has_large_pdf")
|
||||
HAS_LARGE_PDF(FilterCategory.BITSTREAM_MIME, (context, item) ->
|
||||
ItemFilterUtil.countBitstreamLargerThanMaxSize(
|
||||
context, BundleName.ORIGINAL, item, ItemFilterUtil.MIMES_PDF, "rest.report-pdf-max-size") > 0),
|
||||
/**
|
||||
* Matches items having at least one non-text bitstream.
|
||||
*/
|
||||
@JsonProperty("has_doc_without_text")
|
||||
HAS_DOC_WITHOUT_TEXT(FilterCategory.BITSTREAM_MIME, (context, item) -> {
|
||||
int countDoc = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
|
||||
if (countDoc == 0) {
|
||||
return false;
|
||||
}
|
||||
int countText = ItemFilterUtil.countBitstream(BundleName.TEXT, item);
|
||||
return countDoc > countText;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Matches items having at least one image, but all of supported types.
|
||||
*/
|
||||
@JsonProperty("has_only_supp_image_type")
|
||||
HAS_ONLY_SUPPORTED_IMAGE_TYPE(FilterCategory.MIME, (context, item) -> {
|
||||
int imageCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image/");
|
||||
if (imageCount == 0) {
|
||||
return false;
|
||||
}
|
||||
int suppImageCount = ItemFilterUtil.countOriginalBitstreamMime(
|
||||
context, item, ItemFilterUtil.getSupportedImageMimeTypes());
|
||||
return (imageCount == suppImageCount);
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one image of an unsupported type.
|
||||
*/
|
||||
@JsonProperty("has_unsupp_image_type")
|
||||
HAS_UNSUPPORTED_IMAGE_TYPE(FilterCategory.MIME, (context, item) -> {
|
||||
int imageCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image/");
|
||||
if (imageCount == 0) {
|
||||
return false;
|
||||
}
|
||||
int suppImageCount = ItemFilterUtil.countOriginalBitstreamMime(
|
||||
context, item, ItemFilterUtil.getSupportedImageMimeTypes());
|
||||
return (imageCount - suppImageCount) > 0;
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one document, but all of supported types.
|
||||
*/
|
||||
@JsonProperty("has_only_supp_doc_type")
|
||||
HAS_ONLY_SUPPORTED_DOC_TYPE(FilterCategory.MIME, (context, item) -> {
|
||||
int docCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
|
||||
if (docCount == 0) {
|
||||
return false;
|
||||
}
|
||||
int suppDocCount = ItemFilterUtil.countOriginalBitstreamMime(
|
||||
context, item, ItemFilterUtil.getSupportedDocumentMimeTypes());
|
||||
return docCount == suppDocCount;
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one document of an unsupported type.
|
||||
*/
|
||||
@JsonProperty("has_unsupp_doc_type")
|
||||
HAS_UNSUPPORTED_DOC_TYPE(FilterCategory.MIME, (context, item) -> {
|
||||
int docCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
|
||||
if (docCount == 0) {
|
||||
return false;
|
||||
}
|
||||
int suppDocCount = ItemFilterUtil.countOriginalBitstreamMime(
|
||||
context, item, ItemFilterUtil.getSupportedDocumentMimeTypes());
|
||||
return (docCount - suppDocCount) > 0;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Matches items having at least one unsupported bundle.
|
||||
*/
|
||||
@JsonProperty("has_unsupported_bundle")
|
||||
HAS_UNSUPPORTED_BUNDLE(FilterCategory.BUNDLE, (context, item) -> {
|
||||
String[] bundleList = DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getArrayProperty("rest.report-supp-bundles");
|
||||
return ItemFilterUtil.hasUnsupportedBundle(item, bundleList);
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one thumbnail of size less than 400 bytes (configurable in rest.cfg).
|
||||
*/
|
||||
@JsonProperty("has_small_thumbnail")
|
||||
HAS_SMALL_THUMBNAIL(FilterCategory.BUNDLE, (context, item) ->
|
||||
ItemFilterUtil.countBitstreamSmallerThanMinSize(
|
||||
context, BundleName.THUMBNAIL, item, ItemFilterUtil.MIMES_JPG, "rest.report-thumbnail-min-size") > 0),
|
||||
/**
|
||||
* Matches items having at least one original without a thumbnail.
|
||||
*/
|
||||
@JsonProperty("has_original_without_thumbnail")
|
||||
HAS_ORIGINAL_WITHOUT_THUMBNAIL(FilterCategory.BUNDLE, (context, item) -> {
|
||||
int countBit = ItemFilterUtil.countOriginalBitstream(item);
|
||||
if (countBit == 0) {
|
||||
return false;
|
||||
}
|
||||
int countThumb = ItemFilterUtil.countBitstream(BundleName.THUMBNAIL, item);
|
||||
return countBit > countThumb;
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one non-JPEG thumbnail.
|
||||
*/
|
||||
@JsonProperty("has_invalid_thumbnail_name")
|
||||
HAS_INVALID_THUMBNAIL_NAME(FilterCategory.BUNDLE, (context, item) -> {
|
||||
List<String> originalNames = ItemFilterUtil.getBitstreamNames(BundleName.ORIGINAL, item);
|
||||
List<String> thumbNames = ItemFilterUtil.getBitstreamNames(BundleName.THUMBNAIL, item);
|
||||
if (thumbNames.size() != originalNames.size()) {
|
||||
return false;
|
||||
}
|
||||
return originalNames.stream()
|
||||
.anyMatch(name -> !thumbNames.contains(name + ".jpg") && !thumbNames.contains(name + ".jpeg"));
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one non-generated thumbnail.
|
||||
*/
|
||||
@JsonProperty("has_non_generated_thumb")
|
||||
HAS_NON_GENERATED_THUMBNAIL(FilterCategory.BUNDLE, (context, item) -> {
|
||||
String[] generatedThumbDesc = DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getArrayProperty("rest.report-gen-thumbnail-desc");
|
||||
int countThumb = ItemFilterUtil.countBitstream(BundleName.THUMBNAIL, item);
|
||||
if (countThumb == 0) {
|
||||
return false;
|
||||
}
|
||||
int countGen = ItemFilterUtil.countBitstreamByDesc(BundleName.THUMBNAIL, item, generatedThumbDesc);
|
||||
return (countThumb > countGen);
|
||||
}),
|
||||
/**
|
||||
* Matches items having no licence-typed bitstreams.
|
||||
*/
|
||||
@JsonProperty("no_license")
|
||||
NO_LICENSE(FilterCategory.BUNDLE, (context, item) ->
|
||||
ItemFilterUtil.countBitstream(BundleName.LICENSE, item) == 0),
|
||||
/**
|
||||
* Matches items having licence documentation (a licence bitstream named other than license.txt).
|
||||
*/
|
||||
@JsonProperty("has_license_documentation")
|
||||
HAS_LICENSE_DOCUMENTATION(FilterCategory.BUNDLE, (context, item) -> {
|
||||
List<String> names = ItemFilterUtil.getBitstreamNames(BundleName.LICENSE, item);
|
||||
return names.stream()
|
||||
.anyMatch(name -> !name.equals("license.txt"));
|
||||
}),
|
||||
|
||||
/**
|
||||
* Matches items having at least one original with restricted access.
|
||||
*/
|
||||
@JsonProperty("has_restricted_original")
|
||||
HAS_RESTRICTED_ORIGINAL(FilterCategory.PERMISSION, (context, item) -> {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(BundleName.ORIGINAL.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.anyMatch(bit -> {
|
||||
try {
|
||||
if (!getAuthorizeService()
|
||||
.authorizeActionBoolean(getAnonymousContext(), bit, org.dspace.core.Constants.READ)) {
|
||||
return true;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
getLog().warn("SQL Exception testing original bitstream access " + e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}),
|
||||
/**
|
||||
* Matches items having at least one thumbnail with restricted access.
|
||||
*/
|
||||
@JsonProperty("has_restricted_thumbnail")
|
||||
HAS_RESTRICTED_THUMBNAIL(FilterCategory.PERMISSION, (context, item) -> {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(BundleName.THUMBNAIL.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.anyMatch(bit -> {
|
||||
try {
|
||||
if (!getAuthorizeService()
|
||||
.authorizeActionBoolean(getAnonymousContext(), bit, org.dspace.core.Constants.READ)) {
|
||||
return true;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
getLog().warn("SQL Exception testing thumbnail bitstream access " + e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}),
|
||||
/**
|
||||
* Matches items having metadata with restricted access.
|
||||
*/
|
||||
@JsonProperty("has_restricted_metadata")
|
||||
HAS_RESTRICTED_METADATA(FilterCategory.PERMISSION, (context, item) -> {
|
||||
try {
|
||||
return !getAuthorizeService()
|
||||
.authorizeActionBoolean(getAnonymousContext(), item, org.dspace.core.Constants.READ);
|
||||
} catch (SQLException e) {
|
||||
getLog().warn("SQL Exception testing item metadata access " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
private static final Logger log = LogManager.getLogger();
|
||||
private static AuthorizeService authorizeService;
|
||||
private static Context anonymousContext;
|
||||
|
||||
private String id;
|
||||
private FilterCategory category;
|
||||
private BiPredicate<Context, Item> itemTester;
|
||||
|
||||
Filter(FilterCategory category, BiPredicate<Context, Item> itemTester) {
|
||||
try {
|
||||
JsonProperty jp = getClass().getField(name()).getAnnotation(JsonProperty.class);
|
||||
id = Optional.ofNullable(jp).map(JsonProperty::value).orElse(name());
|
||||
} catch (Exception e) {
|
||||
id = name();
|
||||
}
|
||||
this.category = category;
|
||||
this.itemTester = itemTester;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public FilterCategory getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public boolean testItem(Context context, Item item) {
|
||||
return itemTester.test(context, item);
|
||||
}
|
||||
|
||||
private static Logger getLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
private static AuthorizeService getAuthorizeService() {
|
||||
if (authorizeService == null) {
|
||||
authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
}
|
||||
return authorizeService;
|
||||
}
|
||||
|
||||
private static Context getAnonymousContext() {
|
||||
if (anonymousContext == null) {
|
||||
anonymousContext = new Context();
|
||||
}
|
||||
return anonymousContext;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static Filter get(String id) {
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> Objects.equals(item.id, id))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static Set<Filter> getFilters(String filters) {
|
||||
String[] ids = Optional.ofNullable(filters).orElse("").split("[^a-z_]+");
|
||||
Set<Filter> set = Arrays.stream(ids)
|
||||
.map(Filter::get)
|
||||
.filter(f -> f != null)
|
||||
.collect(Collectors.toCollection(() -> EnumSet.noneOf(Filter.class)));
|
||||
if (set == null) {
|
||||
set = EnumSet.noneOf(Filter.class);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.contentreport;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Identifies the category/section of filters defined in the {@link Filter} enum.
|
||||
* This enum will be used when/if the structured filter definitions are returned to
|
||||
* the Angular layer through a REST endpoint.
|
||||
*
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public enum FilterCategory {
|
||||
|
||||
PROPERTY("property"),
|
||||
BITSTREAM("bitstream"),
|
||||
BITSTREAM_MIME("bitstream_mime"),
|
||||
MIME("mime"),
|
||||
BUNDLE("bundle"),
|
||||
PERMISSION("permission");
|
||||
|
||||
private String id;
|
||||
private List<Filter> filters;
|
||||
|
||||
FilterCategory(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Filter> getFilters() {
|
||||
if (filters == null) {
|
||||
filters = Arrays.stream(Filter.values())
|
||||
.filter(f -> f.getCategory() == this)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* This class represents an entry in the Filtered Collections report.
|
||||
*
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public class FilteredCollection implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -231735620268582719L;
|
||||
|
||||
/** Name of the collection */
|
||||
private String label;
|
||||
/** Handle of the collection, used to make it clickable from the generated report */
|
||||
private String handle;
|
||||
/** Name of the owning community */
|
||||
private String communityLabel;
|
||||
/** Handle of the owning community, used to make it clickable from the generated report */
|
||||
private String communityHandle;
|
||||
/** Total number of items in the collection */
|
||||
private int totalItems;
|
||||
/** Number of filtered items per requested filter in the collection */
|
||||
private Map<Filter, Integer> values = new EnumMap<>(Filter.class);
|
||||
/** Number of items in the collection that match all requested filters */
|
||||
private int allFiltersValue;
|
||||
/**
|
||||
* Indicates whether this object is protected against further changes.
|
||||
* This is used in computing summary data in the parent FilteredCollectionsRest class.
|
||||
*/
|
||||
private boolean sealed;
|
||||
|
||||
/**
|
||||
* Shortcut method that builds a FilteredCollectionRest instance
|
||||
* from its building blocks.
|
||||
* @param label Name of the collection
|
||||
* @param handle Handle of the collection
|
||||
* @param communityLabel Name of the owning community
|
||||
* @param communityHandle Handle of the owning community
|
||||
* @param totalItems Total number of items in the collection
|
||||
* @param allFiltersValue Number of items in the collection that match all requested filters
|
||||
* @param values Number of filtered items per requested filter in the collection
|
||||
* @param doSeal true if the collection must be sealed immediately
|
||||
* @return a FilteredCollectionRest instance built from the provided parameters
|
||||
*/
|
||||
public static FilteredCollection of(String label, String handle,
|
||||
String communityLabel, String communityHandle,
|
||||
int totalItems, int allFiltersValue, Map<Filter, Integer> values, boolean doSeal) {
|
||||
var coll = new FilteredCollection();
|
||||
coll.label = label;
|
||||
coll.handle = handle;
|
||||
coll.communityLabel = communityLabel;
|
||||
coll.communityHandle = communityHandle;
|
||||
coll.totalItems = totalItems;
|
||||
coll.allFiltersValue = allFiltersValue;
|
||||
Optional.ofNullable(values).ifPresent(vs -> vs.forEach(coll::addValue));
|
||||
if (doSeal) {
|
||||
coll.seal();
|
||||
}
|
||||
return coll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item counts per filter.
|
||||
* If this object is sealed, a defensive copy will be returned.
|
||||
*
|
||||
* @return the item counts per filter
|
||||
*/
|
||||
public Map<Filter, Integer> getValues() {
|
||||
if (sealed) {
|
||||
return new EnumMap<>(values);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments a filtered item count for a given filter.
|
||||
*
|
||||
* @param filter Filter to add to the requested filters in this collection
|
||||
* @param delta Number by which the filtered item count must be incremented
|
||||
* for the requested filter
|
||||
*/
|
||||
public void addValue(Filter filter, int delta) {
|
||||
checkSealed();
|
||||
Integer oldValue = values.getOrDefault(filter, Integer.valueOf(0));
|
||||
int newValue = oldValue.intValue() + delta;
|
||||
values.put(filter, Integer.valueOf(newValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all filtered item counts for this collection.
|
||||
* The contents are copied into this object's internal Map, which is protected against
|
||||
* further tampering with the provided Map.
|
||||
*
|
||||
* @param values Values that replace the current ones
|
||||
*/
|
||||
public void setValues(Map<? extends Filter, ? extends Integer> values) {
|
||||
checkSealed();
|
||||
this.values.clear();
|
||||
this.values.putAll(values);
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
checkSealed();
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void setHandle(String handle) {
|
||||
checkSealed();
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public String getCommunityLabel() {
|
||||
return communityLabel;
|
||||
}
|
||||
|
||||
public void setCommunityLabel(String communityLabel) {
|
||||
checkSealed();
|
||||
this.communityLabel = communityLabel;
|
||||
}
|
||||
|
||||
public String getCommunityHandle() {
|
||||
return communityHandle;
|
||||
}
|
||||
|
||||
public void setCommunityHandle(String communityHandle) {
|
||||
checkSealed();
|
||||
this.communityHandle = communityHandle;
|
||||
}
|
||||
|
||||
public int getTotalItems() {
|
||||
return totalItems;
|
||||
}
|
||||
|
||||
public void setTotalItems(int totalItems) {
|
||||
checkSealed();
|
||||
this.totalItems = totalItems;
|
||||
}
|
||||
|
||||
public int getAllFiltersValue() {
|
||||
return allFiltersValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the count of items matching all filters.
|
||||
*
|
||||
* @param delta Number by which the count must be incremented
|
||||
*/
|
||||
public void addAllFiltersValue(int delta) {
|
||||
checkSealed();
|
||||
allFiltersValue++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the count of items matching all filters.
|
||||
*
|
||||
* @param allFiltersValue Number that replaces the current item count
|
||||
*/
|
||||
public void setAllFiltersValue(int allFiltersValue) {
|
||||
checkSealed();
|
||||
this.allFiltersValue = allFiltersValue;
|
||||
}
|
||||
|
||||
public boolean getSealed() {
|
||||
return sealed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seals this filtered collection object.
|
||||
* No changes to this object can be made afterwards. Any attempt will throw
|
||||
* an IllegalStateException.
|
||||
*/
|
||||
public void seal() {
|
||||
sealed = true;
|
||||
}
|
||||
|
||||
private void checkSealed() {
|
||||
if (sealed) {
|
||||
throw new IllegalStateException("This filtered collection record is sealed"
|
||||
+ " and cannot be modified anymore. You can apply changes to a non-sealed clone.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-sealed clone of this filtered collection record.
|
||||
*
|
||||
* @return a new non-sealed FilteredCollectionRest instance containing
|
||||
* all attribute values of this object
|
||||
*/
|
||||
@Override
|
||||
public FilteredCollection clone() {
|
||||
var clone = new FilteredCollection();
|
||||
clone.label = label;
|
||||
clone.handle = handle;
|
||||
clone.values.putAll(values);
|
||||
clone.allFiltersValue = allFiltersValue;
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* This class represents the complete result of a Filtered Collections report query.
|
||||
* In addition to the list of FilteredCollection entries, it contains the lazily computed
|
||||
* summary to be included in the completed report.
|
||||
*
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public class FilteredCollections implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3622651208704009095L;
|
||||
|
||||
/** Collections included in the report */
|
||||
private List<FilteredCollection> collections = new ArrayList<>();
|
||||
/**
|
||||
* Summary generated by adding up data for each filter included in the report.
|
||||
* It will be regenerated if any non-sealed collection item is found in
|
||||
* the {@link #collections} collection attribute.
|
||||
*/
|
||||
private FilteredCollection summary;
|
||||
|
||||
/**
|
||||
* Shortcut method that builds a FilteredCollectionsRest instance
|
||||
* from its building blocks.
|
||||
* @param collections a list of FilteredCollectionRest instances
|
||||
* @return a FilteredCollectionsRest instance built from the provided parameters
|
||||
*/
|
||||
public static FilteredCollections of(Collection<FilteredCollection> collections) {
|
||||
var colls = new FilteredCollections();
|
||||
Optional.ofNullable(collections).ifPresent(cs -> cs.stream().forEach(colls::addCollection));
|
||||
return colls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a defensive copy of the collections included in this report.
|
||||
*
|
||||
* @return the collections included in this report
|
||||
*/
|
||||
public List<FilteredCollection> getCollections() {
|
||||
return new ArrayList<>(collections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link FilteredCollectionRest} object to this report.
|
||||
*
|
||||
* @param coll {@link FilteredCollectionRest} to add to this report
|
||||
*/
|
||||
public void addCollection(FilteredCollection coll) {
|
||||
summary = null;
|
||||
collections.add(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all collections for this report.
|
||||
* The contents are copied into this object's internal list, which is protected against
|
||||
* further tampering with the provided list.
|
||||
*
|
||||
* @param collections Values that replace the current ones
|
||||
*/
|
||||
public void setCollections(List<FilteredCollection> collections) {
|
||||
summary = null;
|
||||
this.collections.clear();
|
||||
this.collections.addAll(collections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the report summary.
|
||||
* If the summary has not been computed yet and/or the report includes non-sealed collections,
|
||||
* it will be regenerated.
|
||||
*
|
||||
* @return the generated report summary
|
||||
*/
|
||||
public FilteredCollection getSummary() {
|
||||
boolean needsRefresh = summary == null || collections.stream().anyMatch(c -> !c.getSealed());
|
||||
if (needsRefresh) {
|
||||
summary = new FilteredCollection();
|
||||
for (var coll : collections) {
|
||||
coll.getValues().forEach(summary::addValue);
|
||||
}
|
||||
int total = collections.stream()
|
||||
.mapToInt(FilteredCollection::getTotalItems)
|
||||
.sum();
|
||||
summary.setTotalItems(total);
|
||||
int allFilters = collections.stream()
|
||||
.mapToInt(FilteredCollection::getAllFiltersValue)
|
||||
.sum();
|
||||
summary.setAllFiltersValue(allFilters);
|
||||
summary.seal();
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.contentreport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
|
||||
/**
|
||||
* This class represents a list of items for a Filtered Items report query.
|
||||
* Since the underlying list should correspond to only a page of results,
|
||||
* the total number of items found through the query is included in this report.
|
||||
*
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public class FilteredItems implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7980375013177658249L;
|
||||
|
||||
/** Items included in the report */
|
||||
private List<Item> items = new ArrayList<>();
|
||||
/** Total item count (for pagination) */
|
||||
private long itemCount;
|
||||
|
||||
/**
|
||||
* Returns a defensive copy of the items included in this report.
|
||||
*
|
||||
* @return the items included in this report
|
||||
*/
|
||||
public List<Item> getItems() {
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ItemRest} object to this report.
|
||||
*
|
||||
* @param item {@link ItemRest} to add to this report
|
||||
*/
|
||||
public void addItem(Item item) {
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all items for this report.
|
||||
* The contents are copied into this object's internal list, which is protected
|
||||
* against further tampering with the provided list.
|
||||
*
|
||||
* @param items Values that replace the current ones
|
||||
*/
|
||||
public void setItems(List<Item> items) {
|
||||
this.items.clear();
|
||||
this.items.addAll(items);
|
||||
}
|
||||
|
||||
public long getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
public void setItemCount(long itemCount) {
|
||||
this.itemCount = itemCount;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Structured query contents for the Filtered Items report
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public class FilteredItemsQuery {
|
||||
|
||||
private List<String> collections = new ArrayList<>();
|
||||
private List<QueryPredicate> queryPredicates = new ArrayList<>();
|
||||
private long offset;
|
||||
private int pageLimit;
|
||||
private Set<Filter> filters = EnumSet.noneOf(Filter.class);
|
||||
private List<String> additionalFields = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Shortcut method that builds a FilteredItemsQuery instance
|
||||
* from its building blocks.
|
||||
* @param collectionUuids collection UUIDs to add
|
||||
* @param predicates query predicates used to filter existing items
|
||||
* @param pageLimit number of items per page
|
||||
* @param filters filters to apply to existing items
|
||||
* The filters mapping to true will be applied, others (either missing or
|
||||
* mapping to false) will not.
|
||||
* @param additionalFields additional fields to display in the resulting report
|
||||
* @return a FilteredItemsQuery instance built from the provided parameters
|
||||
*/
|
||||
public static FilteredItemsQuery of(Collection<String> collectionUuids,
|
||||
Collection<QueryPredicate> predicates, long offset, int pageLimit,
|
||||
Collection<Filter> filters, Collection<String> additionalFields) {
|
||||
var query = new FilteredItemsQuery();
|
||||
Optional.ofNullable(collectionUuids).ifPresent(query.collections::addAll);
|
||||
Optional.ofNullable(predicates).ifPresent(query.queryPredicates::addAll);
|
||||
query.offset = offset;
|
||||
query.pageLimit = pageLimit;
|
||||
Optional.ofNullable(filters).ifPresent(query.filters::addAll);
|
||||
Optional.ofNullable(additionalFields).ifPresent(query.additionalFields::addAll);
|
||||
return query;
|
||||
}
|
||||
|
||||
public List<String> getCollections() {
|
||||
return collections;
|
||||
}
|
||||
|
||||
public void setCollections(List<String> collections) {
|
||||
this.collections.clear();
|
||||
if (collections != null) {
|
||||
this.collections.addAll(collections);
|
||||
}
|
||||
}
|
||||
|
||||
public List<QueryPredicate> getQueryPredicates() {
|
||||
return queryPredicates;
|
||||
}
|
||||
|
||||
public void setQueryPredicates(List<QueryPredicate> queryPredicates) {
|
||||
this.queryPredicates.clear();
|
||||
if (queryPredicates != null) {
|
||||
this.queryPredicates.addAll(queryPredicates);
|
||||
}
|
||||
}
|
||||
|
||||
public long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(long offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int getPageLimit() {
|
||||
return pageLimit;
|
||||
}
|
||||
|
||||
public void setPageLimit(int pageLimit) {
|
||||
this.pageLimit = pageLimit;
|
||||
}
|
||||
|
||||
public Set<Filter> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public void setFilters(Set<Filter> filters) {
|
||||
this.filters.clear();
|
||||
if (filters != null) {
|
||||
this.filters.addAll(filters);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getAdditionalFields() {
|
||||
return additionalFields;
|
||||
}
|
||||
|
||||
public void setAdditionalFields(List<String> additionalFields) {
|
||||
this.additionalFields.clear();
|
||||
if (additionalFields != null) {
|
||||
this.additionalFields.addAll(additionalFields);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,353 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import static org.dspace.content.Item.ANY;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Utility methods for applying some of the filters defined in the {@link Filter} enum.
|
||||
*
|
||||
* @author Jean-François Morin (Université Laval) (port to DSpace 7.x)
|
||||
* @author Terry Brady, Georgetown University (original code in DSpace 6.x)
|
||||
*/
|
||||
public class ItemFilterUtil {
|
||||
|
||||
protected static ItemService itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
private static final Logger log = LogManager.getLogger(ItemFilterUtil.class);
|
||||
public static final String[] MIMES_PDF = {"application/pdf"};
|
||||
public static final String[] MIMES_JPG = {"image/jpeg"};
|
||||
|
||||
/**
|
||||
* Supported bundle types.
|
||||
* N.B.: Bundle names are used in metadata as they are named here.
|
||||
* Do NOT change these names, the name() method is invoked at multiple
|
||||
* locations in this class and enum Filter.
|
||||
* If these names are to change, the name() invocations shall be changed
|
||||
* so that they refer to these unchanged names, likely through a String property.
|
||||
*/
|
||||
enum BundleName {
|
||||
ORIGINAL, TEXT, LICENSE, THUMBNAIL;
|
||||
}
|
||||
|
||||
private ItemFilterUtil() {}
|
||||
|
||||
static String[] getDocumentMimeTypes() {
|
||||
return DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getArrayProperty("rest.report-mime-document");
|
||||
}
|
||||
|
||||
static String[] getSupportedDocumentMimeTypes() {
|
||||
return DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getArrayProperty("rest.report-mime-document-supported");
|
||||
}
|
||||
|
||||
static String[] getSupportedImageMimeTypes() {
|
||||
return DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getArrayProperty("rest.report-mime-document-image");
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the original bitstreams of a given item.
|
||||
* @param item Provided item
|
||||
* @return the number of original bitstreams in the item
|
||||
*/
|
||||
static int countOriginalBitstream(Item item) {
|
||||
return countBitstream(BundleName.ORIGINAL, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the bitstreams of a given item for a specific type.
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @return the number of matching bitstreams in the item
|
||||
*/
|
||||
static int countBitstream(BundleName bundleName, Item item) {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.mapToInt(bundle -> bundle.getBitstreams().size())
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the bitstream names of an given item for a specific bundle type.
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @return the names of matching bitstreams in the item
|
||||
*/
|
||||
static List<String> getBitstreamNames(BundleName bundleName, Item item) {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.map(Bitstream::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the original bitstreams of a given item matching one of a list of specific MIME types.
|
||||
* @param context DSpace context
|
||||
* @param item Provided item
|
||||
* @param mimeList List of MIME types to filter bitstreams
|
||||
* @return number of matching original bitstreams
|
||||
*/
|
||||
static int countOriginalBitstreamMime(Context context, Item item, String[] mimeList) {
|
||||
return countBitstreamMime(context, BundleName.ORIGINAL, item, mimeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the bitstreams of a given item for a specific type matching one of a list of specific MIME types.
|
||||
* @param context DSpace context
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @param mimeList List of MIME types to filter bitstreams
|
||||
* @return number of matching bitstreams
|
||||
*/
|
||||
static int countBitstreamMime(Context context, BundleName bundleName, Item item, String[] mimeList) {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.mapToInt(bit -> {
|
||||
int count = 0;
|
||||
for (String mime : mimeList) {
|
||||
try {
|
||||
if (bit.getFormat(context).getMIMEType().equals(mime.trim())) {
|
||||
count++;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Get format error for bitstream " + bit.getName());
|
||||
}
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the bitstreams of a given item for a specific type matching one of a list of specific descriptions.
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @param descList List of descriptions to filter bitstreams
|
||||
* @return number of matching bitstreams
|
||||
*/
|
||||
static int countBitstreamByDesc(BundleName bundleName, Item item, String[] descList) {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.filter(bit -> bit.getDescription() != null)
|
||||
.mapToInt(bit -> {
|
||||
int count = 0;
|
||||
for (String desc : descList) {
|
||||
String bitDesc = bit.getDescription();
|
||||
if (bitDesc.equals(desc.trim())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the bitstreams of a given item smaller than a given size for a specific type
|
||||
* matching one of a list of specific MIME types.
|
||||
* @param context DSpace context
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @param mimeList List of MIME types to filter bitstreams
|
||||
* @param prop Configurable property providing the size to filter bitstreams
|
||||
* @return number of matching bitstreams
|
||||
*/
|
||||
static int countBitstreamSmallerThanMinSize(
|
||||
Context context, BundleName bundleName, Item item, String[] mimeList, String prop) {
|
||||
long size = DSpaceServicesFactory.getInstance().getConfigurationService().getLongProperty(prop);
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.mapToInt(bit -> {
|
||||
int count = 0;
|
||||
for (String mime : mimeList) {
|
||||
try {
|
||||
if (bit.getFormat(context).getMIMEType().equals(mime.trim())) {
|
||||
if (bit.getSizeBytes() < size) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the bitstreams of a given item larger than a given size for a specific type
|
||||
* matching one of a list of specific MIME types.
|
||||
* @param context DSpace context
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @param mimeList List of MIME types to filter bitstreams
|
||||
* @param prop Configurable property providing the size to filter bitstreams
|
||||
* @return number of matching bitstreams
|
||||
*/
|
||||
static int countBitstreamLargerThanMaxSize(
|
||||
Context context, BundleName bundleName, Item item, String[] mimeList, String prop) {
|
||||
long size = DSpaceServicesFactory.getInstance().getConfigurationService().getLongProperty(prop);
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.mapToInt(bit -> {
|
||||
int count = 0;
|
||||
for (String mime : mimeList) {
|
||||
try {
|
||||
if (bit.getFormat(context).getMIMEType().equals(mime.trim())) {
|
||||
if (bit.getSizeBytes() > size) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the original bitstreams of a given item whose MIME type starts with a specific prefix.
|
||||
* @param context DSpace context
|
||||
* @param item Provided item
|
||||
* @param prefix Prefix to filter bitstreams
|
||||
* @return number of matching original bitstreams
|
||||
*/
|
||||
static int countOriginalBitstreamMimeStartsWith(Context context, Item item, String prefix) {
|
||||
return countBitstreamMimeStartsWith(context, BundleName.ORIGINAL, item, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the bitstreams of a given item for a specific type whose MIME type starts with a specific prefix.
|
||||
* @param context DSpace context
|
||||
* @param bundleName Type of bundle to filter bitstreams
|
||||
* @param item Provided item
|
||||
* @param prefix Prefix to filter bitstreams
|
||||
* @return number of matching bitstreams
|
||||
*/
|
||||
static int countBitstreamMimeStartsWith(Context context, BundleName bundleName, Item item, String prefix) {
|
||||
return item.getBundles().stream()
|
||||
.filter(bundle -> bundle.getName().equals(bundleName.name()))
|
||||
.map(Bundle::getBitstreams)
|
||||
.flatMap(List::stream)
|
||||
.mapToInt(bit -> {
|
||||
int count = 0;
|
||||
try {
|
||||
if (bit.getFormat(context).getMIMEType().startsWith(prefix)) {
|
||||
count++;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a given item has a bundle not matching a specific list of bundles.
|
||||
* @param item Provided item
|
||||
* @param bundleList List of bundle names to filter bundles
|
||||
* @return true if the item has a (non-)matching bundle
|
||||
*/
|
||||
static boolean hasUnsupportedBundle(Item item, String[] bundleList) {
|
||||
if (bundleList == null) {
|
||||
return false;
|
||||
}
|
||||
Set<String> bundles = Arrays.stream(bundleList)
|
||||
.collect(Collectors.toSet());
|
||||
return item.getBundles().stream()
|
||||
.anyMatch(bundle -> !bundles.contains(bundle.getName()));
|
||||
}
|
||||
|
||||
static boolean hasOriginalBitstreamMime(Context context, Item item, String[] mimeList) {
|
||||
return hasBitstreamMime(context, BundleName.ORIGINAL, item, mimeList);
|
||||
}
|
||||
|
||||
static boolean hasBitstreamMime(Context context, BundleName bundleName, Item item, String[] mimeList) {
|
||||
return countBitstreamMime(context, bundleName, item, mimeList) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a given item has at least one field of a specific list whose value
|
||||
* matches a provided regular expression.
|
||||
* @param item Provided item
|
||||
* @param fieldList List of fields to check
|
||||
* @param regex Regular expression to check field values against
|
||||
* @return true if there is at least one matching field, false otherwise
|
||||
*/
|
||||
static boolean hasMetadataMatch(Item item, String fieldList, Pattern regex) {
|
||||
if ("*".equals(fieldList)) {
|
||||
return itemService.getMetadata(item, ANY, ANY, ANY, ANY).stream()
|
||||
.anyMatch(md -> regex.matcher(md.getValue()).matches());
|
||||
}
|
||||
|
||||
return Arrays.stream(fieldList.split(","))
|
||||
.map(field -> itemService.getMetadataByMetadataString(item, field.trim()))
|
||||
.flatMap(List::stream)
|
||||
.anyMatch(md -> regex.matcher(md.getValue()).matches());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a given item has at all fields of a specific list whose values
|
||||
* match a provided regular expression.
|
||||
* @param item Provided item
|
||||
* @param fieldList List of fields to check
|
||||
* @param regex Regular expression to check field values against
|
||||
* @return true if all specified fields match, false otherwise
|
||||
*/
|
||||
static boolean hasOnlyMetadataMatch(Item item, String fieldList, Pattern regex) {
|
||||
if ("*".equals(fieldList)) {
|
||||
return itemService.getMetadata(item, ANY, ANY, ANY, ANY).stream()
|
||||
.allMatch(md -> regex.matcher(md.getValue()).matches());
|
||||
}
|
||||
|
||||
return Arrays.stream(fieldList.split(","))
|
||||
.map(field -> itemService.getMetadataByMetadataString(item, field.trim()))
|
||||
.flatMap(List::stream)
|
||||
.allMatch(md -> regex.matcher(md.getValue()).matches());
|
||||
}
|
||||
|
||||
static boolean recentlyModified(Item item, int days) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.add(Calendar.DATE, -days);
|
||||
return cal.getTime().before(item.getLastModified());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BiFunction;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.function.TriFunction;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.MetadataValue_;
|
||||
import org.dspace.util.DSpacePostgreSQLDialect;
|
||||
import org.dspace.util.JpaCriteriaBuilderKit;
|
||||
import org.hibernate.criterion.Criterion;
|
||||
import org.hibernate.criterion.Property;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
||||
/**
|
||||
* Operators available for creating predicates to query the
|
||||
* Filtered Items report
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public enum QueryOperator {
|
||||
|
||||
EXISTS("exists", true, false,
|
||||
(val, regexClause) -> Property.forName("mv.value").isNotNull(),
|
||||
(val, regexClause, jpaKit) -> jpaKit.criteriaBuilder().isNotNull(jpaKit.root().get(MetadataValue_.VALUE))),
|
||||
DOES_NOT_EXIST("doesnt_exist", true, true,
|
||||
(val, regexClause) -> EXISTS.buildPredicate(val, regexClause),
|
||||
(val, regexClause, jpaKit) -> EXISTS.buildJpaPredicate(val, regexClause, jpaKit)),
|
||||
EQUALS("equals", true, false,
|
||||
(val, regexClause) -> Property.forName("mv.value").eq(val),
|
||||
(val, regexClause, jpaKit) -> jpaKit.criteriaBuilder().equal(jpaKit.root().get(MetadataValue_.VALUE), val)),
|
||||
DOES_NOT_EQUAL("not_equals", true, true,
|
||||
(val, regexClause) -> EQUALS.buildPredicate(val, regexClause),
|
||||
(val, regexClause, jpaKit) -> EQUALS.buildJpaPredicate(val, regexClause, jpaKit)),
|
||||
LIKE("like", true, false,
|
||||
(val, regexClause) -> Property.forName("mv.value").like(val),
|
||||
(val, regexClause, jpaKit) -> jpaKit.criteriaBuilder().like(jpaKit.root().get(MetadataValue_.VALUE), val)),
|
||||
NOT_LIKE("not_like", true, true,
|
||||
(val, regexClause) -> LIKE.buildPredicate(val, regexClause),
|
||||
(val, regexClause, jpaKit) -> LIKE.buildJpaPredicate(val, regexClause, jpaKit)),
|
||||
CONTAINS("contains", true, false,
|
||||
(val, regexClause) -> Property.forName("mv.value").like("%" + val + "%"),
|
||||
(val, regexClause, jpaKit) -> LIKE.buildJpaPredicate("%" + val + "%", regexClause, jpaKit)),
|
||||
DOES_NOT_CONTAIN("doesnt_contain", true, true,
|
||||
(val, regexClause) -> CONTAINS.buildPredicate(val, regexClause),
|
||||
(val, regexClause, jpaKit) -> CONTAINS.buildJpaPredicate(val, regexClause, jpaKit)),
|
||||
MATCHES("matches", false, false,
|
||||
(val, regexClause) -> Restrictions.sqlRestriction(regexClause, val, StandardBasicTypes.STRING),
|
||||
(val, regexClause, jpaKit) -> regexPredicate(val, DSpacePostgreSQLDialect.REGEX_MATCHES, jpaKit)),
|
||||
DOES_NOT_MATCH("doesnt_match", false, false,
|
||||
(val, regexClause) -> Restrictions.not(Restrictions.sqlRestriction(
|
||||
regexClause, val, StandardBasicTypes.STRING)),
|
||||
(val, regexClause, jpaKit) -> regexPredicate(val, DSpacePostgreSQLDialect.REGEX_NOT_MATCHES, jpaKit));
|
||||
|
||||
private final String code;
|
||||
/** Criteria builder for the old Hibernate API */
|
||||
@Deprecated(forRemoval = true)
|
||||
private final BiFunction<String, String, Criterion> criterionBuilder;
|
||||
private final TriFunction<String, String, JpaCriteriaBuilderKit<MetadataValue>, Predicate> predicateBuilder;
|
||||
private final boolean usesRegex;
|
||||
private final boolean negate;
|
||||
|
||||
QueryOperator(String code, boolean usesRegex, boolean negate,
|
||||
BiFunction<String, String, Criterion> criterionBuilder,
|
||||
TriFunction<String, String, JpaCriteriaBuilderKit<MetadataValue>, Predicate> predicateBuilder) {
|
||||
this.code = code;
|
||||
this.usesRegex = usesRegex;
|
||||
this.negate = negate;
|
||||
this.criterionBuilder = criterionBuilder;
|
||||
this.predicateBuilder = predicateBuilder;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean getUsesRegex() {
|
||||
return usesRegex;
|
||||
}
|
||||
|
||||
public boolean getNegate() {
|
||||
return negate;
|
||||
}
|
||||
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return criterionBuilder.apply(val, regexClause);
|
||||
}
|
||||
|
||||
public Predicate buildJpaPredicate(String val, String regexClause, JpaCriteriaBuilderKit<MetadataValue> jpaKit) {
|
||||
return predicateBuilder.apply(val, regexClause, jpaKit);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static QueryOperator get(String code) {
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equalsIgnoreCase(code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public BiFunction<String, String, Criterion> getCriterionBuilder() {
|
||||
return criterionBuilder;
|
||||
}
|
||||
|
||||
private static Predicate regexPredicate(String val, String regexFunction,
|
||||
JpaCriteriaBuilderKit<MetadataValue> jpaKit) {
|
||||
// Source: https://stackoverflow.com/questions/24995881/use-regular-expressions-in-jpa-criteriabuilder
|
||||
CriteriaBuilder builder = jpaKit.criteriaBuilder();
|
||||
Expression<String> patternExpression = builder.<String>literal(val);
|
||||
Path<String> path = jpaKit.root().get(MetadataValue_.VALUE);
|
||||
// "matches" comes from the name of the regex function
|
||||
// defined in class DSpacePostgreSQLDialect
|
||||
return builder.equal(builder
|
||||
.function(regexFunction, Boolean.class, path, patternExpression), Boolean.TRUE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.contentreport;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.content.MetadataField;
|
||||
|
||||
/**
|
||||
* Data structure representing a query predicate used by the Filtered Items report
|
||||
* to filter items to retrieve.
|
||||
* @author Jean-François Morin (Université Laval)
|
||||
*/
|
||||
public class QueryPredicate {
|
||||
|
||||
private List<MetadataField> fields = new ArrayList<>();
|
||||
private QueryOperator operator;
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Shortcut method that builds a QueryPredicate from a single field, an operator, and a value.
|
||||
* @param field Predicate subject
|
||||
* @param operator Predicate operator
|
||||
* @param value Predicate object
|
||||
* @return a QueryPredicate instance built from the provided parameters
|
||||
*/
|
||||
public static QueryPredicate of(MetadataField field, QueryOperator operator, String value) {
|
||||
var predicate = new QueryPredicate();
|
||||
predicate.fields.add(field);
|
||||
predicate.operator = operator;
|
||||
predicate.value = value;
|
||||
return predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method that builds a QueryPredicate from a list of fields, an operator, and a value.
|
||||
* @param fields Fields that form the predicate subject
|
||||
* @param operator Predicate operator
|
||||
* @param value Predicate object
|
||||
* @return a QueryPredicate instance built from the provided parameters
|
||||
*/
|
||||
public static QueryPredicate of(Collection<MetadataField> fields, QueryOperator operator, String value) {
|
||||
var predicate = new QueryPredicate();
|
||||
predicate.fields.addAll(fields);
|
||||
predicate.operator = operator;
|
||||
predicate.value = value;
|
||||
return predicate;
|
||||
}
|
||||
|
||||
public List<MetadataField> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public QueryOperator getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.contentreport.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.contentreport.Filter;
|
||||
import org.dspace.contentreport.FilteredCollection;
|
||||
import org.dspace.contentreport.FilteredItems;
|
||||
import org.dspace.contentreport.FilteredItemsQuery;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
public interface ContentReportService {
|
||||
|
||||
/**
|
||||
* Returns <code>true<</code> if Content Reports are enabled.
|
||||
* @return <code>true<</code> if Content Reports are enabled
|
||||
*/
|
||||
boolean getEnabled();
|
||||
|
||||
/**
|
||||
* Retrieves item statistics per collection according to a set of Boolean filters.
|
||||
* @param context DSpace context
|
||||
* @param filters Set of filters
|
||||
* @return a list of collections with the requested statistics for each of them
|
||||
*/
|
||||
List<FilteredCollection> findFilteredCollections(Context context, Collection<Filter> filters);
|
||||
|
||||
/**
|
||||
* Retrieves a list of items according to a set of criteria.
|
||||
* @param context DSpace context
|
||||
* @param query structured query to find items against
|
||||
* @return a list of items filtered according to the provided query
|
||||
*/
|
||||
FilteredItems findFilteredItems(Context context, FilteredItemsQuery query);
|
||||
|
||||
/**
|
||||
* Converts a metadata field name to a list of {@link MetadataField} instances
|
||||
* (one if no wildcards are used, possibly more otherwise).
|
||||
* @param context DSpace context
|
||||
* @param metadataField field to search for
|
||||
* @return a corresponding list of {@link MetadataField} entries
|
||||
*/
|
||||
List<MetadataField> getMetadataFields(org.dspace.core.Context context, String metadataField)
|
||||
throws SQLException;
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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.correctiontype;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
|
||||
/**
|
||||
* Interface class that model the CorrectionType.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public interface CorrectionType {
|
||||
|
||||
/**
|
||||
* Retrieves the unique identifier associated to the CorrectionType.
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* Retrieves the topic associated with the to the CorrectionType.
|
||||
*/
|
||||
public String getTopic();
|
||||
|
||||
/**
|
||||
* Checks whether the CorrectionType required related item.
|
||||
*/
|
||||
public boolean isRequiredRelatedItem();
|
||||
|
||||
/**
|
||||
* Checks whether target item is allowed for current CorrectionType
|
||||
*
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @throws AuthorizeException if authorize error
|
||||
* @throws SQLException if there's a database problem
|
||||
*/
|
||||
public boolean isAllowed(Context context, Item targetItem) throws AuthorizeException, SQLException;
|
||||
|
||||
/**
|
||||
* Checks whether target item and related item are allowed for current CorrectionType
|
||||
*
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @param relatedItem Related item
|
||||
* @throws AuthorizeException if authorize error
|
||||
* @throws SQLException if there's a database problem
|
||||
*/
|
||||
public boolean isAllowed(Context context, Item targetItem, Item relatedItem) throws AuthorizeException,SQLException;
|
||||
|
||||
/**
|
||||
* Creates a QAEvent for a specific target item.
|
||||
*
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @param reason Reason
|
||||
* @return QAEvent
|
||||
*/
|
||||
public QAEvent createCorrection(Context context, Item targetItem, QAMessageDTO reason);
|
||||
|
||||
/**
|
||||
* Creates a QAEvent for a target item and related item.
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @param relatedItem Related item
|
||||
* @param reason Reason
|
||||
* @return QAEvent
|
||||
*/
|
||||
public QAEvent createCorrection(Context context, Item targetItem, Item relatedItem, QAMessageDTO reason);
|
||||
|
||||
}
|
@@ -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.correctiontype;
|
||||
|
||||
import static org.dspace.content.QAEvent.DSPACE_USERS_SOURCE;
|
||||
import static org.dspace.correctiontype.WithdrawnCorrectionType.WITHDRAWAL_REINSTATE_GROUP;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation class for {@link CorrectionType}
|
||||
* that will reinstate target item if it's withdrawn.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class ReinstateCorrectionType implements CorrectionType, InitializingBean {
|
||||
|
||||
private String id;
|
||||
private String topic;
|
||||
private String creationForm;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem) throws SQLException {
|
||||
if (!targetItem.isWithdrawn()) {
|
||||
return false;
|
||||
}
|
||||
boolean isAdmin = authorizeService.isAdmin(context);
|
||||
if (!currentUserIsMemberOfwithdrawalReinstateGroup(context) && !isAdmin) {
|
||||
return false;
|
||||
}
|
||||
long tot = qaEventService.countSourcesByTarget(context, targetItem.getID());
|
||||
return tot == 0;
|
||||
}
|
||||
|
||||
private boolean currentUserIsMemberOfwithdrawalReinstateGroup(Context context) throws SQLException {
|
||||
String groupName = configurationService.getProperty(WITHDRAWAL_REINSTATE_GROUP);
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
return false;
|
||||
}
|
||||
Group withdrawalReinstateGroup = groupService.findByName(context, groupName);
|
||||
return withdrawalReinstateGroup != null && groupService.isMember(context, withdrawalReinstateGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem, Item relatedItem) throws AuthorizeException,
|
||||
SQLException {
|
||||
return isAllowed(context, targetItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, QAMessageDTO reason) {
|
||||
ObjectNode reasonJson = createReasonJson(reason);
|
||||
QAEvent qaEvent = new QAEvent(DSPACE_USERS_SOURCE,
|
||||
context.getCurrentUser().getID().toString(),
|
||||
targetItem.getID().toString(),
|
||||
targetItem.getName(),
|
||||
this.getTopic(),
|
||||
1.0,
|
||||
reasonJson.toString(),
|
||||
new Date()
|
||||
);
|
||||
|
||||
qaEventService.store(context, qaEvent);
|
||||
return qaEvent;
|
||||
}
|
||||
|
||||
private ObjectNode createReasonJson(QAMessageDTO reason) {
|
||||
CorrectionTypeMessageDTO mesasge = (CorrectionTypeMessageDTO) reason;
|
||||
ObjectNode jsonNode = new ObjectMapper().createObjectNode();
|
||||
jsonNode.put("reason", mesasge.getReason());
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, Item relatedItem, QAMessageDTO reason) {
|
||||
return this.createCorrection(context, targetItem, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequiredRelatedItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {}
|
||||
|
||||
public void setCreationForm(String creationForm) {
|
||||
this.creationForm = creationForm;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* 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.correctiontype;
|
||||
|
||||
import static org.dspace.content.QAEvent.DSPACE_USERS_SOURCE;
|
||||
import static org.dspace.core.Constants.READ;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation class for {@link CorrectionType}
|
||||
* that will withdrawn target item if it archived and wasn't withdrawn alredy.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class WithdrawnCorrectionType implements CorrectionType, InitializingBean {
|
||||
|
||||
public static final String WITHDRAWAL_REINSTATE_GROUP = "qaevents.withdraw-reinstate.group";
|
||||
|
||||
private String id;
|
||||
private String topic;
|
||||
private String creationForm;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem) throws SQLException {
|
||||
if (targetItem.isWithdrawn() || !targetItem.isArchived()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
authorizeService.authorizeAction(context, targetItem, READ);
|
||||
} catch (AuthorizeException e) {
|
||||
return false;
|
||||
}
|
||||
boolean isAdmin = authorizeService.isAdmin(context);
|
||||
if (!currentUserIsMemberOfwithdrawalReinstateGroup(context) && !isAdmin) {
|
||||
return false;
|
||||
}
|
||||
long tot = qaEventService.countSourcesByTarget(context, targetItem.getID());
|
||||
return tot == 0;
|
||||
}
|
||||
|
||||
private boolean currentUserIsMemberOfwithdrawalReinstateGroup(Context context) throws SQLException {
|
||||
String groupName = configurationService.getProperty(WITHDRAWAL_REINSTATE_GROUP);
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
return false;
|
||||
}
|
||||
Group withdrawalReinstateGroup = groupService.findByName(context, groupName);
|
||||
return withdrawalReinstateGroup != null && groupService.isMember(context, withdrawalReinstateGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, QAMessageDTO reason) {
|
||||
ObjectNode reasonJson = createReasonJson(reason);
|
||||
QAEvent qaEvent = new QAEvent(DSPACE_USERS_SOURCE,
|
||||
context.getCurrentUser().getID().toString(),
|
||||
targetItem.getID().toString(),
|
||||
targetItem.getName(),
|
||||
this.getTopic(),
|
||||
1.0,
|
||||
reasonJson.toString(),
|
||||
new Date()
|
||||
);
|
||||
|
||||
qaEventService.store(context, qaEvent);
|
||||
return qaEvent;
|
||||
}
|
||||
|
||||
private ObjectNode createReasonJson(QAMessageDTO reason) {
|
||||
CorrectionTypeMessageDTO mesasge = (CorrectionTypeMessageDTO) reason;
|
||||
ObjectNode jsonNode = new ObjectMapper().createObjectNode();
|
||||
jsonNode.put("reason", mesasge.getReason());
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem, Item relatedItem)
|
||||
throws AuthorizeException, SQLException {
|
||||
return isAllowed(context, targetItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, Item relatedItem, QAMessageDTO reason) {
|
||||
return createCorrection(context, targetItem, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequiredRelatedItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {}
|
||||
|
||||
public void setCreationForm(String creationForm) {
|
||||
this.creationForm = creationForm;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.correctiontype.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
|
||||
/**
|
||||
* Service interface class for the CorrectionType object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface CorrectionTypeService {
|
||||
|
||||
/**
|
||||
* Retrieves a CorrectionType object from the system based on a unique identifier.
|
||||
*
|
||||
* @param id The unique identifier of the CorrectionType object to be retrieved.
|
||||
* @return The CorrectionType object corresponding to the provided identifier,
|
||||
* or null if no object is found.
|
||||
*/
|
||||
public CorrectionType findOne(String id);
|
||||
|
||||
/**
|
||||
* Retrieves a list of all CorrectionType objects available in the system.
|
||||
*
|
||||
* @return Returns a List containing all CorrectionType objects in the system.
|
||||
*/
|
||||
public List<CorrectionType> findAll();
|
||||
|
||||
/**
|
||||
* Retrieves a list of CorrectionType objects related to the provided Item.
|
||||
*
|
||||
* @param context Current DSpace session.
|
||||
* @param item Target item
|
||||
* @throws AuthorizeException If authorize error
|
||||
* @throws SQLException If a database error occurs during the operation.
|
||||
*/
|
||||
public List<CorrectionType> findByItem(Context context, Item item) throws AuthorizeException, SQLException;
|
||||
|
||||
/**
|
||||
* Retrieves a CorrectionType object associated with a specific topic.
|
||||
*
|
||||
* @param topic The topic for which the CorrectionType object is to be retrieved.
|
||||
*/
|
||||
public CorrectionType findByTopic(String topic);
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.correctiontype.service.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
import org.dspace.correctiontype.service.CorrectionTypeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Service implementation class for the CorrectionType object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class CorrectionTypeServiceImpl implements CorrectionTypeService {
|
||||
|
||||
@Autowired
|
||||
private List<CorrectionType> correctionTypes;
|
||||
|
||||
@Override
|
||||
public CorrectionType findOne(String id) {
|
||||
return findAll().stream()
|
||||
.filter(correctionType -> correctionType.getId().equals(id))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CorrectionType> findAll() {
|
||||
return CollectionUtils.isNotEmpty(correctionTypes) ? correctionTypes : List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CorrectionType> findByItem(Context context, Item item) throws AuthorizeException, SQLException {
|
||||
List<CorrectionType> correctionTypes = new ArrayList<>();
|
||||
for (CorrectionType correctionType : findAll()) {
|
||||
if (correctionType.isAllowed(context, item)) {
|
||||
correctionTypes.add(correctionType);
|
||||
}
|
||||
}
|
||||
return correctionTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CorrectionType findByTopic(String topic) {
|
||||
return findAll().stream()
|
||||
.filter(correctionType -> correctionType.getTopic().equals(topic))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
@@ -17,6 +17,7 @@ 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.Collection;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
@@ -69,7 +70,7 @@ public class RequiredMetadata extends AbstractCurationTask {
|
||||
handle = "in workflow";
|
||||
}
|
||||
sb.append("Item: ").append(handle);
|
||||
for (String req : getReqList(item.getOwningCollection().getHandle())) {
|
||||
for (String req : getReqList(item.getOwningCollection())) {
|
||||
List<MetadataValue> vals = itemService.getMetadataByMetadataString(item, req);
|
||||
if (vals.size() == 0) {
|
||||
sb.append(" missing required field: ").append(req);
|
||||
@@ -91,14 +92,14 @@ public class RequiredMetadata extends AbstractCurationTask {
|
||||
}
|
||||
}
|
||||
|
||||
protected List<String> getReqList(String handle) throws DCInputsReaderException {
|
||||
List<String> reqList = reqMap.get(handle);
|
||||
protected List<String> getReqList(Collection collection) throws DCInputsReaderException {
|
||||
List<String> reqList = reqMap.get(collection.getHandle());
|
||||
if (reqList == null) {
|
||||
reqList = reqMap.get("default");
|
||||
}
|
||||
if (reqList == null) {
|
||||
reqList = new ArrayList<String>();
|
||||
List<DCInputSet> inputSet = reader.getInputsByCollectionHandle(handle);
|
||||
List<DCInputSet> inputSet = reader.getInputsByCollection(collection);
|
||||
for (DCInputSet inputs : inputSet) {
|
||||
for (DCInput[] row : inputs.getFields()) {
|
||||
for (DCInput input : row) {
|
||||
|
@@ -7,14 +7,20 @@
|
||||
*/
|
||||
package org.dspace.discovery;
|
||||
|
||||
import static org.dspace.discovery.IndexClientOptions.TYPE_OPTION;
|
||||
|
||||
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 java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
@@ -51,6 +57,17 @@ public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguratio
|
||||
return;
|
||||
}
|
||||
|
||||
String type = null;
|
||||
if (commandLine.hasOption(TYPE_OPTION)) {
|
||||
List<String> indexableObjectTypes = IndexObjectFactoryFactory.getInstance().getIndexFactories().stream()
|
||||
.map((indexFactory -> indexFactory.getType())).collect(Collectors.toList());
|
||||
type = commandLine.getOptionValue(TYPE_OPTION);
|
||||
if (!indexableObjectTypes.contains(type)) {
|
||||
handler.handleException(String.format("%s is not a valid indexable object type, options: %s",
|
||||
type, Arrays.toString(indexableObjectTypes.toArray())));
|
||||
}
|
||||
}
|
||||
|
||||
/** Acquire from dspace-services in future */
|
||||
/**
|
||||
* new DSpace.getServiceManager().getServiceByName("org.dspace.discovery.SolrIndexer");
|
||||
@@ -113,6 +130,10 @@ public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguratio
|
||||
} else if (indexClientOptions == IndexClientOptions.BUILD ||
|
||||
indexClientOptions == IndexClientOptions.BUILDANDSPELLCHECK) {
|
||||
handler.logInfo("(Re)building index from scratch.");
|
||||
if (StringUtils.isNotBlank(type)) {
|
||||
handler.logWarning(String.format("Type option, %s, not applicable for entire index rebuild option, b" +
|
||||
", type will be ignored", TYPE_OPTION));
|
||||
}
|
||||
indexer.deleteIndex();
|
||||
indexer.createIndex(context);
|
||||
if (indexClientOptions == IndexClientOptions.BUILDANDSPELLCHECK) {
|
||||
@@ -133,14 +154,14 @@ public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguratio
|
||||
} else if (indexClientOptions == IndexClientOptions.UPDATE ||
|
||||
indexClientOptions == IndexClientOptions.UPDATEANDSPELLCHECK) {
|
||||
handler.logInfo("Updating Index");
|
||||
indexer.updateIndex(context, false);
|
||||
indexer.updateIndex(context, false, type);
|
||||
if (indexClientOptions == IndexClientOptions.UPDATEANDSPELLCHECK) {
|
||||
checkRebuildSpellCheck(commandLine, indexer);
|
||||
}
|
||||
} else if (indexClientOptions == IndexClientOptions.FORCEUPDATE ||
|
||||
indexClientOptions == IndexClientOptions.FORCEUPDATEANDSPELLCHECK) {
|
||||
handler.logInfo("Updating Index");
|
||||
indexer.updateIndex(context, true);
|
||||
indexer.updateIndex(context, true, type);
|
||||
if (indexClientOptions == IndexClientOptions.FORCEUPDATEANDSPELLCHECK) {
|
||||
checkRebuildSpellCheck(commandLine, indexer);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user