diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d6913078e4..a2a0d6294f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 338c7371f6..9f1e407cff 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -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 }} diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 46bdab3b68..aa8327f4d1 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -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), diff --git a/Dockerfile b/Dockerfile index bef894d79b..5bcd683768 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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. diff --git a/Dockerfile.test b/Dockerfile.test index 16a04d0002..6fcc4eda6b 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -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 diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index 7dbdde3703..b2c6df636b 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -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: diff --git a/docker-compose.yml b/docker-compose.yml index 14f47ebdb6..2e3d640940 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 849dbd1356..f6b06f69ed 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -528,7 +528,7 @@ org.hamcrest - hamcrest-all + hamcrest test @@ -620,7 +620,7 @@ com.maxmind.geoip2 geoip2 - 2.11.0 + 2.17.0 org.apache.ant @@ -784,7 +784,7 @@ com.opencsv opencsv - 5.7.1 + 5.9 @@ -867,32 +867,32 @@ io.netty netty-buffer - 4.1.94.Final + 4.1.106.Final io.netty netty-transport - 4.1.94.Final + 4.1.106.Final io.netty netty-transport-native-unix-common - 4.1.94.Final + 4.1.106.Final io.netty netty-common - 4.1.94.Final + 4.1.106.Final io.netty netty-handler - 4.1.94.Final + 4.1.106.Final io.netty netty-codec - 4.1.94.Final + 4.1.106.Final org.apache.velocity @@ -902,7 +902,7 @@ org.xmlunit xmlunit-core - 2.8.0 + 2.9.1 test diff --git a/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java b/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java index 81250e9c82..58b8549391 100644 --- a/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java +++ b/dspace-api/src/main/java/org/dspace/administer/CreateAdministrator.java @@ -116,6 +116,17 @@ public 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(); } diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionProvider.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionProvider.java new file mode 100644 index 0000000000..f5acd2ccbc --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionProvider.java @@ -0,0 +1,140 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.suggestion; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +import org.apache.logging.log4j.Logger; +import org.apache.solr.client.solrj.SolrServerException; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.external.model.ExternalDataObject; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Suggestion provider that read the suggestion from the local suggestion solr + * core + * + * @author Andrea Bollini (andrea.bollini at 4science dot it) + * + */ +public abstract class SolrSuggestionProvider implements SuggestionProvider { + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SolrSuggestionProvider.class); + + @Autowired + protected ItemService itemService; + + @Autowired + protected SolrSuggestionStorageService solrSuggestionStorageService; + + private String sourceName; + + public String getSourceName() { + return sourceName; + } + + public void setSourceName(String sourceName) { + this.sourceName = sourceName; + } + + public void setItemService(ItemService itemService) { + this.itemService = itemService; + } + + @Override + public long countAllTargets(Context context) { + try { + return this.solrSuggestionStorageService.countAllTargets(context, sourceName); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public long countUnprocessedSuggestionByTarget(Context context, UUID target) { + try { + return this.solrSuggestionStorageService.countUnprocessedSuggestionByTarget(context, sourceName, target); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public List findAllUnprocessedSuggestions(Context context, UUID target, int pageSize, long offset, + boolean ascending) { + + try { + return this.solrSuggestionStorageService.findAllUnprocessedSuggestions(context, sourceName, + target, pageSize, offset, ascending); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + + } + + @Override + public List findAllTargets(Context context, int pageSize, long offset) { + try { + return this.solrSuggestionStorageService.findAllTargets(context, sourceName, pageSize, offset); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public Suggestion findUnprocessedSuggestion(Context context, UUID target, String id) { + try { + return this.solrSuggestionStorageService.findUnprocessedSuggestion(context, sourceName, target, id); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public SuggestionTarget findTarget(Context context, UUID target) { + try { + return this.solrSuggestionStorageService.findTarget(context, sourceName, target); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void rejectSuggestion(Context context, UUID target, String idPart) { + Suggestion suggestion = findUnprocessedSuggestion(context, target, idPart); + try { + solrSuggestionStorageService.flagSuggestionAsProcessed(suggestion); + } catch (SolrServerException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void flagRelatedSuggestionsAsProcessed(Context context, ExternalDataObject externalDataObject) { + if (!isExternalDataObjectPotentiallySuggested(context, externalDataObject)) { + return; + } + try { + solrSuggestionStorageService.flagAllSuggestionAsProcessed(sourceName, externalDataObject.getId()); + } catch (SolrServerException | IOException e) { + log.error(e.getMessage(), e); + } + } + + /** + * check if the externalDataObject may have suggestion + * @param context + * @param externalDataObject + * @return true if the externalDataObject could be suggested by this provider + * (i.e. it comes from a DataProvider used by this suggestor) + */ + protected abstract boolean isExternalDataObjectPotentiallySuggested(Context context, + ExternalDataObject externalDataObject); +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionStorageService.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionStorageService.java new file mode 100644 index 0000000000..b7de6146f2 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionStorageService.java @@ -0,0 +1,191 @@ +/** + * 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.suggestion; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +import org.apache.solr.client.solrj.SolrServerException; +import org.dspace.core.Context; + +/** + * Service to deal with the local suggestion solr core used by the + * SolrSuggestionProvider(s) + * + * @author Andrea Bollini (andrea.bollini at 4science dot it) + * @author Luca Giamminonni (luca.giamminonni at 4science dot it) + * + */ +public interface SolrSuggestionStorageService { + public static final String SOURCE = "source"; + /** This is the URI Part of the suggestion source:target:id */ + public static final String SUGGESTION_FULLID = "suggestion_fullid"; + public static final String SUGGESTION_ID = "suggestion_id"; + public static final String TARGET_ID = "target_id"; + public static final String TITLE = "title"; + public static final String DATE = "date"; + public static final String DISPLAY = "display"; + public static final String CONTRIBUTORS = "contributors"; + public static final String ABSTRACT = "abstract"; + public static final String CATEGORY = "category"; + public static final String EXTERNAL_URI = "external-uri"; + public static final String PROCESSED = "processed"; + public static final String SCORE = "trust"; + public static final String EVIDENCES = "evidences"; + + /** + * Add a new suggestion to SOLR + * + * @param suggestion + * @param force true if the suggestion must be reindexed + * @param commit + * @throws IOException + * @throws SolrServerException + */ + public void addSuggestion(Suggestion suggestion, boolean force, boolean commit) + throws SolrServerException, IOException; + + /** + * Return true if the suggestion is already in SOLR and flagged as processed + * + * @param suggestion + * @return true if the suggestion is already in SOLR and flagged as processed + * @throws IOException + * @throws SolrServerException + */ + public boolean exist(Suggestion suggestion) throws SolrServerException, IOException; + + /** + * Delete a suggestion from SOLR if any + * + * @param suggestion + * @throws IOException + * @throws SolrServerException + */ + public void deleteSuggestion(Suggestion suggestion) throws SolrServerException, IOException; + + /** + * Flag a suggestion as processed in SOLR if any + * + * @param suggestion + * @throws IOException + * @throws SolrServerException + */ + public void flagSuggestionAsProcessed(Suggestion suggestion) throws SolrServerException, IOException; + + /** + * Delete all the suggestions from SOLR if any related to a specific target + * + * @param target + * @throws IOException + * @throws SolrServerException + */ + public void deleteTarget(SuggestionTarget target) throws SolrServerException, IOException; + + /** + * Performs an explicit commit, causing pending documents to be committed for + * indexing. + * + * @throws SolrServerException + * @throws IOException + */ + void commit() throws SolrServerException, IOException; + + /** + * Flag all the suggestion related to the given source and id as processed. + * + * @param source the source name + * @param idPart the id's last part + * @throws SolrServerException + * @throws IOException + */ + void flagAllSuggestionAsProcessed(String source, String idPart) throws SolrServerException, IOException; + + /** + * Count all the targets related to the given source. + * + * @param source the source name + * @return the target's count + * @throws IOException + * @throws SolrServerException + */ + long countAllTargets(Context context, String source) throws SolrServerException, IOException; + + /** + * Count all the unprocessed suggestions related to the given source and target. + * + * @param context the DSpace Context + * @param source the source name + * @param target the target id + * @return the suggestion count + * @throws SolrServerException + * @throws IOException + */ + long countUnprocessedSuggestionByTarget(Context context, String source, UUID target) + throws SolrServerException, IOException; + + /** + * Find all the unprocessed suggestions related to the given source and target. + * + * @param context the DSpace Context + * @param source the source name + * @param target the target id + * @param pageSize the page size + * @param offset the page offset + * @param ascending true to retrieve the suggestions ordered by score + * ascending + * @return the found suggestions + * @throws SolrServerException + * @throws IOException + */ + List findAllUnprocessedSuggestions(Context context, String source, UUID target, + int pageSize, long offset, boolean ascending) throws SolrServerException, IOException; + + /** + * + * Find all the suggestion targets related to the given source. + * + * @param context the DSpace Context + * @param source the source name + * @param pageSize the page size + * @param offset the page offset + * @return the found suggestion targets + * @throws SolrServerException + * @throws IOException + */ + List findAllTargets(Context context, String source, int pageSize, long offset) + throws SolrServerException, IOException; + + /** + * Find an unprocessed suggestion by the given source, target id and suggestion + * id. + * + * @param context the DSpace Context + * @param source the source name + * @param target the target id + * @param id the suggestion id + * @return the suggestion, if any + * @throws SolrServerException + * @throws IOException + */ + Suggestion findUnprocessedSuggestion(Context context, String source, UUID target, String id) + throws SolrServerException, IOException; + + /** + * Find a suggestion target by the given source and target. + * + * @param context the DSpace Context + * @param source the source name + * @param target the target id + * @return the suggestion target, if any + * @throws SolrServerException + * @throws IOException + */ + SuggestionTarget findTarget(Context context, String source, UUID target) throws SolrServerException, IOException; +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionStorageServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionStorageServiceImpl.java new file mode 100644 index 0000000000..6281b69107 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SolrSuggestionStorageServiceImpl.java @@ -0,0 +1,360 @@ +/** + * 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.suggestion; + +import static org.apache.commons.collections.CollectionUtils.isEmpty; + +import java.io.IOException; +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.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; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.response.FacetField; +import org.apache.solr.client.solrj.response.FacetField.Count; +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; +import org.dspace.core.Context; +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) + * + * @author Andrea Bollini (andrea.bollini at 4science dot it) + * + */ +public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageService { + private static final Logger log = LogManager.getLogger(SolrSuggestionStorageServiceImpl.class); + + protected SolrClient solrSuggestionClient; + + @Autowired + private ItemService itemService; + + /** + * Get solr client which use suggestion core + * + * @return solr client + */ + protected SolrClient getSolr() { + if (solrSuggestionClient == null) { + String solrService = DSpaceServicesFactory.getInstance().getConfigurationService() + .getProperty("suggestion.solr.server", "http://localhost:8983/solr/suggestion"); + solrSuggestionClient = new HttpSolrClient.Builder(solrService).build(); + } + return solrSuggestionClient; + } + + @Override + public void addSuggestion(Suggestion suggestion, boolean force, boolean commit) + throws SolrServerException, IOException { + if (force || !exist(suggestion)) { + 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]); + document.addField(TARGET_ID, suggestion.getTarget().getID().toString()); + document.addField(DISPLAY, suggestion.getDisplay()); + document.addField(TITLE, getFirstValue(suggestion, "dc", "title", null)); + document.addField(DATE, getFirstValue(suggestion, "dc", "date", "issued")); + document.addField(CONTRIBUTORS, getAllValues(suggestion, "dc", "contributor", "author")); + document.addField(ABSTRACT, getFirstValue(suggestion, "dc", "description", "abstract")); + document.addField(CATEGORY, getAllValues(suggestion, "dc", "source", null)); + document.addField(EXTERNAL_URI, suggestion.getExternalSourceUri()); + document.addField(SCORE, suggestion.getScore()); + document.addField(PROCESSED, false); + document.addField(EVIDENCES, jsonMapper.writeValueAsString(suggestion.getEvidences())); + getSolr().add(document); + if (commit) { + getSolr().commit(); + } + } + } + + @Override + public void commit() throws SolrServerException, IOException { + getSolr().commit(); + } + + private List getAllValues(Suggestion suggestion, String schema, String element, String qualifier) { + return suggestion.getMetadata().stream() + .filter(st -> StringUtils.isNotBlank(st.getValue()) && StringUtils.equals(st.getSchema(), schema) + && StringUtils.equals(st.getElement(), element) + && StringUtils.equals(st.getQualifier(), qualifier)) + .map(st -> st.getValue()).collect(Collectors.toList()); + } + + private String getFirstValue(Suggestion suggestion, String schema, String element, String qualifier) { + return suggestion.getMetadata().stream() + .filter(st -> StringUtils.isNotBlank(st.getValue()) + && StringUtils.equals(st.getSchema(), schema) + && StringUtils.equals(st.getElement(), element) + && StringUtils.equals(st.getQualifier(), qualifier)) + .map(st -> st.getValue()).findFirst().orElse(null); + } + + @Override + public boolean exist(Suggestion suggestion) throws SolrServerException, IOException { + SolrQuery query = new SolrQuery( + SUGGESTION_FULLID + ":\"" + suggestion.getID() + "\" AND " + PROCESSED + ":true"); + return getSolr().query(query).getResults().getNumFound() == 1; + } + + @Override + public void deleteSuggestion(Suggestion suggestion) throws SolrServerException, IOException { + getSolr().deleteById(suggestion.getID()); + getSolr().commit(); + } + + @Override + public void flagSuggestionAsProcessed(Suggestion suggestion) throws SolrServerException, IOException { + SolrInputDocument sdoc = new SolrInputDocument(); + sdoc.addField(SUGGESTION_FULLID, suggestion.getID()); + Map fieldModifier = new HashMap<>(1); + fieldModifier.put("set", true); + sdoc.addField(PROCESSED, fieldModifier); // add the map as the field value + getSolr().add(sdoc); + getSolr().commit(); + } + + @Override + public void flagAllSuggestionAsProcessed(String source, String idPart) throws SolrServerException, IOException { + SolrQuery query = new SolrQuery(SOURCE + ":" + source + " AND " + SUGGESTION_ID + ":\"" + idPart + "\""); + query.setRows(Integer.MAX_VALUE); + query.setFields(SUGGESTION_FULLID); + SolrDocumentList results = getSolr().query(query).getResults(); + if (results.getNumFound() > 0) { + for (SolrDocument rDoc : results) { + SolrInputDocument sdoc = new SolrInputDocument(); + sdoc.addField(SUGGESTION_FULLID, rDoc.getFieldValue(SUGGESTION_FULLID)); + Map fieldModifier = new HashMap<>(1); + fieldModifier.put("set", true); + sdoc.addField(PROCESSED, fieldModifier); // add the map as the field value + getSolr().add(sdoc); + } + } + getSolr().commit(); + } + + @Override + public void deleteTarget(SuggestionTarget target) throws SolrServerException, IOException { + getSolr().deleteByQuery( + SOURCE + ":" + target.getSource() + " AND " + TARGET_ID + ":" + target.getTarget().getID().toString()); + getSolr().commit(); + } + + @Override + public long countAllTargets(Context context, String source) throws SolrServerException, IOException { + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setRows(0); + solrQuery.setQuery(SOURCE + ":" + source); + solrQuery.addFilterQuery(PROCESSED + ":false"); + solrQuery.setFacet(true); + solrQuery.setFacetMinCount(1); + solrQuery.addFacetField(TARGET_ID); + solrQuery.setFacetLimit(Integer.MAX_VALUE); + QueryResponse response = getSolr().query(solrQuery); + return response.getFacetField(TARGET_ID).getValueCount(); + } + + @Override + public long countUnprocessedSuggestionByTarget(Context context, String source, UUID target) + throws SolrServerException, IOException { + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setRows(0); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery( + SOURCE + ":" + source, + TARGET_ID + ":" + target.toString(), + PROCESSED + ":false"); + + QueryResponse response = getSolr().query(solrQuery); + return response.getResults().getNumFound(); + } + + @Override + public List findAllUnprocessedSuggestions(Context context, String source, UUID target, + int pageSize, long offset, boolean ascending) throws SolrServerException, IOException { + + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setRows(pageSize); + solrQuery.setStart((int) offset); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery( + SOURCE + ":" + source, + TARGET_ID + ":" + target.toString(), + PROCESSED + ":false"); + + if (ascending) { + solrQuery.addSort(SortClause.asc("trust")); + } else { + solrQuery.addSort(SortClause.desc("trust")); + } + + solrQuery.addSort(SortClause.desc("date")); + solrQuery.addSort(SortClause.asc("title")); + + QueryResponse response = getSolr().query(solrQuery); + List suggestions = new ArrayList(); + for (SolrDocument solrDoc : response.getResults()) { + suggestions.add(convertSolrDoc(context, solrDoc, source)); + } + return suggestions; + + } + + @Override + public List findAllTargets(Context context, String source, int pageSize, long offset) + throws SolrServerException, IOException { + + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setRows(0); + solrQuery.setQuery(SOURCE + ":" + source); + solrQuery.addFilterQuery(PROCESSED + ":false"); + solrQuery.setFacet(true); + solrQuery.setFacetMinCount(1); + solrQuery.addFacetField(TARGET_ID); + solrQuery.setParam(FacetParams.FACET_OFFSET, String.valueOf(offset)); + solrQuery.setFacetLimit((int) (pageSize)); + QueryResponse response = getSolr().query(solrQuery); + FacetField facetField = response.getFacetField(TARGET_ID); + List suggestionTargets = new ArrayList(); + int idx = 0; + for (Count c : facetField.getValues()) { + SuggestionTarget target = new SuggestionTarget(); + target.setSource(source); + target.setTotal((int) c.getCount()); + target.setTarget(findItem(context, c.getName())); + suggestionTargets.add(target); + idx++; + } + return suggestionTargets; + + } + + @Override + public Suggestion findUnprocessedSuggestion(Context context, String source, UUID target, String id) + throws SolrServerException, IOException { + + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setRows(1); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery( + SOURCE + ":" + source, + TARGET_ID + ":" + target.toString(), + SUGGESTION_ID + ":\"" + id + "\"", + PROCESSED + ":false"); + + SolrDocumentList results = getSolr().query(solrQuery).getResults(); + return isEmpty(results) ? null : convertSolrDoc(context, results.get(0), source); + } + + @Override + public SuggestionTarget findTarget(Context context, String source, UUID target) + throws SolrServerException, IOException { + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setRows(0); + solrQuery.setQuery(SOURCE + ":" + source); + solrQuery.addFilterQuery( + TARGET_ID + ":" + target.toString(), + PROCESSED + ":false"); + QueryResponse response = getSolr().query(solrQuery); + SuggestionTarget sTarget = new SuggestionTarget(); + sTarget.setSource(source); + sTarget.setTotal((int) response.getResults().getNumFound()); + Item itemTarget = findItem(context, target); + if (itemTarget != null) { + sTarget.setTarget(itemTarget); + } else { + return null; + } + return sTarget; + } + + private Suggestion convertSolrDoc(Context context, SolrDocument solrDoc, String sourceName) { + Item target = findItem(context, (String) solrDoc.getFieldValue(TARGET_ID)); + + Suggestion suggestion = new Suggestion(sourceName, target, (String) solrDoc.getFieldValue(SUGGESTION_ID)); + suggestion.setDisplay((String) solrDoc.getFieldValue(DISPLAY)); + suggestion.getMetadata() + .add(new MetadataValueDTO("dc", "title", null, null, (String) solrDoc.getFieldValue(TITLE))); + suggestion.getMetadata() + .add(new MetadataValueDTO("dc", "date", "issued", null, (String) solrDoc.getFieldValue(DATE))); + suggestion.getMetadata().add( + new MetadataValueDTO("dc", "description", "abstract", null, (String) solrDoc.getFieldValue(ABSTRACT))); + + suggestion.setExternalSourceUri((String) solrDoc.getFieldValue(EXTERNAL_URI)); + if (solrDoc.containsKey(CATEGORY)) { + for (Object o : solrDoc.getFieldValues(CATEGORY)) { + suggestion.getMetadata().add( + new MetadataValueDTO("dc", "source", null, null, (String) o)); + } + } + if (solrDoc.containsKey(CONTRIBUTORS)) { + for (Object o : solrDoc.getFieldValues(CONTRIBUTORS)) { + suggestion.getMetadata().add( + new MetadataValueDTO("dc", "contributor", "author", null, (String) o)); + } + } + String evidencesJson = (String) solrDoc.getFieldValue(EVIDENCES); + ObjectMapper jsonMapper = new JsonMapper(); + jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + List evidences = new LinkedList(); + try { + evidences = jsonMapper.readValue(evidencesJson, new TypeReference>() {}); + } catch (JsonProcessingException e) { + log.error(e); + } + suggestion.getEvidences().addAll(evidences); + return suggestion; + } + + private Item findItem(Context context, UUID itemId) { + try { + return itemService.find(context, itemId); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private Item findItem(Context context, String itemId) { + return findItem(context, UUIDUtils.fromString(itemId)); + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/Suggestion.java b/dspace-api/src/main/java/org/dspace/app/suggestion/Suggestion.java new file mode 100644 index 0000000000..7812cbd522 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/Suggestion.java @@ -0,0 +1,99 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.suggestion; + +import java.util.LinkedList; +import java.util.List; + +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 evidences = new LinkedList(); + + private List metadata = new LinkedList(); + + /** 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; + this.id = source + ":" + target.getID().toString() + ":" + idPart; + } + + public String getDisplay() { + return display; + } + + public void setDisplay(String display) { + this.display = display; + } + + public String getSource() { + return source; + } + + public String getExternalSourceUri() { + return externalSourceUri; + } + + public void setExternalSourceUri(String externalSourceUri) { + this.externalSourceUri = externalSourceUri; + } + + public List getEvidences() { + return evidences; + } + + public List getMetadata() { + return metadata; + } + + public Item getTarget() { + return target; + } + + public String getID() { + return id; + } + + public Double getScore() { + if (evidences != null && evidences.size() > 0) { + double score = 0; + for (SuggestionEvidence evidence : evidences) { + score += evidence.getScore(); + } + return score; + } + return null; + } +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionEvidence.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionEvidence.java new file mode 100644 index 0000000000..d7f04929a1 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionEvidence.java @@ -0,0 +1,61 @@ +/** + * 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.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() { + } + + public SuggestionEvidence(String name, double score, String notes) { + this.name = name; + this.score = score; + this.notes = notes; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getScore() { + return score; + } + + public void setScore(double score) { + this.score = score; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionProvider.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionProvider.java new file mode 100644 index 0000000000..7cfc3cfb53 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionProvider.java @@ -0,0 +1,54 @@ +/** + * 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.suggestion; + +import java.util.List; +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 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 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); + +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionService.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionService.java new file mode 100644 index 0000000000..41d33026ed --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionService.java @@ -0,0 +1,61 @@ +/** + * 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.suggestion; + +import java.util.List; +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 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 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 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 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 getSuggestionProviders(); +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionServiceImpl.java new file mode 100644 index 0000000000..66773fbc12 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionServiceImpl.java @@ -0,0 +1,194 @@ +/** + * 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.suggestion; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.annotation.Resource; + +import org.apache.logging.log4j.Logger; +import org.dspace.core.Context; +import org.springframework.stereotype.Service; + +@Service +public class SuggestionServiceImpl implements SuggestionService { + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SuggestionServiceImpl.class); + + @Resource(name = "suggestionProviders") + private Map providersMap; + + @Override + public List getSuggestionProviders() { + if (providersMap != null) { + return providersMap.values().stream().collect(Collectors.toList()); + } + return null; + } + + @Override + public SuggestionTarget find(Context context, String source, UUID id) { + if (providersMap.containsKey(source)) { + return providersMap.get(source).findTarget(context, id); + } else { + return null; + } + } + + @Override + public long countAll(Context context, String source) { + if (providersMap.containsKey(source)) { + return providersMap.get(source).countAllTargets(context); + } else { + return 0; + } + } + + @Override + public List findAllTargets(Context context, String source, int pageSize, long offset) { + if (providersMap.containsKey(source)) { + return providersMap.get(source).findAllTargets(context, pageSize, offset); + } else { + return null; + } + } + + @Override + public long countAllByTarget(Context context, UUID target) { + int count = 0; + for (String provider : providersMap.keySet()) { + if (providersMap.get(provider).countUnprocessedSuggestionByTarget(context, target) > 0) { + count++; + } + } + return count; + } + + @Override + public List findByTarget(Context context, UUID target, int pageSize, long offset) { + List fullSourceTargets = new ArrayList(); + 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); + } + } + fullSourceTargets.sort(new Comparator() { + @Override + public int compare(SuggestionTarget arg0, SuggestionTarget arg1) { + return -(arg0.getTotal() - arg1.getTotal()); + } + } + ); + // 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()); + } + + @Override + public long countSources(Context context) { + return providersMap.size(); + } + + @Override + public SuggestionSource findSource(Context context, String source) { + if (providersMap.containsKey(source)) { + SuggestionSource ssource = new SuggestionSource(source); + ssource.setTotal((int) providersMap.get(source).countAllTargets(context)); + return ssource; + } else { + return null; + } + } + + @Override + public List findAllSources(Context context, int pageSize, long offset) { + List fullSources = getSources(context).stream().skip(offset).limit(pageSize) + .collect(Collectors.toList()); + return fullSources; + } + + private List getSources(Context context) { + List results = new ArrayList(); + for (String source : providersMap.keySet()) { + SuggestionSource ssource = new SuggestionSource(source); + ssource.setTotal((int) providersMap.get(source).countAllTargets(context)); + results.add(ssource); + } + return results; + } + + @Override + public long countAllByTargetAndSource(Context context, String source, UUID target) { + if (providersMap.containsKey(source)) { + return providersMap.get(source).countUnprocessedSuggestionByTarget(context, target); + } + return 0; + } + + @Override + public List findByTargetAndSource(Context context, UUID target, String source, int pageSize, + long offset, boolean ascending) { + if (providersMap.containsKey(source)) { + return providersMap.get(source).findAllUnprocessedSuggestions(context, target, pageSize, offset, ascending); + } + return null; + } + + @Override + public Suggestion findUnprocessedSuggestion(Context context, String id) { + String source = null; + UUID target = null; + String idPart = null; + String[] split; + try { + split = id.split(":", 3); + source = split[0]; + target = UUID.fromString(split[1]); + idPart = split[2]; + } catch (Exception e) { + log.warn("findSuggestion got an invalid id " + id + ", return null"); + return null; + } + if (split.length != 3) { + return null; + } + if (providersMap.containsKey(source)) { + return providersMap.get(source).findUnprocessedSuggestion(context, target, idPart); + } + return null; + } + + @Override + public void rejectSuggestion(Context context, String id) { + String source = null; + UUID target = null; + String idPart = null; + String[] split; + try { + split = id.split(":", 3); + source = split[0]; + target = UUID.fromString(split[1]); + idPart = split[2]; + } catch (Exception e) { + log.warn("rejectSuggestion got an invalid id " + id + ", doing nothing"); + return; + } + if (split.length != 3) { + return; + } + if (providersMap.containsKey(source)) { + providersMap.get(source).rejectSuggestion(context, target, idPart); + } + + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionSource.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionSource.java new file mode 100644 index 0000000000..6dcc3f7e1e --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionSource.java @@ -0,0 +1,49 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.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() { + } + + /** + * Summarize the available suggestions from a source. + * + * @param name the name must be not null + */ + public SuggestionSource(String name) { + super(); + this.name = name; + } + + public String getID() { + return name; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionTarget.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionTarget.java new file mode 100644 index 0000000000..db82aa8081 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionTarget.java @@ -0,0 +1,75 @@ +/** + * 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.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 repository item (usually a person item) into a suggestion target. + * + * @param item must be not null + */ + public SuggestionTarget(Item item) { + super(); + this.target = item; + } + + /** + * The suggestion target uses the concatenation of the source and target uuid separated by colon as id + * + * @return the source:uuid of the wrapped item + */ + public String getID() { + return source + ":" + (target != null ? target.getID() : ""); + } + + public Item getTarget() { + return target; + } + + public void setTarget(Item target) { + this.target = target; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionUtils.java b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionUtils.java new file mode 100644 index 0000000000..30ced75fc9 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/SuggestionUtils.java @@ -0,0 +1,111 @@ +/** + * 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.suggestion; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.external.model.ExternalDataObject; + +/** + * This utility class provides convenient methods to deal with the + * {@link ExternalDataObject} for the purpose of the Suggestion framework + * + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +public class SuggestionUtils { + private SuggestionUtils() { + } + /** + * This method receive an ExternalDataObject and a metadatum key. + * It return only the values of the Metadata associated with the key. + * + * @param record the ExternalDataObject to extract metadata from + * @param schema schema of the searching record + * @param element element of the searching record + * @param qualifier qualifier of the searching record + * @return value of the first matching record + */ + public static List getAllEntriesByMetadatum(ExternalDataObject record, String schema, String element, + String qualifier) { + return record.getMetadata().stream() + .filter(x -> + StringUtils.equals(x.getSchema(), schema) + && StringUtils.equals(x.getElement(), element) + && StringUtils.equals(x.getQualifier(), qualifier)) + .map(x -> x.getValue()).collect(Collectors.toList()); + } + + /** + * This method receive an ExternalDataObject and a metadatum key. + * It return only the values of the Metadata associated with the key. + * + * @param record the ExternalDataObject to extract metadata from + * @param metadataFieldKey the metadata field key (i.e. dc.title or dc.contributor.author), + * the jolly char is not supported + * @return value of the first matching record + */ + public static List getAllEntriesByMetadatum(ExternalDataObject record, String metadataFieldKey) { + if (metadataFieldKey == null) { + return Collections.EMPTY_LIST; + } + String[] fields = metadataFieldKey.split("\\."); + String schema = fields[0]; + String element = fields[1]; + String qualifier = null; + if (fields.length == 3) { + qualifier = fields[2]; + } + return getAllEntriesByMetadatum(record, schema, element, qualifier); + } + + /** + * This method receive and ExternalDataObject and a metadatum key. + * It return only the value of the first Metadatum from the list associated with the key. + * + * @param record the ExternalDataObject to extract metadata from + * @param schema schema of the searching record + * @param element element of the searching record + * @param qualifier qualifier of the searching record + * @return value of the first matching record + */ + public static String getFirstEntryByMetadatum(ExternalDataObject record, String schema, String element, + String qualifier) { + return record.getMetadata().stream() + .filter(x -> + StringUtils.equals(x.getSchema(), schema) + && StringUtils.equals(x.getElement(), element) + && StringUtils.equals(x.getQualifier(), qualifier)) + .map(x -> x.getValue()).findFirst().orElse(null); + } + + /** + * This method receive and ExternalDataObject and a metadatum key. + * It return only the value of the first Metadatum from the list associated with the key. + * + * @param record the ExternalDataObject to extract metadata from + * @param metadataFieldKey the metadata field key (i.e. dc.title or dc.contributor.author), + * the jolly char is not supported + * @return value of the first matching record + */ + public static String getFirstEntryByMetadatum(ExternalDataObject record, String metadataFieldKey) { + if (metadataFieldKey == null) { + return null; + } + String[] fields = metadataFieldKey.split("\\."); + String schema = fields[0]; + String element = fields[1]; + String qualifier = null; + if (fields.length == 3) { + qualifier = fields[2]; + } + return getFirstEntryByMetadatum(record, schema, element, qualifier); + } +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/AuthorNamesScorer.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/AuthorNamesScorer.java new file mode 100644 index 0000000000..60b1521f7e --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/AuthorNamesScorer.java @@ -0,0 +1,151 @@ +/** + * 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.suggestion.openaire; + +import static org.dspace.app.suggestion.SuggestionUtils.getAllEntriesByMetadatum; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.ibm.icu.text.CharsetDetector; +import com.ibm.icu.text.CharsetMatch; +import com.ibm.icu.text.Normalizer; +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.suggestion.SuggestionEvidence; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.service.ItemService; +import org.dspace.external.model.ExternalDataObject; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@see org.dspace.app.suggestion.oaire.EvidenceScorer} which evaluate ImportRecords + * based on Author's name. + * + * @author Andrea Bollini (andrea.bollini at 4science dot it) + * @author Pasquale Cavallo (pasquale.cavallo at 4science dot it) + * + */ +public class AuthorNamesScorer implements EvidenceScorer { + + private List contributorMetadata; + + private List names; + + @Autowired + private ItemService itemService; + + /** + * returns the metadata key of the Item which to base the filter on + * @return metadata key + */ + public List getContributorMetadata() { + return contributorMetadata; + } + + /** + * set the metadata key of the Item which to base the filter on + */ + public void setContributorMetadata(List contributorMetadata) { + this.contributorMetadata = contributorMetadata; + } + + /** + * return the metadata key of ImportRecord which to base the filter on + * @return + */ + public List getNames() { + return names; + } + + /** + * set the metadata key of ImportRecord which to base the filter on + */ + public void setNames(List names) { + this.names = names; + } + + /** + * Method which is responsible to evaluate ImportRecord based on authors name. + * This method extract the researcher name from Item using contributorMetadata fields + * and try to match them with values extract from ImportRecord using metadata keys defined + * in names. + * ImportRecords which don't match will be discarded. + * + * @param importRecord the import record to check + * @param researcher DSpace item + * @return the generated evidence or null if the record must be discarded + */ + @Override + public SuggestionEvidence computeEvidence(Item researcher, ExternalDataObject importRecord) { + List names = searchMetadataValues(researcher); + int maxNameLenght = names.stream().mapToInt(n -> n[0].length()).max().orElse(1); + List metadataAuthors = new ArrayList<>(); + for (String contributorMetadatum : contributorMetadata) { + metadataAuthors.addAll(getAllEntriesByMetadatum(importRecord, contributorMetadatum)); + } + List normalizedMetadataAuthors = metadataAuthors.stream().map(x -> normalize(x)) + .collect(Collectors.toList()); + int idx = 0; + for (String nMetadataAuthor : normalizedMetadataAuthors) { + Optional found = names.stream() + .filter(a -> StringUtils.equalsIgnoreCase(a[0], nMetadataAuthor)).findFirst(); + if (found.isPresent()) { + return new SuggestionEvidence(this.getClass().getSimpleName(), + 100 * ((double) nMetadataAuthor.length() / (double) maxNameLenght), + "The author " + metadataAuthors.get(idx) + " at position " + (idx + 1) + + " in the authors list matches the name " + found.get()[1] + + " in the researcher profile"); + } + idx++; + } + return null; + } + + /** + * Return list of Item metadata values starting from metadata keys defined in class level variable names. + * + * @param researcher DSpace item + * @return list of metadata values + */ + private List searchMetadataValues(Item researcher) { + List authors = new ArrayList(); + for (String name : names) { + List values = itemService.getMetadataByMetadataString(researcher, name); + if (values != null) { + for (MetadataValue v : values) { + authors.add(new String[] {normalize(v.getValue()), v.getValue()}); + } + } + } + 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(); + cd.setText(value.getBytes()); + CharsetMatch detect = cd.detect(); + if (detect != null && detect.getLanguage() != null) { + norm = norm.replaceAll("[^\\p{L}]", " ").toLowerCase(new Locale(detect.getLanguage())); + } else { + norm = norm.replaceAll("[^\\p{L}]", " ").toLowerCase(); + } + return Arrays.asList(norm.split("\\s+")).stream().sorted().collect(Collectors.joining()); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/DateScorer.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/DateScorer.java new file mode 100644 index 0000000000..94f81715fa --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/DateScorer.java @@ -0,0 +1,214 @@ +/** + * 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.suggestion.openaire; + +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; + +import org.dspace.app.suggestion.SuggestionEvidence; +import org.dspace.app.suggestion.SuggestionUtils; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.service.ItemService; +import org.dspace.external.model.ExternalDataObject; +import org.dspace.util.MultiFormatDateParser; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@see org.dspace.app.suggestion.oaire.EvidenceScorer} which evaluate ImportRecords + * based on the distance from a date extracted from the ResearcherProfile (birthday / graduation date) + * + * @author Andrea Bollini (andrea.bollini at 4science dot it) + * + */ +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; + + /** + * 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) { + this.itemService = itemService; + } + + public void setBirthDateMetadata(String birthDate) { + this.birthDateMetadata = birthDate; + } + + public String getBirthDateMetadata() { + return birthDateMetadata; + } + + public void setEducationDateMetadata(String educationDate) { + this.educationDateMetadata = educationDate; + } + + public String getEducationDateMetadata() { + return educationDateMetadata; + } + + public void setBirthDateDelta(int birthDateDelta) { + this.birthDateDelta = birthDateDelta; + } + + public void setBirthDateRange(int birthDateRange) { + this.birthDateRange = birthDateRange; + } + + public void setEducationDateDelta(int educationDateDelta) { + this.educationDateDelta = educationDateDelta; + } + + public void setEducationDateRange(int educationDateRange) { + this.educationDateRange = educationDateRange; + } + + public void setPublicationDateMetadata(String publicationDateMetadata) { + this.publicationDateMetadata = publicationDateMetadata; + } + + /** + * 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 + * @return the generated evidence or null if the record must be discarded + */ + @Override + public SuggestionEvidence computeEvidence(Item researcher, ExternalDataObject importRecord) { + Integer[] range = calculateRange(researcher); + if (range == null) { + return new SuggestionEvidence(this.getClass().getSimpleName(), + 0, + "No assumption was possible about the publication year range. " + + "Please consider setting your birthday in your profile."); + } else { + String optDate = SuggestionUtils.getFirstEntryByMetadatum(importRecord, publicationDateMetadata); + int year = getYear(optDate); + if (year > 0) { + if ((range[0] == null || year >= range[0]) && + (range[1] == null || year <= range[1])) { + return new SuggestionEvidence(this.getClass().getSimpleName(), + 10, + "The publication date is within the expected range [" + range[0] + ", " + + range[1] + "]"); + } else { + // outside the range, discard the suggestion + return null; + } + } else { + return new SuggestionEvidence(this.getClass().getSimpleName(), + 0, + "No assumption was possible as the publication date is " + (optDate != null + ? "unprocessable [" + optDate + "]" + : "unknown")); + } + } + } + + /** + * 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 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 { + return null; + } + } + + private List getListMetadataValues(Item researcher, String metadataKey) { + if (metadataKey != null) { + return itemService.getMetadataByMetadataString(researcher, metadataKey); + } else { + return Collections.EMPTY_LIST; + } + } + + private String getSingleValue(Item researcher, String metadataKey) { + if (metadataKey != null) { + return itemService.getMetadata(researcher, metadataKey); + } + return null; + } + + private int getYear(String birthDateStr) { + int birthDateYear = -1; + if (birthDateStr != null) { + Date birthDate = MultiFormatDateParser.parse(birthDateStr); + if (birthDate != null) { + Calendar calendar = new GregorianCalendar(); + calendar.setTime(birthDate); + birthDateYear = calendar.get(Calendar.YEAR); + } + } + return birthDateYear; + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/EvidenceScorer.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/EvidenceScorer.java new file mode 100644 index 0000000000..027e9902f9 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/EvidenceScorer.java @@ -0,0 +1,37 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.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.PublicationApproverServiceImpl} + * to construct filtering pipeline. + * + * For each EvidenceScorer, the service call computeEvidence method. + * + * @author Andrea Bollini (andrea.bollini at 4science dot it) + * @author Pasquale Cavallo (pasquale.cavallo at 4science dot it) + * + */ +public interface EvidenceScorer { + + /** + * Method to compute the suggestion evidence of an ImportRecord, a null evidence + * would lead the record to be discarded. + * + * @param researcher DSpace item + * @param importRecord the record to evaluate + * @return the generated suggestion evidence or null if the record should be + * discarded + */ + public SuggestionEvidence computeEvidence(Item researcher, ExternalDataObject importRecord); + +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoader.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoader.java new file mode 100644 index 0000000000..7ad723af12 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoader.java @@ -0,0 +1,256 @@ +/** + * 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.suggestion.openaire; + +import static org.dspace.app.suggestion.SuggestionUtils.getAllEntriesByMetadatum; +import static org.dspace.app.suggestion.SuggestionUtils.getFirstEntryByMetadatum; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.solr.client.solrj.SolrServerException; +import org.dspace.app.suggestion.SolrSuggestionProvider; +import org.dspace.app.suggestion.Suggestion; +import org.dspace.app.suggestion.SuggestionEvidence; +import org.dspace.content.Item; +import org.dspace.content.dto.MetadataValueDTO; +import org.dspace.core.Context; +import org.dspace.external.model.ExternalDataObject; +import org.dspace.external.provider.ExternalDataProvider; +import org.dspace.services.ConfigurationService; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Class responsible to load and manage ImportRecords from OpenAIRE + * + * @author Pasquale Cavallo (pasquale.cavallo at 4science dot it) + * + */ +public class PublicationLoader extends SolrSuggestionProvider { + + private List names; + + private ExternalDataProvider primaryProvider; + + private List otherProviders; + + @Autowired + private ConfigurationService configurationService; + + private List pipeline; + + public void setPrimaryProvider(ExternalDataProvider primaryProvider) { + this.primaryProvider = primaryProvider; + } + + public void setOtherProviders(List otherProviders) { + this.otherProviders = otherProviders; + } + + /** + * Set the pipeline of Approver + * @param pipeline list Approver + */ + public void setPipeline(List pipeline) { + this.pipeline = pipeline; + } + + /** + * This method filter a list of ImportRecords using a pipeline of AuthorNamesApprover + * and return a filtered list of ImportRecords. + * + * @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 + */ + public List reduceAndTransform(Item researcher, List importRecords) { + List results = new ArrayList<>(); + for (ExternalDataObject r : importRecords) { + boolean skip = false; + List evidences = new ArrayList(); + for (EvidenceScorer authorNameApprover : pipeline) { + SuggestionEvidence evidence = authorNameApprover.computeEvidence(researcher, r); + if (evidence != null) { + evidences.add(evidence); + } else { + skip = true; + break; + } + } + if (!skip) { + Suggestion suggestion = translateImportRecordToSuggestion(researcher, r); + suggestion.getEvidences().addAll(evidences); + results.add(suggestion); + } + } + return results; + } + + /** + * Save a List of ImportRecord into Solr. + * ImportRecord will be translate into a SolrDocument by the method translateImportRecordToSolrDocument. + * + * @param context the DSpace Context + * @param researcher a DSpace Item + * @throws SolrServerException + * @throws IOException + */ + public void importAuthorRecords(Context context, Item researcher) + throws SolrServerException, IOException { + int offset = 0; + int limit = 10; + int loaded = limit; + List searchValues = searchMetadataValues(researcher); + while (loaded > 0) { + List metadata = getImportRecords(searchValues, researcher, offset, limit); + if (metadata.isEmpty()) { + loaded = 0; + continue; + } + offset += limit; + loaded = metadata.size(); + List records = reduceAndTransform(researcher, metadata); + for (Suggestion record : records) { + solrSuggestionStorageService.addSuggestion(record, false, false); + } + } + solrSuggestionStorageService.commit(); + } + + /** + * Translate an ImportRecord into a Suggestion + * @param item DSpace item + * @param record ImportRecord + * @return Suggestion + */ + private Suggestion translateImportRecordToSuggestion(Item item, ExternalDataObject record) { + String openAireId = record.getId(); + Suggestion suggestion = new Suggestion(getSourceName(), item, openAireId); + suggestion.setDisplay(getFirstEntryByMetadatum(record, "dc", "title", null)); + suggestion.getMetadata().add( + new MetadataValueDTO("dc", "title", null, null, getFirstEntryByMetadatum(record, "dc", "title", null))); + suggestion.getMetadata().add(new MetadataValueDTO("dc", "date", "issued", null, + getFirstEntryByMetadatum(record, "dc", "date", "issued"))); + suggestion.getMetadata().add(new MetadataValueDTO("dc", "description", "abstract", null, + getFirstEntryByMetadatum(record, "dc", "description", "abstract"))); + suggestion.setExternalSourceUri(configurationService.getProperty("dspace.server.url") + + "/api/integration/externalsources/" + primaryProvider.getSourceIdentifier() + "/entryValues/" + + openAireId); + for (String o : getAllEntriesByMetadatum(record, "dc", "source", null)) { + suggestion.getMetadata().add(new MetadataValueDTO("dc", "source", null, null, o)); + } + for (String o : getAllEntriesByMetadatum(record, "dc", "contributor", "author")) { + suggestion.getMetadata().add(new MetadataValueDTO("dc", "contributor", "author", null, o)); + } + return suggestion; + } + + public List getNames() { + return names; + } + + public void setNames(List names) { + this.names = names; + } + + /** + * Load metadata from OpenAIRE using the import service. The service use the value + * 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 getImportRecords(List searchValues, + Item researcher, int offset, int limit) { + List matchingRecords = new ArrayList<>(); + for (String searchValue : searchValues) { + matchingRecords.addAll( + primaryProvider.searchExternalDataObjects(searchValue, offset, limit)); + } + List toReturn = removeDuplicates(matchingRecords); + return toReturn; + } + + /** + * This method remove duplicates from importRecords list. + * An element is a duplicate if in the list exist another element + * with the same value of the metadatum 'dc.identifier.other' + * + * @param importRecords list of ImportRecord + * @return list of ImportRecords without duplicates + */ + private List removeDuplicates(List importRecords) { + List filteredRecords = new ArrayList<>(); + for (ExternalDataObject currentRecord : importRecords) { + if (!isDuplicate(currentRecord, filteredRecords)) { + filteredRecords.add(currentRecord); + } + } + return filteredRecords; + } + + + /** + * Check if the ImportRecord is already present in the list. + * The comparison is made on the value of metadatum with key 'dc.identifier.other' + * + * @param dto An importRecord instance + * @param importRecords a list of importRecord + * @return true if dto is already present in importRecords, false otherwise + */ + private boolean isDuplicate(ExternalDataObject dto, List importRecords) { + String currentItemId = dto.getId(); + if (currentItemId == null) { + return true; + } + for (ExternalDataObject importRecord : importRecords) { + if (currentItemId.equals(importRecord.getId())) { + return true; + } + } + return false; + } + + + /** + * Return list of Item metadata values starting from metadata keys defined in class level variable names. + * + * @param researcher DSpace item + * @return list of metadata values + */ + private List searchMetadataValues(Item researcher) { + List authors = new ArrayList(); + for (String name : names) { + String value = itemService.getMetadata(researcher, name); + if (value != null) { + authors.add(value); + } + } + return authors; + } + + @Override + protected boolean isExternalDataObjectPotentiallySuggested(Context context, ExternalDataObject externalDataObject) { + if (StringUtils.equals(externalDataObject.getSource(), primaryProvider.getSourceIdentifier())) { + return true; + } else if (otherProviders != null) { + return otherProviders.stream() + .anyMatch(x -> StringUtils.equals(externalDataObject.getSource(), x.getSourceIdentifier())); + } else { + return false; + } + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderCliScriptConfiguration.java new file mode 100644 index 0000000000..f5289fd99a --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderCliScriptConfiguration.java @@ -0,0 +1,29 @@ +/** + * 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.suggestion.openaire; + +import org.apache.commons.cli.Options; + +/** + * Extension of {@link PublicationLoaderScriptConfiguration} for CLI. + * + * @author Alessandro Martelli (alessandro.martelli at 4science.it) + */ +public class PublicationLoaderCliScriptConfiguration + extends PublicationLoaderScriptConfiguration { + + @Override + public Options getOptions() { + Options options = super.getOptions(); + options.addOption("h", "help", false, "help"); + options.getOption("h").setType(boolean.class); + super.options = options; + return options; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderRunnable.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderRunnable.java new file mode 100644 index 0000000000..a408c448e9 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderRunnable.java @@ -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.app.suggestion.openaire; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.cli.ParseException; +import org.dspace.content.Item; +import org.dspace.core.Context; +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; + +/** + * Runner responsible to import metadata about authors from OpenAIRE to Solr. + * This runner works in two ways: + * If -s parameter with a valid UUID is received, then the specific researcher + * with this UUID will be used. + * Invocation without any parameter results in massive import, processing all + * authors registered in DSpace. + * + * @author Alessandro Martelli (alessandro.martelli at 4science.it) + */ +public class PublicationLoaderRunnable + extends DSpaceRunnable> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PublicationLoaderRunnable.class); + + private PublicationLoader oairePublicationLoader = null; + + protected Context context; + + protected String profile; + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public PublicationLoaderScriptConfiguration getScriptConfiguration() { + PublicationLoaderScriptConfiguration configuration = new DSpace().getServiceManager() + .getServiceByName("import-openaire-suggestions", PublicationLoaderScriptConfiguration.class); + return configuration; + } + + @Override + public void setup() throws ParseException { + + oairePublicationLoader = new DSpace().getServiceManager().getServiceByName( + "OpenairePublicationLoader", PublicationLoader.class); + + profile = commandLine.getOptionValue("s"); + if (profile == null) { + LOGGER.info("No argument for -s, process all profile"); + } else { + LOGGER.info("Process eperson item with UUID " + profile); + } + } + + @Override + public void internalRun() throws Exception { + + context = new Context(); + + Iterator researchers = getResearchers(profile); + while (researchers.hasNext()) { + Item researcher = researchers.next(); + oairePublicationLoader.importAuthorRecords(context, researcher); + } + + } + + /** + * Get the Item(s) which map a researcher from Solr. If the uuid is specified, + * the researcher with this UUID will be chosen. If the uuid doesn't match any + * researcher, the method returns an empty array list. If uuid is null, all + * research will be return. + * + * @param profileUUID uuid of the researcher. If null, all researcher will be + * returned. + * @return the researcher with specified UUID or all researchers + */ + @SuppressWarnings("rawtypes") + private Iterator getResearchers(String profileUUID) { + SearchService searchService = new DSpace().getSingletonService(SearchService.class); + DiscoverQueryBuilder queryBuilder = SearchUtils.getQueryBuilder(); + List filters = new ArrayList(); + String query = "*:*"; + if (profileUUID != null) { + query = "search.resourceid:" + profileUUID.toString(); + } + 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); + } + return null; + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderRunnableCli.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderRunnableCli.java new file mode 100644 index 0000000000..6c59b725d5 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderRunnableCli.java @@ -0,0 +1,36 @@ +/** + * 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.suggestion.openaire; + +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.ParseException; +import org.dspace.utils.DSpace; + +public class PublicationLoaderRunnableCli extends PublicationLoaderRunnable { + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public PublicationLoaderCliScriptConfiguration getScriptConfiguration() { + PublicationLoaderCliScriptConfiguration configuration = new DSpace().getServiceManager() + .getServiceByName("import-openaire-suggestions", PublicationLoaderCliScriptConfiguration.class); + return configuration; + } + + @Override + public void setup() throws ParseException { + super.setup(); + + // in case of CLI we show the help prompt + if (commandLine.hasOption('h')) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("Import Researchers Suggestions", getScriptConfiguration().getOptions()); + System.exit(0); + } + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderScriptConfiguration.java new file mode 100644 index 0000000000..8bef7de40d --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/suggestion/openaire/PublicationLoaderScriptConfiguration.java @@ -0,0 +1,56 @@ +/** + * 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.suggestion.openaire; + +import org.apache.commons.cli.Options; +import org.dspace.scripts.configuration.ScriptConfiguration; + +public class PublicationLoaderScriptConfiguration + extends ScriptConfiguration { + + private Class dspaceRunnableClass; + + @Override + public Class getDspaceRunnableClass() { + return dspaceRunnableClass; + } + + /** + * Generic setter for the dspaceRunnableClass + * @param dspaceRunnableClass The dspaceRunnableClass to be set on this PublicationLoaderScriptConfiguration + */ + @Override + public void setDspaceRunnableClass(Class dspaceRunnableClass) { + this.dspaceRunnableClass = dspaceRunnableClass; + } + + /* + @Override + public boolean isAllowedToExecute(Context context) { + try { + return authorizeService.isAdmin(context); + } catch (SQLException e) { + throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e); + } + } + */ + + @Override + public Options getOptions() { + if (options == null) { + Options options = new Options(); + + options.addOption("s", "single-researcher", true, "Single researcher UUID"); + options.getOption("s").setType(String.class); + + super.options = options; + } + return options; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index efd813d29b..b498b69395 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -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 * diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 38692c73a6..8dc8239ca5 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -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 getInputsByCollectionHandle(String collectionHandle) + public List 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 inputSets = getInputsByCollectionHandle(collection.getHandle()); + List inputSets = getInputsByCollection(collection); for (DCInputSet inputSet : inputSets) { String[] tokenized = Utils.tokenize(field); String schema = tokenized[0]; diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 0f144fd69f..1c91b40b97 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -11,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 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 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(); - submitDefns = new HashMap>>(); + communityToSubmissionConfig = new HashMap(); + submitDefns = new LinkedHashMap>>(); 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 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 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 collections = collectionService.findAllCollectionsByEntityType( context, diff --git a/dspace-api/src/main/java/org/dspace/app/util/Util.java b/dspace-api/src/main/java/org/dspace/app/util/Util.java index f8ef3b1731..3bc828d6c4 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/Util.java +++ b/dspace-api/src/main/java/org/dspace/app/util/Util.java @@ -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 inputSets = inputsReader.getInputsByCollectionHandle(col_handle); + List 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 differenceInSubmissionFields(Collection fromCollection, Collection toCollection) throws DCInputsReaderException { DCInputsReader reader = new DCInputsReader(); - List from = reader.getInputsByCollectionHandle(fromCollection.getHandle()); - List to = reader.getInputsByCollectionHandle(toCollection.getHandle()); + List from = reader.getInputsByCollection(fromCollection); + List to = reader.getInputsByCollection(toCollection); Set fromFieldName = new HashSet<>(); Set toFieldName = new HashSet<>(); diff --git a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java index 1aadbea162..89e42c182b 100644 --- a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java @@ -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. diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java index a0847e4be2..9791f69abb 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -1435,16 +1435,6 @@ prevent the generation of resource policy entry values with null dspace_object a } } - @Override - public Iterator findByMetadataQuery(Context context, List> listFieldList, - List query_op, List query_val, List collectionUuids, - String regexClause, int offset, int limit) - throws SQLException, AuthorizeException, IOException { - return itemDAO - .findByMetadataQuery(context, listFieldList, query_op, query_val, collectionUuids, regexClause, offset, - limit); - } - @Override public DSpaceObject getAdminObject(Context context, Item item, int action) throws SQLException { DSpaceObject adminObject = null; diff --git a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java index 34ba9e8c45..f4d1f02710 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java @@ -242,7 +242,7 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService // check if it is the requested collection Map 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 diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java index b1d8cf36a5..902bded33e 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/DCInputAuthority.java @@ -156,7 +156,8 @@ public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority int found = 0; List v = new ArrayList(); 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)) { diff --git a/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java index 86da51e6cc..49d3527a35 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/ItemDAO.java @@ -11,7 +11,6 @@ import java.sql.SQLException; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.UUID; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -80,10 +79,6 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO { public Iterator findByMetadataField(Context context, MetadataField metadataField, String value, boolean inArchive) throws SQLException; - public Iterator findByMetadataQuery(Context context, List> listFieldList, - List query_op, List query_val, List collectionUuids, - String regexClause, int offset, int limit) throws SQLException; - public Iterator findByAuthorityValue(Context context, MetadataField metadataField, String authority, boolean inArchive) throws SQLException; diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java index 6730e6ab4a..965ab569eb 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.UUID; import javax.persistence.Query; import javax.persistence.TemporalType; import javax.persistence.criteria.CriteriaBuilder; @@ -24,21 +23,11 @@ import org.dspace.content.Collection; import org.dspace.content.Item; import org.dspace.content.Item_; import org.dspace.content.MetadataField; -import org.dspace.content.MetadataValue; import org.dspace.content.dao.ItemDAO; import org.dspace.core.AbstractHibernateDSODAO; import org.dspace.core.Context; import org.dspace.core.UUIDIterator; 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; /** * Hibernate implementation of the Database Access Object interface class for the Item object. @@ -193,120 +182,6 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO implements ItemDA return new UUIDIterator(context, uuids, Item.class, this); } - 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 - @Deprecated - public Iterator findByMetadataQuery(Context context, List> listFieldList, - List query_op, List query_val, List collectionUuids, - String regexClause, int offset, int limit) throws SQLException { - - Criteria criteria = getHibernateSession(context).createCriteria(Item.class, "item"); - criteria.setFirstResult(offset); - criteria.setMaxResults(limit); - - 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)); - } - - 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)); - if (op == null) { - log.warn("Skipping Invalid Operator: " + query_op.get(i)); - continue; - } - - if (op == OP.matches || op == OP.doesnt_match) { - if (regexClause.isEmpty()) { - log.warn("Skipping Unsupported Regex Operator: " + query_op.get(i)); - continue; - } - } - - DetachedCriteria subcriteria = DetachedCriteria.forClass(MetadataValue.class, "mv"); - subcriteria.add(Property.forName("mv.dSpaceObject").eqProperty("item.id")); - subcriteria.setProjection(Projections.property("mv.dSpaceObject")); - - if (!listFieldList.get(i).isEmpty()) { - subcriteria.add(Restrictions.in("metadataField", listFieldList.get(i))); - } - - subcriteria.add(op.buildPredicate(query_val.get(i), regexClause)); - - if (op == OP.exists || op == OP.equals || op == OP.like || op == OP.contains || op == OP.matches) { - criteria.add(Subqueries.exists(subcriteria)); - } else { - criteria.add(Subqueries.notExists(subcriteria)); - } - } - criteria.addOrder(Order.asc("item.id")); - - log.debug(String.format("Running custom query with %d filters", index)); - - return ((List) criteria.list()).iterator(); - - } - @Override public Iterator findByAuthorityValue(Context context, MetadataField metadataField, String authority, boolean inArchive) throws SQLException { diff --git a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java index de7644af83..43a804cde2 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java @@ -23,7 +23,6 @@ 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; @@ -749,11 +748,6 @@ public interface ItemService String schema, String element, String qualifier, String value) throws SQLException, AuthorizeException, IOException; - public Iterator findByMetadataQuery(Context context, List> listFieldList, - List query_op, List query_val, List collectionUuids, - String regexClause, int offset, int limit) - throws SQLException, AuthorizeException, IOException; - /** * Find all the items in the archive with a given authority key value * in the indicated metadata field. diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java b/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java index 07bfed5fe5..2899e3f6bd 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/RequiredMetadata.java @@ -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 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 getReqList(String handle) throws DCInputsReaderException { - List reqList = reqMap.get(handle); + protected List getReqList(Collection collection) throws DCInputsReaderException { + List reqList = reqMap.get(collection.getHandle()); if (reqList == null) { reqList = reqMap.get("default"); } if (reqList == null) { reqList = new ArrayList(); - List inputSet = reader.getInputsByCollectionHandle(handle); + List inputSet = reader.getInputsByCollection(collection); for (DCInputSet inputs : inputSet) { for (DCInput[] row : inputs.getFields()) { for (DCInput input : row) { diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java index 661c48d91c..b70e9162f7 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java @@ -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 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 indexableObjectTypes = IndexObjectFactoryFactory.getInstance().getIndexFactories().stream() + .map((indexFactory -> indexFactory.getType())).collect(Collectors.toList()); options .addOption("r", "remove", true, "remove an Item, Collection or Community from index based on its handle"); options.addOption("i", "index", true, "add or update an Item, Collection or Community based on its handle or uuid"); + options.addOption(TYPE_OPTION, "type", true, "reindex only specific type of " + + "(re)indexable objects; options: " + Arrays.toString(indexableObjectTypes.toArray())); options.addOption("c", "clean", false, "clean existing index removing any documents that no longer exist in the db"); options.addOption("d", "delete", false, diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index 55c99b168e..f1ae137b91 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -64,7 +64,14 @@ public abstract class IndexFactoryImpl implements //Do any additional indexing, depends on the plugins for (SolrServiceIndexPlugin solrServiceIndexPlugin : ListUtils.emptyIfNull(solrServiceIndexPlugins)) { - solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc); + try { + solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc); + } catch (Exception e) { + log.error("An error occurred while indexing additional fields. " + + "Could not fully index item with UUID: {}. Plugin: {}", + indexableObject.getUniqueIndexID(), solrServiceIndexPlugin.getClass().getSimpleName()); + + } } return doc; @@ -82,7 +89,7 @@ public abstract class IndexFactoryImpl implements writeDocument(solrInputDocument, null); } catch (Exception e) { log.error("Error occurred while writing SOLR document for {} object {}", - indexableObject.getType(), indexableObject.getID(), e); + indexableObject.getType(), indexableObject.getID(), e); } } @@ -101,8 +108,8 @@ public abstract class IndexFactoryImpl implements if (streams != null && !streams.isEmpty()) { // limit full text indexing to first 100,000 characters unless configured otherwise final int charLimit = DSpaceServicesFactory.getInstance().getConfigurationService() - .getIntProperty("discovery.solr.fulltext.charLimit", - 100000); + .getIntProperty("discovery.solr.fulltext.charLimit", + 100000); // Use Tika's Text parser as the streams are always from the TEXT bundle (i.e. already extracted text) TextAndCSVParser tikaParser = new TextAndCSVParser(); @@ -113,6 +120,18 @@ public abstract class IndexFactoryImpl implements // Use Apache Tika to parse the full text stream(s) try (InputStream fullTextStreams = streams.getStream()) { tikaParser.parse(fullTextStreams, tikaHandler, tikaMetadata, tikaContext); + + // Write Tika metadata to "tika_meta_*" fields. + // This metadata is not very useful right now, + // but we'll keep it just in case it becomes more useful. + for (String name : tikaMetadata.names()) { + for (String value : tikaMetadata.getValues(name)) { + doc.addField("tika_meta_" + name, value); + } + } + + // Save (parsed) full text to "fulltext" field + doc.addField("fulltext", tikaHandler.toString()); } catch (SAXException saxe) { // Check if this SAXException is just a notice that this file was longer than the character limit. // Unfortunately there is not a unique, public exception type to catch here. This error is thrown @@ -121,30 +140,23 @@ public abstract class IndexFactoryImpl implements if (saxe.getMessage().contains("limit has been reached")) { // log that we only indexed up to that configured limit log.info("Full text is larger than the configured limit (discovery.solr.fulltext.charLimit)." - + " Only the first {} characters were indexed.", charLimit); + + " Only the first {} characters were indexed.", charLimit); } else { log.error("Tika parsing error. Could not index full text.", saxe); throw new IOException("Tika parsing error. Could not index full text.", saxe); } - } catch (TikaException ex) { + } catch (TikaException | IOException ex) { log.error("Tika parsing error. Could not index full text.", ex); throw new IOException("Tika parsing error. Could not index full text.", ex); + } finally { + // Add document to index + solr.add(doc); } - - // Write Tika metadata to "tika_meta_*" fields. - // This metadata is not very useful right now, but we'll keep it just in case it becomes more useful. - for (String name : tikaMetadata.names()) { - for (String value : tikaMetadata.getValues(name)) { - doc.addField("tika_meta_" + name, value); - } - } - - // Save (parsed) full text to "fulltext" field - doc.addField("fulltext", tikaHandler.toString()); + return; } - // Add document to index solr.add(doc); + } } diff --git a/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java b/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java index f91ea00cac..59cbe4f9d0 100644 --- a/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java @@ -13,6 +13,8 @@ import java.util.Optional; import java.util.stream.Collectors; import org.apache.logging.log4j.Logger; +import org.dspace.app.suggestion.SuggestionProvider; +import org.dspace.app.suggestion.SuggestionService; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Item; @@ -44,6 +46,9 @@ public class ExternalDataServiceImpl implements ExternalDataService { @Autowired private WorkspaceItemService workspaceItemService; + @Autowired + private SuggestionService suggestionService; + @Override public Optional getExternalDataObject(String source, String id) { ExternalDataProvider provider = getExternalDataProvider(source); @@ -105,6 +110,16 @@ public class ExternalDataServiceImpl implements ExternalDataService { log.info(LogHelper.getHeader(context, "create_item_from_externalDataObject", "Created item" + "with id: " + item.getID() + " from source: " + externalDataObject.getSource() + " with identifier: " + externalDataObject.getId())); + try { + List providers = suggestionService.getSuggestionProviders(); + if (providers != null) { + for (SuggestionProvider p : providers) { + p.flagRelatedSuggestionsAsProcessed(context, externalDataObject); + } + } + } catch (Exception e) { + log.error("Got problems with the solr suggestion storage service: " + e.getMessage(), e); + } return workspaceItem; } diff --git a/dspace-api/src/main/java/org/dspace/importer/external/datamodel/ImportRecord.java b/dspace-api/src/main/java/org/dspace/importer/external/datamodel/ImportRecord.java index 3fc34dc511..cbd4bc1245 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/datamodel/ImportRecord.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/datamodel/ImportRecord.java @@ -11,7 +11,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; +import org.dspace.content.MetadataFieldName; import org.dspace.importer.external.metadatamapping.MetadatumDTO; /** @@ -94,6 +96,31 @@ public class ImportRecord { return values; } + /** + * Returns an {@code Optional} representing the value + * of the metadata {@code field} found inside the {@code valueList}. + * @param field String of the MetadataField to search + * @return {@code Optional} non empty if found. + */ + public Optional getSingleValue(String field) { + MetadataFieldName metadataFieldName = new MetadataFieldName(field); + return getSingleValue(metadataFieldName.schema, metadataFieldName.element, metadataFieldName.qualifier); + } + + /** + * Retrieves a single value for the given schema, element, and qualifier. + * + * @param schema the schema for the value + * @param element the element for the value + * @param qualifier the qualifier for the value + * @return an optional containing the single value, if present + */ + public Optional getSingleValue(String schema, String element, String qualifier) { + return getValue(schema, element, qualifier).stream() + .map(MetadatumDTO::getValue) + .findFirst(); + } + /** * Add a value to the valueList * diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/RorParentOrgUnitMetadataContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/RorParentOrgUnitMetadataContributor.java new file mode 100644 index 0000000000..52c6422554 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/RorParentOrgUnitMetadataContributor.java @@ -0,0 +1,130 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.metadatamapping.contributor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.dspace.importer.external.metadatamapping.MetadatumDTO; + +/** + * A ROR JsonPath Metadata processor that should be configured inside the {@code ror-integration.xml} file. + * This allows the extraction of a given contributor with a specific mappings from the ROR JSON response. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + */ +public class RorParentOrgUnitMetadataContributor extends SimpleJsonPathMetadataContributor { + + /** + * Determines which field of the JSON detains the {@code type} of this + * specific node (that needs to be mapped). + * + */ + private String typeField; + + /** + * Determines which is the type of the main parent node that needs to be mapped. + * It should match the value of the {@code typeField} of the JSON node. + * + */ + private String parentType; + + /** + * Determines which is the field of the JSON that contains the value + * that needs to be mapped into a {@code MetadatumDTO}. + */ + private String labelField; + + /** + * Creates a {@code MetadatumDTO} for each correctly mapped JSON node + * of the ROR response. + * Partial / Unmatched parent-type metadatum will be ignored from this mapping. + * + * @param fullJson ROR response + * @return a collection of read ROR metadata. + */ + @Override + public Collection contributeMetadata(String fullJson) { + + Collection metadata = new ArrayList<>(); + Collection metadataValue = new ArrayList<>(); + + JsonNode jsonNode = convertStringJsonToJsonNode(fullJson); + JsonNode array = jsonNode.at(getQuery()); + if (!array.isArray()) { + return metadata; + } + + Iterator nodes = array.iterator(); + while (nodes.hasNext()) { + JsonNode node = nodes.next(); + + if (!node.has(labelField)) { + continue; + } + + String type = node.has(typeField) ? node.get(typeField).asText() : null; + String label = node.get(labelField).asText(); + + if (parentType.equalsIgnoreCase(type)) { + metadataValue.add(label); + } + + } + + for (String value : metadataValue) { + MetadatumDTO metadatumDto = new MetadatumDTO(); + metadatumDto.setValue(value); + metadatumDto.setElement(getField().getElement()); + metadatumDto.setQualifier(getField().getQualifier()); + metadatumDto.setSchema(getField().getSchema()); + metadata.add(metadatumDto); + } + return metadata; + } + + private JsonNode convertStringJsonToJsonNode(String json) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode body = null; + try { + body = mapper.readTree(json); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + return body; + } + + public String getTypeField() { + return typeField; + } + + public void setTypeField(String typeField) { + this.typeField = typeField; + } + + public String getLabelField() { + return labelField; + } + + public void setLabelField(String labelField) { + this.labelField = labelField; + } + + public String getParentType() { + return parentType; + } + + public void setParentType(String parentType) { + this.parentType = parentType; + } + +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathDateFormatMetadataContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathDateFormatMetadataContributor.java index fb15cd60ab..f3a186e450 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathDateFormatMetadataContributor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathDateFormatMetadataContributor.java @@ -87,5 +87,4 @@ public class SimpleXpathDateFormatMetadataContributor extends SimpleXpathMetadat dcValue.setSchema(field.getSchema()); return dcValue; } - -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/openaire/metadatamapping/OpenAIREPublicationFieldMapping.java b/dspace-api/src/main/java/org/dspace/importer/external/openaire/metadatamapping/OpenAIREPublicationFieldMapping.java new file mode 100644 index 0000000000..d58ffc8ca9 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/openaire/metadatamapping/OpenAIREPublicationFieldMapping.java @@ -0,0 +1,29 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.openaire.metadatamapping; + +import java.util.Map; +import javax.annotation.Resource; + +import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping; + +/** + * An implementation of {@link AbstractMetadataFieldMapping} responsible for + * defining the mapping of the OpenAIRE metadatum fields on the DSpace metadatum + * fields + * + * @author Mykhaylo Boychuk (4science.it) + */ +public class OpenAIREPublicationFieldMapping extends AbstractMetadataFieldMapping { + + @Override + @Resource(name = "openairePublicationsMetadataFieldMap") + public void setMetadataFieldMap(Map metadataFieldMap) { + super.setMetadataFieldMap(metadataFieldMap); + } +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java new file mode 100644 index 0000000000..0e59c9eb0e --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java @@ -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.importer.external.openaire.service; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import javax.el.MethodNotFoundException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import org.dspace.content.Item; +import org.dspace.importer.external.datamodel.ImportRecord; +import org.dspace.importer.external.datamodel.Query; +import org.dspace.importer.external.exception.MetadataSourceException; +import org.dspace.importer.external.metadatamapping.MetadatumDTO; +import org.dspace.importer.external.service.AbstractImportMetadataSourceService; +import org.dspace.importer.external.service.components.QuerySource; +import org.dspace.services.ConfigurationService; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.filter.Filters; +import org.jdom2.input.SAXBuilder; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implements a data source for querying OpenAIRE + * + * @author Pasquale Cavallo (pasquale.cavallo at 4science dot it) + */ +public class OpenAireImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService + implements QuerySource { + + @Autowired(required = true) + protected ConfigurationService configurationService; + + private String baseAddress; + + private WebTarget webTarget; + + private String queryParam; + + @Override + public String getImportSource() { + return "openaire"; + } + + /** + * The string that identifies this import implementation. Preferable a URI + * + * @return the identifying uri + */ + @Override + public ImportRecord getRecord(String id) throws MetadataSourceException { + return retry(new SearchByIdCallable(id)); + } + + /** + * The string that identifies this import implementation. Preferable a URI + * + * @return the identifying uri + */ + @Override + public ImportRecord getRecord(Query query) throws MetadataSourceException { + return retry(new SearchByIdCallable(query)); + } + + + /** + * Find the number of records matching a query; + * + * @param query a query string to base the search on. + * @return the sum of the matching records over this import source + * @throws MetadataSourceException if the underlying methods throw any exception. + */ + @Override + public int getRecordsCount(String query) throws MetadataSourceException { + return retry(new CountByQueryCallable(query)); + } + + /** + * Find the number of records matching a query; + * + * @param query a query object to base the search on. + * @return the sum of the matching records over this import source + * @throws MetadataSourceException if the underlying methods throw any exception. + */ + @Override + public int getRecordsCount(Query query) throws MetadataSourceException { + return retry(new CountByQueryCallable(query)); + } + + /** + * Find the number of records matching a string query. Supports pagination + * + * @param query a query string to base the search on. + * @param start offset to start at + * @param count number of records to retrieve. + * @return a set of records. Fully transformed. + * @throws MetadataSourceException if the underlying methods throw any exception. + */ + @Override + public Collection getRecords(String query, int start, int count) throws MetadataSourceException { + return retry(new SearchByQueryCallable(query, start, count)); + } + + + /** + * Find records based on a object query. + * + * @param query a query object to base the search on. + * @return a set of records. Fully transformed. + * @throws MetadataSourceException if the underlying methods throw any exception. + */ + @Override + public Collection getRecords(Query query) throws MetadataSourceException { + return retry(new SearchByQueryCallable(query)); + } + + @Override + public Collection findMatchingRecords(Query query) throws MetadataSourceException { + throw new MethodNotFoundException("This method is not implemented for OpenAIRE"); + } + + @Override + public Collection findMatchingRecords(Item item) throws MetadataSourceException { + throw new MethodNotFoundException("This method is not implemented for OpenAIRE"); + } + + /** + * Set the baseAddress to this object + * + * @param baseAddress The String object that represents the baseAddress of this object + */ + public void setBaseAddress(String baseAddress) { + this.baseAddress = baseAddress; + } + + /** + * Return the baseAddress set to this object + * + * @return The String object that represents the baseAddress of this object + */ + public String getBaseAddress() { + return baseAddress; + } + + /** + * Set the name of the query param, this correspond to the index used (title, author) + * + * @param queryParam on which index make the query + */ + public void setQueryParam(String queryParam) { + this.queryParam = queryParam; + } + + /** + * Get the name of the query param for the rest call + * + * @return the name of the query param, i.e. the index (title, author) to use + */ + public String getQueryParam() { + return queryParam; + } + /** + * Initialize the class + * + * @throws Exception on generic exception + */ + @Override + public void init() throws Exception { + Client client = ClientBuilder.newClient(); + if (baseAddress == null) { + baseAddress = configurationService.getProperty("openaire.base.url"); + } + if (queryParam == null) { + queryParam = "title"; + } + webTarget = client.target(baseAddress); + } + + public class SearchByIdCallable implements Callable { + + String id = null; + + public SearchByIdCallable(String id) { + this.id = id; + } + + public SearchByIdCallable(Query query) { + this.id = query.getParameterAsClass("id", String.class); + } + + @Override + public ImportRecord call() throws Exception { + List results = new ArrayList(); + WebTarget localTarget = webTarget.queryParam("openairePublicationID", id); + Invocation.Builder invocationBuilder = localTarget.request(); + Response response = invocationBuilder.get(); + if (response.getStatus() == 200) { + String responseString = response.readEntity(String.class); + List omElements = splitToRecords(responseString); + if (omElements != null) { + for (Element record : omElements) { + results.add(filterMultipleTitles(transformSourceRecords(record))); + } + } + return results != null ? results.get(0) : null; + } else { + return null; + } + } + } + + public class CountByQueryCallable implements Callable { + + String q; + + public CountByQueryCallable(String query) { + q = query; + } + + public CountByQueryCallable(Query query) { + q = query.getParameterAsClass("query", String.class); + } + + @Override + public Integer call() throws Exception { + WebTarget localTarget = webTarget.queryParam(queryParam, q); + Invocation.Builder invocationBuilder = localTarget.request(); + Response response = invocationBuilder.get(); + if (response.getStatus() == 200) { + String responseString = response.readEntity(String.class); + + SAXBuilder saxBuilder = new SAXBuilder(); + Document document = saxBuilder.build(new StringReader(responseString)); + Element root = document.getRootElement(); + + XPathExpression xpath = XPathFactory.instance().compile("/header/total", + Filters.element(), null); + + Element totalItem = (Element) xpath.evaluateFirst(root); + return totalItem != null ? Integer.parseInt(totalItem.getText()) : null; + + } else { + return 0; + } + } + } + + public class SearchByQueryCallable implements Callable> { + + String q; + int page; + int count; + + public SearchByQueryCallable(String query, int start, int count) { + this.q = query; + this.page = start / count; + this.count = count; + } + + public SearchByQueryCallable(Query query) { + this.q = query.getParameterAsClass("query", String.class); + this.page = query.getParameterAsClass("start", Integer.class) / + query.getParameterAsClass("count", Integer.class); + this.count = query.getParameterAsClass("count", Integer.class); + } + + @Override + public List call() throws Exception { + WebTarget localTarget = webTarget.queryParam(queryParam, q); + localTarget = localTarget.queryParam("page", page + 1); + localTarget = localTarget.queryParam("size", count); + List results = new ArrayList(); + Invocation.Builder invocationBuilder = localTarget.request(); + Response response = invocationBuilder.get(); + if (response.getStatus() == 200) { + String responseString = response.readEntity(String.class); + List omElements = splitToRecords(responseString); + if (omElements != null) { + for (Element record : omElements) { + results.add(filterMultipleTitles(transformSourceRecords(record))); + } + } + } + return results; + } + } + + /** + * This method remove multiple titles occurrences + * + * @param transformSourceRecords + * @return ImportRecord with one or zero title + */ + private ImportRecord filterMultipleTitles(ImportRecord transformSourceRecords) { + List metadata = (List)transformSourceRecords.getValueList(); + ArrayList nextSourceRecord = new ArrayList<>(); + boolean found = false; + for (MetadatumDTO dto : metadata) { + if ("dc".equals(dto.getSchema()) && "title".equals(dto.getElement()) && dto.getQualifier() == null) { + if (!found) { + nextSourceRecord.add(dto); + found = true; + } + } else { + nextSourceRecord.add(dto); + } + } + return new ImportRecord(nextSourceRecord); + } + + private List splitToRecords(String recordsSrc) { + + try { + SAXBuilder saxBuilder = new SAXBuilder(); + Document document = saxBuilder.build(new StringReader(recordsSrc)); + Element root = document.getRootElement(); + + List namespaces = Arrays.asList( + Namespace.getNamespace("dri", "http://www.driver-repository.eu/namespace/dri"), + Namespace.getNamespace("oaf", "http://namespace.openaire.eu/oaf"), + Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")); + XPathExpression xpath = XPathFactory.instance().compile("//results/result", + Filters.element(), null, namespaces); + + List recordsList = xpath.evaluate(root); + return recordsList; + } catch (JDOMException | IOException e) { + return null; + } + } + + + +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java new file mode 100644 index 0000000000..d7caeffdba --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java @@ -0,0 +1,38 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.ror.service; + +import java.util.Map; +import javax.annotation.Resource; + +import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping; + + +/** + * An implementation of {@link AbstractMetadataFieldMapping} + * Responsible for defining the mapping of the ROR metadatum fields on the DSpace metadatum fields + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + */ +public class RorFieldMapping extends AbstractMetadataFieldMapping { + + /** + * Defines which metadatum is mapped on which metadatum. Note that while the key must be unique it + * only matters here for postprocessing of the value. The mapped MetadatumContributor has full control over + * what metadatafield is generated. + * + * @param metadataFieldMap The map containing the link between retrieve metadata and metadata that will be set to + * the item. + */ + @Override + @Resource(name = "rorMetadataFieldMap") + public void setMetadataFieldMap(Map metadataFieldMap) { + super.setMetadataFieldMap(metadataFieldMap); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java new file mode 100644 index 0000000000..59063271f3 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java @@ -0,0 +1,278 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.ror.service; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import javax.el.MethodNotFoundException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.utils.URIBuilder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.content.Item; +import org.dspace.importer.external.datamodel.ImportRecord; +import org.dspace.importer.external.datamodel.Query; +import org.dspace.importer.external.exception.MetadataSourceException; +import org.dspace.importer.external.liveimportclient.service.LiveImportClient; +import org.dspace.importer.external.service.AbstractImportMetadataSourceService; +import org.dspace.importer.external.service.components.QuerySource; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implements a {@code AbstractImportMetadataSourceService} for querying ROR services. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + */ +public class RorImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService + implements QuerySource { + + private final static Logger log = LogManager.getLogger(); + protected static final String ROR_IDENTIFIER_PREFIX = "https://ror.org/"; + + private String url; + + private int timeout = 1000; + + @Autowired + private LiveImportClient liveImportClient; + + @Override + public String getImportSource() { + return "ror"; + } + + @Override + public ImportRecord getRecord(String id) throws MetadataSourceException { + List records = retry(new SearchByIdCallable(id)); + return CollectionUtils.isEmpty(records) ? null : records.get(0); + } + + @Override + public int getRecordsCount(String query) throws MetadataSourceException { + return retry(new CountByQueryCallable(query)); + } + + @Override + public int getRecordsCount(Query query) throws MetadataSourceException { + return retry(new CountByQueryCallable(query)); + } + + @Override + public Collection getRecords(String query, int start, int count) throws MetadataSourceException { + return retry(new SearchByQueryCallable(query)); + } + + @Override + public Collection getRecords(Query query) throws MetadataSourceException { + return retry(new SearchByQueryCallable(query)); + } + + @Override + public ImportRecord getRecord(Query query) throws MetadataSourceException { + List records = retry(new SearchByIdCallable(query)); + return CollectionUtils.isEmpty(records) ? null : records.get(0); + } + + @Override + public Collection findMatchingRecords(Query query) throws MetadataSourceException { + throw new MethodNotFoundException("This method is not implemented for ROR"); + } + + @Override + public Collection findMatchingRecords(Item item) throws MetadataSourceException { + throw new MethodNotFoundException("This method is not implemented for ROR"); + } + + @Override + public void init() throws Exception { + } + + /** + * This class is a Callable implementation to get ROR entries based on query + * object. This Callable use as query value the string queryString passed to + * constructor. If the object will be construct through Query.class instance, a + * Query's map entry with key "query" will be used. Pagination is supported too, + * using the value of the Query's map with keys "start" and "count". + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com) + */ + private class SearchByQueryCallable implements Callable> { + + private Query query; + + private SearchByQueryCallable(String queryString) { + query = new Query(); + query.addParameter("query", queryString); + } + + private SearchByQueryCallable(Query query) { + this.query = query; + } + + @Override + public List call() throws Exception { + return search(query.getParameterAsClass("query", String.class)); + } + } + + /** + * This class is a Callable implementation to get an ROR entry using bibcode The + * bibcode to use can be passed through the constructor as a String or as + * Query's map entry, with the key "id". + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com) + */ + private class SearchByIdCallable implements Callable> { + private Query query; + + private SearchByIdCallable(Query query) { + this.query = query; + } + + private SearchByIdCallable(String id) { + this.query = new Query(); + query.addParameter("id", id); + } + + @Override + public List call() throws Exception { + return searchById(query.getParameterAsClass("id", String.class)); + } + } + + /** + * This class is a Callable implementation to count the number of entries for a + * ROR query. This Callable uses as query value to ROR the string queryString + * passed to constructor. If the object will be construct through {@code Query} + * instance, the value of the Query's map with the key "query" will be used. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + */ + private class CountByQueryCallable implements Callable { + private Query query; + + private CountByQueryCallable(String queryString) { + query = new Query(); + query.addParameter("query", queryString); + } + + private CountByQueryCallable(Query query) { + this.query = query; + } + + @Override + public Integer call() throws Exception { + return count(query.getParameterAsClass("query", String.class)); + } + } + + /** + * Counts the number of results for the given query. + * + * @param query the query string to count results for + * @return the number of results for the given query + */ + public Integer count(String query) { + try { + Map> params = new HashMap>(); + + URIBuilder uriBuilder = new URIBuilder(this.url); + uriBuilder.addParameter("query", query); + + String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params); + if (StringUtils.isEmpty(resp)) { + return 0; + } + JsonNode jsonNode = convertStringJsonToJsonNode(resp); + return jsonNode.at("/number_of_results").asInt(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return 0; + } + + private List searchById(String id) { + + List importResults = new ArrayList<>(); + + id = StringUtils.removeStart(id, ROR_IDENTIFIER_PREFIX); + + try { + Map> params = new HashMap>(); + + URIBuilder uriBuilder = new URIBuilder(this.url + "/" + id); + + String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params); + if (StringUtils.isEmpty(resp)) { + return importResults; + } + + JsonNode jsonNode = convertStringJsonToJsonNode(resp); + importResults.add(transformSourceRecords(jsonNode.toString())); + + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return importResults; + } + + private List search(String query) { + List importResults = new ArrayList<>(); + try { + Map> params = new HashMap>(); + + URIBuilder uriBuilder = new URIBuilder(this.url); + uriBuilder.addParameter("query", query); + + String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params); + if (StringUtils.isEmpty(resp)) { + return importResults; + } + + JsonNode jsonNode = convertStringJsonToJsonNode(resp); + JsonNode docs = jsonNode.at("/items"); + if (docs.isArray()) { + Iterator nodes = docs.elements(); + while (nodes.hasNext()) { + JsonNode node = nodes.next(); + importResults.add(transformSourceRecords(node.toString())); + } + } else { + importResults.add(transformSourceRecords(docs.toString())); + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return importResults; + } + + private JsonNode convertStringJsonToJsonNode(String json) { + try { + return new ObjectMapper().readTree(json); + } catch (JsonProcessingException e) { + log.error("Unable to process json response.", e); + } + return null; + } + + public void setUrl(String url) { + this.url = url; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java index c4b111a38f..36ba82f60d 100644 --- a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java +++ b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigService.java @@ -34,7 +34,7 @@ public interface SubmissionConfigService { public int countSubmissionConfigs(); - public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle); + public SubmissionConfig getSubmissionConfigByCollection(Collection collection); public SubmissionConfig getSubmissionConfigByName(String submitName); diff --git a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java index a72bcc2c3b..fe063954a1 100644 --- a/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/submit/service/SubmissionConfigServiceImpl.java @@ -57,8 +57,8 @@ public class SubmissionConfigServiceImpl implements SubmissionConfigService, Ini } @Override - public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) { - return submissionConfigReader.getSubmissionConfigByCollection(collectionHandle); + public SubmissionConfig getSubmissionConfigByCollection(Collection collection) { + return submissionConfigReader.getSubmissionConfigByCollection(collection); } @Override diff --git a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml index 6b0ef3e9b9..7f6f872ce0 100644 --- a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml +++ b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml @@ -123,6 +123,20 @@ + + + + + + + + + + + @@ -150,6 +164,12 @@ + + + + + + diff --git a/dspace-api/src/main/resources/spring/spring-dspace-addon-suggestion-services.xml b/dspace-api/src/main/resources/spring/spring-dspace-addon-suggestion-services.xml new file mode 100644 index 0000000000..fb720137c4 --- /dev/null +++ b/dspace-api/src/main/resources/spring/spring-dspace-addon-suggestion-services.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml index 452460501a..921306ca2b 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -24,6 +24,9 @@ + + + @@ -257,6 +260,18 @@ + + + + + + + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/external-services.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/external-services.xml index 37e1fb5089..83d45b38cc 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/external-services.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/external-services.xml @@ -90,6 +90,8 @@ + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/solr-services.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/solr-services.xml index 29703e3ee0..0a7d7c5196 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/solr-services.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/solr-services.xml @@ -47,12 +47,14 @@ - + - + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/suggestions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/suggestions.xml new file mode 100644 index 0000000000..a3ae1cb875 --- /dev/null +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/suggestions.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/dspace-api/src/test/java/org/dspace/app/suggestion/MockSolrSuggestionProvider.java b/dspace-api/src/test/java/org/dspace/app/suggestion/MockSolrSuggestionProvider.java new file mode 100644 index 0000000000..af890da455 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/suggestion/MockSolrSuggestionProvider.java @@ -0,0 +1,20 @@ +/** + * 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.suggestion; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.core.Context; +import org.dspace.external.model.ExternalDataObject; + +public class MockSolrSuggestionProvider extends SolrSuggestionProvider { + + @Override + protected boolean isExternalDataObjectPotentiallySuggested(Context context, ExternalDataObject externalDataObject) { + return StringUtils.equals(MockSuggestionExternalDataSource.NAME, externalDataObject.getSource()); + } +} diff --git a/dspace-api/src/test/java/org/dspace/app/suggestion/MockSolrSuggestionStorageService.java b/dspace-api/src/test/java/org/dspace/app/suggestion/MockSolrSuggestionStorageService.java new file mode 100644 index 0000000000..1c843026d4 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/suggestion/MockSolrSuggestionStorageService.java @@ -0,0 +1,38 @@ +/** + * 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.suggestion; + +import org.dspace.solr.MockSolrServer; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Service; + +/** + * Mock SOLR service for the suggestion Core. + */ +@Service +public class MockSolrSuggestionStorageService extends SolrSuggestionStorageServiceImpl + implements InitializingBean, DisposableBean { + private MockSolrServer mockSolrServer; + + @Override + public void afterPropertiesSet() throws Exception { + mockSolrServer = new MockSolrServer("suggestion"); + solrSuggestionClient = mockSolrServer.getSolrServer(); + } + + /** Clear all records from the search core. */ + public void reset() { + mockSolrServer.reset(); + } + + @Override + public void destroy() throws Exception { + mockSolrServer.destroy(); + } +} \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/app/suggestion/MockSuggestionExternalDataSource.java b/dspace-api/src/test/java/org/dspace/app/suggestion/MockSuggestionExternalDataSource.java new file mode 100644 index 0000000000..cf0303debd --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/suggestion/MockSuggestionExternalDataSource.java @@ -0,0 +1,67 @@ +/** + * 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.suggestion; + +import java.util.List; +import java.util.Optional; + +import org.apache.commons.codec.binary.StringUtils; +import org.dspace.core.Context; +import org.dspace.external.model.ExternalDataObject; +import org.dspace.external.provider.AbstractExternalDataProvider; +import org.dspace.services.RequestService; +import org.dspace.services.model.Request; +import org.dspace.utils.DSpace; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class MockSuggestionExternalDataSource extends AbstractExternalDataProvider { + public static final String NAME = "suggestion"; + + @Autowired + private SuggestionService suggestionService; + + @Override + public String getSourceIdentifier() { + return NAME; + } + + @Override + public Optional getExternalDataObject(String id) { + RequestService requestService = new DSpace().getRequestService(); + Request currentRequest = requestService.getCurrentRequest(); + Context context = (Context) currentRequest.getAttribute("dspace.context"); + Suggestion suggestion = suggestionService.findUnprocessedSuggestion(context, id); + if (suggestion != null) { + ExternalDataObject extDataObj = new ExternalDataObject(NAME); + extDataObj.setDisplayValue(suggestion.getDisplay()); + extDataObj.setId(suggestion.getExternalSourceUri() + .substring(suggestion.getExternalSourceUri().lastIndexOf("/") + 1)); + extDataObj.setMetadata(suggestion.getMetadata()); + return Optional.of(extDataObj); + } + return null; + } + + @Override + public List searchExternalDataObjects(String query, int start, int limit) { + return null; + } + + @Override + public boolean supports(String source) { + return StringUtils.equals(NAME, source); + } + + @Override + public int getNumberOfResults(String query) { + return 0; + } + +} diff --git a/dspace-api/src/test/java/org/dspace/app/suggestion/SuggestionUtilsIT.java b/dspace-api/src/test/java/org/dspace/app/suggestion/SuggestionUtilsIT.java new file mode 100644 index 0000000000..dd9c0d8f5f --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/suggestion/SuggestionUtilsIT.java @@ -0,0 +1,218 @@ +/** + * 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.suggestion; + +import static java.util.Optional.of; +import static org.dspace.app.suggestion.SuggestionUtils.getFirstEntryByMetadatum; +import static org.dspace.orcid.model.OrcidProfileSectionType.EXTERNAL_IDS; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.net.URL; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; + +import org.apache.commons.collections.CollectionUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.dto.MetadataValueDTO; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.ItemService; +import org.dspace.external.factory.ExternalServiceFactory; +import org.dspace.external.model.ExternalDataObject; +import org.dspace.external.provider.ExternalDataProvider; +import org.dspace.external.provider.impl.OrcidPublicationDataProvider; +import org.dspace.external.service.ExternalDataService; +import org.dspace.kernel.ServiceManager; +import org.dspace.orcid.client.OrcidClient; +import org.dspace.orcid.client.OrcidConfiguration; +import org.dspace.orcid.factory.OrcidServiceFactory; +import org.dspace.orcid.model.OrcidTokenResponseDTO; +import org.dspace.orcid.service.OrcidProfileSectionFactoryService; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.utils.DSpace; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.orcid.jaxb.model.v3.release.record.Work; +import org.orcid.jaxb.model.v3.release.record.WorkBulk; +import org.orcid.jaxb.model.v3.release.record.summary.Works; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Tests for suggestion utilities @see SuggestionUtils + * @author Francesco Bacchelli (francesco.bacchelli at 4science.it) + */ +public class SuggestionUtilsIT extends AbstractIntegrationTestWithDatabase { + + private static ConfigurationService cfg; + private static final String ORCID = "0000-1111-2222-3333"; + private static final String ACCESS_TOKEN = "32c83ccb-c6d5-4981-b6ea-6a34a36de8ab"; + private static final String BASE_XML_DIR_PATH = "org/dspace/app/orcid-works/"; + private OrcidPublicationDataProvider dataProvider; + private SolrSuggestionProvider solrSuggestionProvider; + private OrcidProfileSectionFactoryService profileSectionFactoryService; + private ItemService itemService; + private Collection collection; + private ExternalDataProvider primaryProvider; + private Collection persons; + private OrcidConfiguration orcidConfiguration; + private OrcidClient orcidClientMock; + private OrcidClient orcidClient; + private String originalClientId; + + @Autowired + private SuggestionService suggestionService; + + @Before + public void setup() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + persons = CollectionBuilder.createCollection(context, parentCommunity) + .withEntityType("Person") + .withName("Profiles") + .build(); + + profileSectionFactoryService = OrcidServiceFactory.getInstance().getOrcidProfileSectionFactoryService(); + itemService = ContentServiceFactory.getInstance().getItemService(); + + context.restoreAuthSystemState(); + + cfg = DSpaceServicesFactory.getInstance().getConfigurationService(); + + ServiceManager serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + HashMap providers = serviceManager.getServiceByName("suggestionProviders", + HashMap.class); + solrSuggestionProvider = (SolrSuggestionProvider) providers.get("scopus"); + dataProvider = new DSpace().getServiceManager() + .getServiceByName("orcidPublicationDataProvider", OrcidPublicationDataProvider.class); + ExternalDataService externalDataService = ExternalServiceFactory.getInstance().getExternalDataService(); + primaryProvider = externalDataService.getExternalDataProvider("openaireFunding"); + + orcidConfiguration = new DSpace().getServiceManager() + .getServiceByName("org.dspace.orcid.client.OrcidConfiguration", OrcidConfiguration.class); + + orcidClientMock = mock(OrcidClient.class); + orcidClient = dataProvider.getOrcidClient(); + + dataProvider.setReadPublicAccessToken(null); + dataProvider.setOrcidClient(orcidClientMock); + + originalClientId = orcidConfiguration.getClientId(); + orcidConfiguration.setClientId("DSPACE-CLIENT-ID"); + orcidConfiguration.setClientSecret("DSPACE-CLIENT-SECRET"); + + when(orcidClientMock.getReadPublicAccessToken()).thenReturn(buildTokenResponse(ACCESS_TOKEN)); + + when(orcidClientMock.getWorks(any(), eq(ORCID))).thenReturn(unmarshall("works.xml", Works.class)); + when(orcidClientMock.getWorks(eq(ORCID))).thenReturn(unmarshall("works.xml", Works.class)); + + when(orcidClientMock.getObject(any(), eq(ORCID), any(), eq(Work.class))) + .then((invocation) -> of(unmarshall("work-" + invocation.getArgument(2) + ".xml", Work.class))); + when(orcidClientMock.getObject(eq(ORCID), any(), eq(Work.class))) + .then((invocation) -> of(unmarshall("work-" + invocation.getArgument(1) + ".xml", Work.class))); + + when(orcidClientMock.getWorkBulk(any(), eq(ORCID), any())) + .then((invocation) -> unmarshallWorkBulk(invocation.getArgument(2))); + when(orcidClientMock.getWorkBulk(eq(ORCID), any())) + .then((invocation) -> unmarshallWorkBulk(invocation.getArgument(1))); + } + + @After + public void after() { + dataProvider.setOrcidClient(orcidClient); + orcidConfiguration.setClientId(originalClientId); + } + + @Test + public void testGetAllEntriesByMetadatum() { + context.turnOffAuthorisationSystem(); + Item item = ItemBuilder.createItem(context, persons) + .withTitle("Test profile") + .withScopusAuthorIdentifier("SCOPUS-123456") + .withResearcherIdentifier("R-ID-01") + .build(); + context.restoreAuthSystemState(); + + List values = List.of(getMetadata(item, "person.identifier.scopus-author-id", 0)); + + Object firstOrcidObject = profileSectionFactoryService.createOrcidObject(context, values, EXTERNAL_IDS); + Optional optional = dataProvider.getExternalDataObject(ORCID + "::277902"); + + ExternalDataObject externalDataObject = optional.get(); + String openAireId = externalDataObject.getId(); + Suggestion suggestion = new Suggestion(solrSuggestionProvider.getSourceName(), item, openAireId); + suggestion.getMetadata().add( + new MetadataValueDTO("dc", "title", null, null, "dcTitle")); + suggestion.setDisplay(getFirstEntryByMetadatum(externalDataObject, "dc", "title", null)); + suggestion.getMetadata().add(new MetadataValueDTO("dc", "date", "issued", null, new Date().toString())); + suggestion.getMetadata().add(new MetadataValueDTO("dc", "description", "abstract", null, "description")); + suggestion.setExternalSourceUri(cfg.getProperty("dspace.server.url") + + "/api/integration/externalsources/" + primaryProvider.getSourceIdentifier() + "/entryValues/" + + openAireId); + List result = SuggestionUtils.getAllEntriesByMetadatum(externalDataObject, "dc", "title", null); + + assertTrue(result != null && !result.isEmpty()); + + assertTrue(CollectionUtils.isEqualCollection( + SuggestionUtils.getAllEntriesByMetadatum(externalDataObject, "dc.title"), + result)); + + String firstResult = SuggestionUtils.getFirstEntryByMetadatum(externalDataObject, "dc", "title", null); + assertTrue("Another cautionary tale.".equalsIgnoreCase(firstResult)); + firstResult = SuggestionUtils.getFirstEntryByMetadatum(externalDataObject, "dc.title"); + assertTrue("Another cautionary tale.".equalsIgnoreCase(firstResult)); + } + + private MetadataValue getMetadata(Item item, String metadataField, int place) { + List values = itemService.getMetadataByMetadataString(item, metadataField); + assertThat(values.size(), greaterThan(place)); + return values.get(place); + } + + private OrcidTokenResponseDTO buildTokenResponse(String accessToken) { + OrcidTokenResponseDTO response = new OrcidTokenResponseDTO(); + response.setAccessToken(accessToken); + return response; + } + + private WorkBulk unmarshallWorkBulk(List putCodes) throws Exception { + return unmarshall("workBulk-" + String.join("-", putCodes) + ".xml", WorkBulk.class); + } + + @SuppressWarnings("unchecked") + private T unmarshall(String fileName, Class clazz) throws Exception { + JAXBContext jaxbContext = JAXBContext.newInstance(clazz); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + URL resource = getClass().getClassLoader().getResource(BASE_XML_DIR_PATH + fileName); + if (resource == null) { + throw new IllegalStateException("No resource found named " + BASE_XML_DIR_PATH + fileName); + } + return (T) unmarshaller.unmarshal(new File(resource.getFile())); + } +} diff --git a/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java new file mode 100644 index 0000000000..0db4926d22 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigIT.java @@ -0,0 +1,73 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.util; + +import static org.junit.Assert.assertEquals; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.submit.factory.SubmissionServiceFactory; +import org.dspace.submit.service.SubmissionConfigService; +import org.junit.Test; + +/** + * Integration Tests for parsing and utilities on submission config forms / readers + * + * @author Toni Prieto + */ +public class SubmissionConfigIT extends AbstractIntegrationTestWithDatabase { + + @Test + public void testSubmissionMapByCommunityHandleSubmissionConfig() + throws SubmissionConfigReaderException { + + context.turnOffAuthorisationSystem(); + // Sep up a structure with one top community and two subcommunities with collections + Community topcom = CommunityBuilder.createCommunity(context, "123456789/topcommunity-test") + .withName("Parent Community") + .build(); + Community subcom1 = CommunityBuilder.createSubCommunity(context, topcom, "123456789/subcommunity-test") + .withName("Subcommunity 1") + .build(); + Community subcom2 = CommunityBuilder.createSubCommunity(context, topcom, "123456789/not-mapped3") + .withName("Subcommunity 2") + .build(); + // col1 should use the form item submission form mapped for subcom1 + Collection col1 = CollectionBuilder.createCollection(context, subcom1, "123456789/not-mapped1") + .withName("Collection 1") + .build(); + // col2 should use the item submission form mapped for the top community + Collection col2 = CollectionBuilder.createCollection(context, subcom2, "123456789/not-mapped2") + .withName("Collection 2") + .build(); + // col3 should use the item submission form directly mapped for this collection + Collection col3 = CollectionBuilder.createCollection(context, subcom1, "123456789/collection-test") + .withName("Collection 3") + .build(); + context.restoreAuthSystemState(); + + SubmissionConfigService submissionConfigService = SubmissionServiceFactory.getInstance() + .getSubmissionConfigService(); + + // for col1, it should return the item submission form defined for their parent subcom1 + SubmissionConfig submissionConfig1 = submissionConfigService.getSubmissionConfigByCollection(col1); + assertEquals("subcommunitytest", submissionConfig1.getSubmissionName()); + + // for col2, it should return the item submission form defined for topcom + SubmissionConfig submissionConfig2 = submissionConfigService.getSubmissionConfigByCollection(col2); + assertEquals("topcommunitytest", submissionConfig2.getSubmissionName()); + + // for col3, it should return the item submission form defined directly for the collection + SubmissionConfig submissionConfig3 = submissionConfigService.getSubmissionConfigByCollection(col3); + assertEquals("collectiontest", submissionConfig3.getSubmissionName()); + + } +} diff --git a/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java index cb1f828b93..4ac1931098 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/SubmissionConfigTest.java @@ -9,17 +9,20 @@ package org.dspace.app.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; import org.dspace.AbstractUnitTest; +import org.dspace.content.Collection; import org.dspace.submit.factory.SubmissionServiceFactory; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mock; /** * Tests for parsing and utilities on submission config forms / readers @@ -30,6 +33,9 @@ public class SubmissionConfigTest extends AbstractUnitTest { DCInputsReader inputReader; + @Mock + private Collection col1; + @BeforeClass public static void setUpClass() { } @@ -56,6 +62,8 @@ public class SubmissionConfigTest extends AbstractUnitTest { String typeBindSubmissionName = "typebindtest"; String typeBindSubmissionStepName = "typebindtest"; + when(col1.getHandle()).thenReturn(typeBindHandle); + // Expected field lists from typebindtest form List allConfiguredFields = new ArrayList<>(); allConfiguredFields.add("dc.title"); @@ -67,7 +75,7 @@ public class SubmissionConfigTest extends AbstractUnitTest { // Get submission configuration SubmissionConfig submissionConfig = SubmissionServiceFactory.getInstance().getSubmissionConfigService() - .getSubmissionConfigByCollection(typeBindHandle); + .getSubmissionConfigByCollection(col1); // Submission name should match name defined in item-submission.xml assertEquals(typeBindSubmissionName, submissionConfig.getSubmissionName()); // Step 0 - our process only has one step. It should not be null and have the ID typebindtest diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java index 775dfaabe2..013a18cd52 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java @@ -16,6 +16,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.alerts.service.SystemWideAlertService; import org.dspace.app.requestitem.factory.RequestItemServiceFactory; import org.dspace.app.requestitem.service.RequestItemService; +import org.dspace.app.suggestion.SolrSuggestionStorageService; import org.dspace.app.util.SubmissionConfigReaderException; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.factory.AuthorizeServiceFactory; @@ -116,6 +117,7 @@ public abstract class AbstractBuilder { static SubscribeService subscribeService; static SupervisionOrderService supervisionOrderService; static QAEventService qaEventService; + static SolrSuggestionStorageService solrSuggestionService; protected Context context; @@ -185,6 +187,7 @@ public abstract class AbstractBuilder { subscribeService = ContentServiceFactory.getInstance().getSubscribeService(); supervisionOrderService = SupervisionOrderServiceFactory.getInstance().getSupervisionOrderService(); qaEventService = new DSpace().getSingletonService(QAEventService.class); + solrSuggestionService = new DSpace().getSingletonService(SolrSuggestionStorageService.class); } diff --git a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java index f4f504e60f..5e9545fcaf 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java @@ -186,6 +186,10 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder { return addMetadataValue(item, "iiif", "image", "height", String.valueOf(i)); } + public ItemBuilder withDSpaceObjectOwner(String name, String authority) { + return addMetadataValue(item, "dspace", "object", "owner", null, name, authority, 600); + } + public ItemBuilder withMetadata(final String schema, final String element, final String qualifier, final String value) { return addMetadataValue(item, schema, element, qualifier, value); diff --git a/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java index 0631e1b55a..fe8f7b8167 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java @@ -68,8 +68,8 @@ public class ProcessBuilder extends AbstractBuilder { public ProcessBuilder withStartAndEndTime(String startTime, String endTime) throws ParseException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy"); - process.setStartTime(simpleDateFormat.parse(startTime)); - process.setFinishedTime(simpleDateFormat.parse(endTime)); + process.setStartTime(startTime == null ? null : simpleDateFormat.parse(startTime)); + process.setFinishedTime(endTime == null ? null : simpleDateFormat.parse(endTime)); return this; } diff --git a/dspace-api/src/test/java/org/dspace/builder/SuggestionTargetBuilder.java b/dspace-api/src/test/java/org/dspace/builder/SuggestionTargetBuilder.java new file mode 100644 index 0000000000..f9671bba60 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/builder/SuggestionTargetBuilder.java @@ -0,0 +1,161 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.builder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.solr.client.solrj.SolrServerException; +import org.dspace.app.suggestion.MockSuggestionExternalDataSource; +import org.dspace.app.suggestion.SolrSuggestionStorageService; +import org.dspace.app.suggestion.Suggestion; +import org.dspace.app.suggestion.SuggestionEvidence; +import org.dspace.app.suggestion.SuggestionTarget; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.dto.MetadataValueDTO; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; + +/** + * Builder to construct Item objects + * + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +public class SuggestionTargetBuilder extends AbstractBuilder { + public final static String EVIDENCE_MOCK_NAME = "MockEvidence"; + public final static String EVIDENCE_MOCK_NOTE = "Generated for testing purpose..."; + private Item item; + private SuggestionTarget target; + private List suggestions; + private String source; + private int total; + + protected SuggestionTargetBuilder(Context context) { + super(context); + } + + public static SuggestionTargetBuilder createTarget(final Context context, final Collection col, final String name) { + return createTarget(context, col, name, null); + } + + public static SuggestionTargetBuilder createTarget(final Context context, final Collection col, final String name, + final EPerson eperson) { + SuggestionTargetBuilder builder = new SuggestionTargetBuilder(context); + return builder.create(context, col, name, eperson); + } + + public static SuggestionTargetBuilder createTarget(final Context context, final Item item) { + SuggestionTargetBuilder builder = new SuggestionTargetBuilder(context); + return builder.create(context, item); + } + + private SuggestionTargetBuilder create(final Context context, final Collection col, final String name) { + return create(context, col, name, null); + } + + private SuggestionTargetBuilder create(final Context context, final Collection col, final String name, + final EPerson eperson) { + this.context = context; + + try { + ItemBuilder itemBuilder = ItemBuilder.createItem(context, col).withTitle(name); + if (eperson != null) { + itemBuilder = itemBuilder.withDSpaceObjectOwner(eperson.getFullName(), eperson.getID().toString()); + } + item = itemBuilder.build(); + context.dispatchEvents(); + indexingService.commit(); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private SuggestionTargetBuilder create(final Context context, final Item item) { + this.context = context; + this.item = item; + return this; + } + + public SuggestionTargetBuilder withSuggestionCount(final String source, final int total) { + this.source = source; + this.total = total; + return this; + } + + @Override + public SuggestionTarget build() { + target = new SuggestionTarget(item); + target.setTotal(total); + target.setSource(source); + suggestions = generateAllSuggestion(); + try { + for (Suggestion s : suggestions) { + solrSuggestionService.addSuggestion(s, false, false); + } + solrSuggestionService.commit(); + } catch (SolrServerException | IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return target; + } + + @Override + public void cleanup() throws Exception { + solrSuggestionService.deleteTarget(target); + } + + @Override + protected SolrSuggestionStorageService getService() { + return solrSuggestionService; + } + + @Override + public void delete(Context c, SuggestionTarget dso) throws Exception { + solrSuggestionService.deleteTarget(dso); + } + + private List generateAllSuggestion() { + List allSuggestions = new ArrayList(); + for (int idx = 0; idx < target.getTotal(); idx++) { + String idPartStr = String.valueOf(idx + 1); + Suggestion sug = new Suggestion(source, item, idPartStr); + sug.setDisplay("Suggestion " + source + " " + idPartStr); + MetadataValueDTO mTitle = new MetadataValueDTO(); + mTitle.setSchema("dc"); + mTitle.setElement("title"); + mTitle.setValue("Title Suggestion " + idPartStr); + + MetadataValueDTO mSource1 = new MetadataValueDTO(); + mSource1.setSchema("dc"); + mSource1.setElement("source"); + mSource1.setValue("Source 1"); + + MetadataValueDTO mSource2 = new MetadataValueDTO(); + mSource2.setSchema("dc"); + mSource2.setElement("source"); + mSource2.setValue("Source 2"); + + sug.getMetadata().add(mTitle); + sug.getMetadata().add(mSource1); + sug.getMetadata().add(mSource2); + + sug.setExternalSourceUri( + "http://localhost/api/integration/externalsources/" + MockSuggestionExternalDataSource.NAME + + "/entryValues/" + idPartStr); + sug.getEvidences().add(new SuggestionEvidence(EVIDENCE_MOCK_NAME, + idx % 2 == 0 ? 100 - idx : (double) idx / 2, EVIDENCE_MOCK_NOTE)); + allSuggestions.add(sug); + } + return allSuggestions; + } + +} diff --git a/dspace-api/src/test/resources/org/dspace/app/orcid-works/works.xml b/dspace-api/src/test/resources/org/dspace/app/orcid-works/works.xml index 411160ef8e..9ad07607e3 100644 --- a/dspace-api/src/test/resources/org/dspace/app/orcid-works/works.xml +++ b/dspace-api/src/test/resources/org/dspace/app/orcid-works/works.xml @@ -77,6 +77,7 @@ Another cautionary tale. + Second title journal-article diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index b900ebe88d..db8b55c79b 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -156,7 +156,7 @@ org.hamcrest - hamcrest-all + hamcrest compile diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index 4f842b8e94..25cc1ee365 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -450,6 +450,16 @@ public class XOAI { doc.addField("item.communities", "com_" + com.getHandle().replace("/", "_")); } + boolean hasBitstream = false; + + for (Bundle b : item.getBundles("ORIGINAL")) { + if (b.getBitstreams().size() > 0) { + hasBitstream = true; + } + } + + doc.addField("item.hasbitstream", hasBitstream); + List allData = itemService.getMetadata(item, Item.ANY, Item.ANY, Item.ANY, Item.ANY); for (MetadataValue dc : allData) { MetadataField field = dc.getMetadataField(); diff --git a/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java b/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java new file mode 100644 index 0000000000..3599c5b9e1 --- /dev/null +++ b/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java @@ -0,0 +1,57 @@ +/** + * 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.xoai.filter; + +import java.sql.SQLException; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.dspace.content.Bundle; +import org.dspace.content.Item; +import org.dspace.handle.factory.HandleServiceFactory; +import org.dspace.handle.service.HandleService; +import org.dspace.xoai.data.DSpaceItem; +import org.dspace.xoai.filter.results.SolrFilterResult; + + +/** + * Created by Philip Vissenaekens (philip at atmire dot com) + * Date: 21/04/15 + * Time: 15:18 + */ +public class ItemsWithBitstreamFilter extends DSpaceFilter { + + private static Logger log = LogManager.getLogger(ItemsWithBitstreamFilter.class); + + private static final HandleService handleService + = HandleServiceFactory.getInstance().getHandleService(); + + @Override + public SolrFilterResult buildSolrQuery() { + return new SolrFilterResult("item.hasbitstream:true"); + } + + @Override + public boolean isShown(DSpaceItem item) { + try { + String handle = DSpaceItem.parseHandle(item.getIdentifier()); + if (handle == null) { + return false; + } + Item dspaceItem = (Item) handleService.resolveToObject(context, handle); + for (Bundle b : dspaceItem.getBundles("ORIGINAL")) { + if (b.getBitstreams().size() > 0) { + return true; + } + } + } catch (SQLException e) { + log.error(e.getMessage(), e); + } + return false; + } +} diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 938cf0d64a..20dcabcb20 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -11,6 +11,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; import com.lyncode.xoai.dataprovider.xml.xoai.Element; @@ -21,6 +23,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.app.util.service.MetadataExposureService; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.factory.AuthorizeServiceFactory; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; @@ -114,23 +117,21 @@ public class ItemUtils { log.error("Null bitstream found, check item uuid: " + item.getID()); break; } + boolean primary = false; + // Check if current bitstream is in original bundle + 1 of the 2 following + // Bitstream = primary bitstream in bundle -> true + // No primary bitstream found in bundle-> only the first one gets flagged as "primary" + if (b.getName() != null && b.getName().equals("ORIGINAL") && (b.getPrimaryBitstream() != null + && b.getPrimaryBitstream().getID() == bit.getID() + || b.getPrimaryBitstream() == null && bit.getID() == bits.get(0).getID())) { + primary = true; + } + Element bitstream = create("bitstream"); bitstreams.getElement().add(bitstream); - String url = ""; - String bsName = bit.getName(); - String sid = String.valueOf(bit.getSequenceID()); + String baseUrl = configurationService.getProperty("oai.bitstream.baseUrl"); - String handle = null; - // get handle of parent Item of this bitstream, if there - // is one: - List bn = bit.getBundles(); - if (!bn.isEmpty()) { - List bi = bn.get(0).getItems(); - if (!bi.isEmpty()) { - handle = bi.get(0).getHandle(); - } - } - url = baseUrl + "/bitstreams/" + bit.getID().toString() + "/download"; + String url = baseUrl + "/bitstreams/" + bit.getID().toString() + "/download"; String cks = bit.getChecksum(); String cka = bit.getChecksumAlgorithm(); @@ -147,18 +148,65 @@ public class ItemUtils { if (description != null) { bitstream.getField().add(createValue("description", description)); } + // Add bitstream embargo information (READ policy present, for Anonymous group with a start date) + addResourcePolicyInformation(context, bit, bitstream); + bitstream.getField().add(createValue("format", bit.getFormat(context).getMIMEType())); bitstream.getField().add(createValue("size", "" + bit.getSizeBytes())); bitstream.getField().add(createValue("url", url)); bitstream.getField().add(createValue("checksum", cks)); bitstream.getField().add(createValue("checksumAlgorithm", cka)); bitstream.getField().add(createValue("sid", bit.getSequenceID() + "")); + // Add primary bitstream field to allow locating easily the primary bitstream information + bitstream.getField().add(createValue("primary", primary + "")); } } return bundles; } + /** + * This method will add metadata information about associated resource policies for a give bitstream. + * It will parse of relevant policies and add metadata information + * @param context + * @param bitstream the bitstream object + * @param bitstreamEl the bitstream metadata object to add resource policy information to + * @throws SQLException + */ + private static void addResourcePolicyInformation(Context context, Bitstream bitstream, Element bitstreamEl) + throws SQLException { + // Pre-filter access policies by DSO (bitstream) and Action (READ) + List policies = authorizeService.getPoliciesActionFilter(context, bitstream, Constants.READ); + + // Create resourcePolicies container + Element resourcePolicies = create("resourcePolicies"); + + for (ResourcePolicy policy : policies) { + String groupName = policy.getGroup() != null ? policy.getGroup().getName() : null; + String user = policy.getEPerson() != null ? policy.getEPerson().getName() : null; + String action = Constants.actionText[policy.getAction()]; + Date startDate = policy.getStartDate(); + Date endDate = policy.getEndDate(); + + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + + Element resourcePolicyEl = create("resourcePolicy"); + resourcePolicyEl.getField().add(createValue("group", groupName)); + resourcePolicyEl.getField().add(createValue("user", user)); + resourcePolicyEl.getField().add(createValue("action", action)); + if (startDate != null) { + resourcePolicyEl.getField().add(createValue("start-date", formatter.format(startDate))); + } + if (endDate != null) { + resourcePolicyEl.getField().add(createValue("end-date", formatter.format(endDate))); + } + // Add resourcePolicy to list of resourcePolicies + resourcePolicies.getElement().add(resourcePolicyEl); + } + // Add list of resource policies to the corresponding Bitstream XML Element + bitstreamEl.getElement().add(resourcePolicies); + } + private static Element createLicenseElement(Context context, Item item) throws SQLException, AuthorizeException, IOException { Element license = create("license"); @@ -178,7 +226,7 @@ public class ItemUtils { license.getField().add(createValue("bin", Base64Utils.encode(out.toString()))); } else { log.info("Missing READ rights for license bitstream. Did not include license bitstream for item: " - + item.getID() + "."); + + item.getID() + "."); } } } diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/RioxxXslTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/RioxxXslTest.java new file mode 100644 index 0000000000..74dfaf2902 --- /dev/null +++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/RioxxXslTest.java @@ -0,0 +1,35 @@ +/** + * 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.xoai.tests.stylesheets; + +import static org.dspace.xoai.tests.support.XmlMatcherBuilder.xml; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +import org.dspace.xoai.tests.support.XmlMatcherBuilder; +import org.junit.Test; + +public class RioxxXslTest extends AbstractXSLTest { + @Test + public void rioxxCanTransformInput() throws Exception { + String result = apply("rioxx.xsl").to(resource("xoai-rioxx-test.xml")); + + assertThat(result, is(rioxx().withXPath("//dc:title", equalTo("The Intercorrelation Between " + + "Executive Function, Physics Problem Solving, Mathematical, and Matrix Reasoning Skills: " + + "Reflections from a Small-Scale Experiment")))); + } + + private XmlMatcherBuilder rioxx() { + return xml() + .withNamespace("rioxx", "http://www.rioxx.net/schema/v3.0/rioxx/") + .withNamespace("rioxxterms", "http://docs.rioxx.net/schema/v3.0/rioxxterms/") + .withNamespace("dcterms", "http://purl.org/dc/terms/") + .withNamespace("dc", "http://purl.org/dc/elements/1.1/"); + } +} diff --git a/dspace-oai/src/test/resources/rioxx-test-invalid.xml b/dspace-oai/src/test/resources/rioxx-test-invalid.xml new file mode 100644 index 0000000000..c8daf1a28d --- /dev/null +++ b/dspace-oai/src/test/resources/rioxx-test-invalid.xml @@ -0,0 +1,89 @@ + + + Data on Secchi disc depth (the depth at which a standard white disc lowered into the water just becomes invisible to a surface observer) show that water clarity in the North Sea declined during the 20th century, with likely consequences for marine primary production. However, the causes of this trend remain unknown. Here we analyse the hypothesis that changes in the North Sea's wave climate were largely responsible by causing an increase in the concentrations of suspended particulate matter (SPM) in the water column through the resuspension of seabed sediments. First, we analysed the broad-scale statistical relationships between SPM and bed shear stress due to waves and tides. We used hindcasts of wave and current data to construct a space–time dataset of bed shear stress between 1997 and 2017 across the northwest European Continental Shelf and compared the results with satellite-derived SPM concentrations. Bed shear stress was found to drive most of the inter-annual variation in SPM in the hydrographically mixed waters of the central and southern North Sea. We then used a long-term wave reanalysis to construct a time series of bed shear stress from 1900 to 2010. This shows that bed shear stress increased significantly across much of the shelf during this period, with increases of over 20 % in the southeastern North Sea. An increase in bed shear stress of this magnitude would have resulted in a large reduction in water clarity. Wave-driven processes are rarely included in projections of climate change impacts on marine ecosystems, but our analysis indicates that this should be reconsidered for shelf sea regions. + + en + + + European Geosciences Union + https://isni.org/isni/0000000110927289 + + + 1812-0792 + + Increasing turbidity in the North Sea during the 20th century due to changing wave climate + + 2019-10-02 + + + Wilson, Robert J. + https://orcid.org/0000-0002-0592-366X + + + + Heath, Michael R. + https://orcid.org/0000-0001-6602-3107 + https://viaf.org/viaf/15147423189944882613 + + + 2019-12-09 + + 2019-10-15 + + https://purl.org/coar/resource_type/c_2df8fbb1 + + + DP190101507 + + + + 61387 + + + + https://strathprints.strath.ac.uk/70117/7/Wilson_Heath_OS2019_Increasing_turbidity_in_the_North_Sea_during_the_20th_century.pdf + + + + + https://doi.org/10.1007/s11229-020-02724-x + + + + + https://doi.org/10.15129/5d28213e-8f9f-402a-b550-fc588518cb8b + + + + + https://doi.org/10.5281/zenodo.3478185 + + diff --git a/dspace-oai/src/test/resources/rioxx-test-valid.xml b/dspace-oai/src/test/resources/rioxx-test-valid.xml new file mode 100644 index 0000000000..74ffd43eb6 --- /dev/null +++ b/dspace-oai/src/test/resources/rioxx-test-valid.xml @@ -0,0 +1,92 @@ + + + + Data on Secchi disc depth (the depth at which a standard white disc lowered into the water just becomes invisible to a surface observer) show that water clarity in the North Sea declined during the 20th century, with likely consequences for marine primary production. However, the causes of this trend remain unknown. Here we analyse the hypothesis that changes in the North Sea's wave climate were largely responsible by causing an increase in the concentrations of suspended particulate matter (SPM) in the water column through the resuspension of seabed sediments. First, we analysed the broad-scale statistical relationships between SPM and bed shear stress due to waves and tides. We used hindcasts of wave and current data to construct a space–time dataset of bed shear stress between 1997 and 2017 across the northwest European Continental Shelf and compared the results with satellite-derived SPM concentrations. Bed shear stress was found to drive most of the inter-annual variation in SPM in the hydrographically mixed waters of the central and southern North Sea. We then used a long-term wave reanalysis to construct a time series of bed shear stress from 1900 to 2010. This shows that bed shear stress increased significantly across much of the shelf during this period, with increases of over 20 % in the southeastern North Sea. An increase in bed shear stress of this magnitude would have resulted in a large reduction in water clarity. Wave-driven processes are rarely included in projections of climate change impacts on marine ecosystems, but our analysis indicates that this should be reconsidered for shelf sea regions. + + en + + + European Geosciences Union + https://isni.org/isni/0000000110927289 + + + 1812-0792 + + Increasing turbidity in the North Sea during the 20th century due to changing wave climate + + 2019-10-02 + + + Wilson, Robert J. + https://orcid.org/0000-0002-0592-366X + + + + Heath, Michael R. + https://orcid.org/0000-0001-6602-3107 + https://viaf.org/viaf/15147423189944882613 + + + 2019-12-09 + + 2019-10-15 + + https://purl.org/coar/resource_type/c_2df8fbb1 + + + DP190101507 + + + + 61387 + + + https://strathprints.strath.ac.uk/70117/ + + + https://strathprints.strath.ac.uk/70117/7/Wilson_Heath_OS2019_Increasing_turbidity_in_the_North_Sea_during_the_20th_century.pdf + + + + + https://doi.org/10.1007/s11229-020-02724-x + + + + + https://doi.org/10.15129/5d28213e-8f9f-402a-b550-fc588518cb8b + + + + + https://doi.org/10.5281/zenodo.3478185 + + diff --git a/dspace-oai/src/test/resources/xoai-rioxx-test.xml b/dspace-oai/src/test/resources/xoai-rioxx-test.xml new file mode 100644 index 0000000000..33c2c3d101 --- /dev/null +++ b/dspace-oai/src/test/resources/xoai-rioxx-test.xml @@ -0,0 +1,217 @@ + + + + + + + + Publication + + + + + + + + 2023-11-07 + + + + + + + + Tsigaridis, Konstantinos G. + virtual::44 + -1 + Wang, Rui + virtual::46 + -1 + Ellefson, Michelle R. + virtual::47 + -1 + + + + + + + 2023-11-07T11:34:10Z + + + + + 2023-11-07T11:34:10Z + + + + + 2022-11-30 + + + + + + + https://example.org/handle/1811/160 + + + + + + + eng + + + + + + The Intercorrelation Between Executive Function, Physics Problem Solving, Mathematical, and Matrix Reasoning Skills: Reflections from a Small-Scale Experiment + + + + + Article + + + + + + + a57363fa-f82e-4684-bd76-f7bc1e893603 + virtual::44 + -1 + e00b3d0d-65e2-4c30-825d-1a4839845790 + virtual::46 + -1 + bdd38a03-206d-4f9b-bafb-70e060ad176f + virtual::47 + -1 + + + + a57363fa-f82e-4684-bd76-f7bc1e893603 + virtual::44 + -1 + e00b3d0d-65e2-4c30-825d-1a4839845790 + virtual::46 + -1 + bdd38a03-206d-4f9b-bafb-70e060ad176f + virtual::47 + -1 + + + + + + 05a400b1-ff0b-4e40-80cd-a7d1b712ace2 + virtual::71 + -1 + + + + + 7524a0cf-3ea2-40c7-a265-d583425ed4d7 + virtual::71 + -1 + + + + 7524a0cf-3ea2-40c7-a265-d583425ed4d7 + virtual::71 + -1 + + + + + + + + + 0000-0003-0407-9767 + virtual::47 + -1 + + + + + + + + 2634-9876 + virtual::71 + -1 + + + + + + ORIGINAL + + + Tsigaridis et al., 2022.pdf + application/pdf + 1554917 + https://example.org/bitstreams/9121e795-0af3-4ff3-be2a-4b28418fb269/download + 42d8cd076931e43e02d0af70a36d704e + MD5 + 1 + true + + + Anonymous + Anonymous + READ + + + + + + + THUMBNAIL + + + cerj_volume_9_thumbnail.jpg + image/jpeg + 14513 + https://example.org/bitstreams/16245937-10bb-46db-9817-683a5ebd8d63/download + 8c39d691daa8e5f9d668668db7910cd6 + MD5 + 2 + false + + + Anonymous + Anonymous + READ + + + + + + + + 1811/160 + oai:example.org:1811/160 + 2023-12-13 13:07:56.51 + + open.access + + + + https://example.org + Diamond DSpace (dev) + support@example.org + + + diff --git a/dspace-rest/README.md b/dspace-rest/README.md deleted file mode 100644 index 07d71d66ed..0000000000 --- a/dspace-rest/README.md +++ /dev/null @@ -1,194 +0,0 @@ -#DSpace REST API (Jersey) - DEPRECATED - -A RESTful web services API for DSpace, built using JAX-RS1 JERSEY. - -_This REST API has been deprecated and will be removed in v8. Please use the Server API (/server) webapp instead._ - -##Getting Started -This REST API is integrated directly into the DSpace codebase. - - * Rebuild as usual: mvn + ant - * Deploy the webapp (i.e to Tomcat) - * `````` - - -REST API can do all CRUD (create, read, update, delete) operations over communities, collections, items, bitstream and bitstream policies. Without logging into the REST API, you have read access as an anonymous user (member of the Anonymous group). If you want to make changes in DSpace using the REST API, you must log into the API using the "login" endpoint and then use the returned token in request header of your subsequent API calls. - -##Endpoints - -| Resource |CREATE|READ list|READ single|Edit|Delete|Search| -| ------------- |------|:-------:|-----------|----|------|------| -| /communities | Y | Y | Y | Y | Y | | -| /collections | Y | Y | Y | Y | Y | Y | -| /items | Y | Y | Y | Y | Y | Y | -| /bitstreams | Y | Y | Y | Y | Y | || - -Search in collections is possible only by name and search in items only by metadata field. - -###Index -Get information on how to use the API -- GET http://localhost:8080 - -Test whether the REST API is running and available -- GET http://localhost:8080/rest/test - -Log into REST API -- POST http://localhost:8080/rest/login - -Logout from REST API -- POST http://localhost:8080/rest/logout - -Get status of REST API and the logged-in user -- GET http://localhost:8080/rest/status - - -###Communities -View the list of top-level communities -- GET http://localhost:8080/rest/communities/top-communities - -View the list of all communities -- GET http://localhost:8080/rest/communities[?expand={collections,parentCommunity,subCommunities,logo,all}] - -View a specific community -- GET http://localhost:8080/rest/communities/:ID[?expand={collections,parentCommunity,subCommunities,logo,all}] - -View the list of subcollections in community -- GET http://localhost:8080/rest/communities/:ID/collections[?expand={items,parentCommunityList,license,logo,all}] - -View the list of subcommunities in community -- GET http://localhost:8080/rest/communities/:ID/communities[?expand={collections,parentCommunity,subCommunities,logo,all}] - -Create new top-level community -- POST http://localhost:8080/rest/communities - -Create new subcollection in community -- POST http://localhost:8080/rest/communities/:ID/collections - -Create new subcommunity in community -- POST http://localhost:8080/rest/communities/:ID/communities - -Update community -- PUT http://localhost:8080/rest/communities/:ID - -Delete community -- DELETE http://localhost:8080/rest/communities/:ID - -Delete subcollection in community -- DELETE http://localhost:8080/rest/communities/:ID/collections/:ID - -Delete subcommunity in community -- DELETE http://localhost:8080/rest/communities/:ID/communities/:ID - - -###Collections -View the list of collections -- GET http://localhost:8080/rest/collections[?expand={items,parentCommunityList,license,logo,all}] - -View a specific collection -- GET http://localhost:8080/rest/collections/:ID[?expand={items,parentCommunityList,license,logo,all}] - -View items in collection -- GET http://localhost:8080/rest/collections/:ID/items[?expand={metadata,parentCollection,parentcollectionList,parentCommunityList,bitstreams,all}] - -Create item in collection -- POST http://localhost:8080/rest/collections/:ID/items - -Find collection by name -- POST http://localhost:8080/rest/collections/find-collection - -Update collection -- PUT http://localhost:8080/rest/collections/:ID - -Delete collection -- DELETE http://localhost:8080/rest/collections/:ID - -Delete item in collection -- DELETE http://localhost:8080/rest/collections/:ID/items/:ID - - -###Items -View the list of items -- GET http://localhost:8080/rest/items[?expand={metadata,parentCollection,parentcollectionList,parentCommunityList,bitstreams,all}] - -View speciific item -- GET http://localhost:8080/rest/items/:ID[?expand={metadata,parentCollection,parentcollectionList,parentCommunityList,bitstreams,all}] - -View an Item and view its bitstreams -- GET http://localhost:8080/rest/items/:ID/bitstreams[?expand={parent,policies,all}] - -View an Item, and view its metadata -- GET http://localhost:8080/rest/items/:ID/metadata - -Find item by metadata -- POST http://localhost:8080/rest/items/find-by-metadata-field - -Add metadata to item -- POST http://localhost:8080/rest/items/:ID/metadata - -Create bitstream in item -- POST http://localhost:8080/rest/items/:ID/bitstreams - -Update metadata in item -- PUT http://localhost:8080/rest/items/:ID/metadata - -Delete item -- DELETE http://localhost:8080/rest/items/:ID - -Delete all metadata in item -- DELETE http://localhost:8080/rest/items/:ID/metadata - -Delete bitstream in item -- DELETE http://localhost:8080/rest/items/:ID/bitstreams/:ID - - -###Bitstreams -View the list of bitstreams -- GET http://localhost:8080/rest/bitstreams[?expand={parent,policies,all}] - -View information about a bitstream -- GET http://localhost:8080/rest/bitstreams/:ID[?expand={parent,policies,all}] - -View/Download a specific Bitstream -- GET http://localhost:8080/rest/bitstreams/:ID/retrieve - -View the list of policies of bitstream -- GET http://localhost:8080/rest/bitstreams/:ID/policy - -Add policy to bitstream -- POST http://localhost:8080/rest/bitstreams/:ID/policy - -Update bitstream -- PUT http://localhost:8080/rest/bitstreams/:ID - -Update data of bitstream -- PUT http://localhost:8080/rest/bitstreams/:ID/data - -Delete bitstream -- DELETE http://localhost:8080/rest/bitstreams/:ID - -Delete policy of bitstream -- DELETE http://localhost:8080/rest/bitstreams/:ID/policy/:ID - - -####Statistics -Recording view events of items and download events of bitstreams (set stats = true in rest.cfg to enable recording of events) -http://localhost:8080/rest/items/:ID?userIP=ip&userAgent=userAgent&xforwardedfor=xforwardedfor -If no parameters are given, the details of the HTTP request sender are used in statistics. -This enables tools like proxies to supply the details of their user rather than themselves. - - -###Handles -Lookup a DSpaceObject by its Handle, this produces the name/ID that you look up in /bitstreams, /items, /collections, /communities -- http://localhost:8080/rest/handle/{prefix}/{suffix} - -##Expand -There is an ?expand= query parameter for more expensive operations. You can add it at the end of the request URL. -It is optional, all, some or none. The response will usually indicate what the available "expand" options are. - -##HTTP Responses -* 200 OK - The requested object/objects exists -* 401 Unauthorized - The anonymous user does not have READ access to that object -* 404 Not Found - The specified object doesn't exist -* 405 Method Not Allowed - Wrong request method (GET,POST,PUT,DELETE) or wrong data format (JSON/XML). -* 415 Unsupported Media Type - Missing "Content-Type: application/json" or "Content-Type: application/xml" request header -* 500 Server Error - Likely a SQLException, IOException, more details in the logs. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml deleted file mode 100644 index d7daf92aba..0000000000 --- a/dspace-rest/pom.xml +++ /dev/null @@ -1,202 +0,0 @@ - - 4.0.0 - org.dspace - dspace-rest - war - 8.0-SNAPSHOT - DSpace (Deprecated) REST Webapp - DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. - Please consider using the REST API in the dspace-server-webapp instead! - http://demo.dspace.org - - - org.dspace - dspace-parent - 8.0-SNAPSHOT - .. - - - - - ${basedir}/.. - - - - - org.apache.maven.plugins - maven-war-plugin - - true - - true - - - - com.mycila - license-maven-plugin - - - - **/static/reports/spin.js - **/static/reports/README.md - **/*.xsd - - - - - - - - - - org.glassfish.jersey.core - jersey-server - ${jersey.version} - - - org.glassfish.jersey.containers - jersey-container-servlet - ${jersey.version} - - - org.glassfish.jersey.media - jersey-media-json-jackson - ${jersey.version} - - - org.glassfish.jersey.media - jersey-media-jaxb - ${jersey.version} - - - - - org.springframework - spring-core - - - - org.springframework - spring-context - - - - org.springframework - spring-web - - - - - org.glassfish.jersey.ext - jersey-spring5 - ${jersey.version} - - - - org.springframework - spring - - - org.springframework - spring-core - - - org.springframework - spring-web - - - org.springframework - spring-beans - - - org.springframework - spring-context - - - org.springframework - spring-aop - - - - jakarta.annotation - jakarta.annotation-api - - - - - org.springframework.security - spring-security-core - ${spring-security.version} - - - - org.springframework - spring-expression - - - - - org.springframework.security - spring-security-web - ${spring-security.version} - - - - org.springframework - spring-expression - - - - - org.springframework.security - spring-security-config - ${spring-security.version} - - - - org.dspace - dspace-api - - - - - org.apache.commons - commons-dbcp2 - - - org.postgresql - postgresql - - - javax.servlet - javax.servlet-api - provided - - - org.atteo - evo-inflector - 1.2.1 - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-core - - - org.apache.logging.log4j - log4j-web - - - org.dspace - dspace-services - - - junit - junit - test - - - diff --git a/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java b/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java deleted file mode 100644 index 3a6ad85960..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/BitstreamResource.java +++ /dev/null @@ -1,783 +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.rest; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URLConnection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.authorize.service.ResourcePolicyService; -import org.dspace.content.BitstreamFormat; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.BitstreamFormatService; -import org.dspace.content.service.BitstreamService; -import org.dspace.content.service.BundleService; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.GroupService; -import org.dspace.rest.common.Bitstream; -import org.dspace.rest.common.ResourcePolicy; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.storage.bitstore.factory.StorageServiceFactory; -import org.dspace.storage.bitstore.service.BitstreamStorageService; -import org.dspace.usage.UsageEvent; - -/** - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -// Every DSpace class used without namespace is from package -// org.dspace.rest.common.*. Otherwise namespace is defined. -@Path("/bitstreams") -public class BitstreamResource extends Resource { - protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() - .getBitstreamFormatService(); - protected BitstreamStorageService bitstreamStorageService = StorageServiceFactory.getInstance() - .getBitstreamStorageService(); - protected ResourcePolicyService resourcePolicyService = AuthorizeServiceFactory.getInstance() - .getResourcePolicyService(); - protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(BitstreamResource.class); - - /** - * Return bitstream properties without file data. It can throw - * WebApplicationException with three response codes. Response code - * NOT_FOUND(404) or UNAUTHORIZED(401) or INTERNAL_SERVER_ERROR(500). Bad - * request is when the bitstream id does not exist. UNAUTHORIZED if the user - * logged into the DSpace context does not have the permission to access the - * bitstream. Server error when something went wrong. - * - * @param bitstreamId Id of bitstream in DSpace. - * @param expand This string defines which additional optional fields will be added - * to bitstream response. Individual options are separated by commas without - * spaces. The options are: "all", "parent". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return If user is allowed to read bitstream, it returns instance of - * bitstream. Otherwise, it throws WebApplicationException with - * response code UNAUTHORIZED. - * @throws WebApplicationException It can happen on: Bad request, unauthorized, SQL exception - * and context exception(could not create context). - */ - @GET - @Path("/{bitstream_id}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Bitstream getBitstream(@PathParam("bitstream_id") String bitstreamId, @QueryParam("expand") String expand, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading bitstream(id=" + bitstreamId + ") metadata."); - org.dspace.core.Context context = null; - Bitstream bitstream = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.READ); - - writeStats(dspaceBitstream, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, - request, context); - - bitstream = new Bitstream(dspaceBitstream, servletContext, expand, context); - context.complete(); - log.trace("Bitstream(id=" + bitstreamId + ") was successfully read."); - - } catch (SQLException e) { - processException( - "Someting went wrong while reading bitstream(id=" + bitstreamId + ") from database! Message: " + e, - context); - } catch (ContextException e) { - processException( - "Someting went wrong while reading bitstream(id=" + bitstreamId + "), ContextException. Message: " - + e.getMessage(), context); - } finally { - processFinally(context); - } - - return bitstream; - } - - /** - * Return all bitstream resource policies from all bundles, in which - * the bitstream is present. - * - * @param bitstreamId Id of bitstream in DSpace. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @return Returns an array of ResourcePolicy objects. - */ - @GET - @Path("/{bitstream_id}/policy") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public ResourcePolicy[] getBitstreamPolicies(@PathParam("bitstream_id") String bitstreamId, - @Context HttpHeaders headers) { - - log.info("Reading bitstream(id=" + bitstreamId + ") policies."); - org.dspace.core.Context context = null; - ResourcePolicy[] policies = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.READ); - policies = new Bitstream(dspaceBitstream, servletContext, "policies", context).getPolicies(); - - context.complete(); - log.trace("Policies for bitstream(id=" + bitstreamId + ") was successfully read."); - - } catch (SQLException e) { - processException("Someting went wrong while reading policies of bitstream(id=" + bitstreamId - + "), SQLException! Message: " + e, context); - } catch (ContextException e) { - processException("Someting went wrong while reading policies of bitstream(id=" + bitstreamId - + "), ContextException. Message: " + e.getMessage(), context); - } finally { - processFinally(context); - } - - return policies; - } - - /** - * Read list of bitstreams. It throws WebApplicationException with response - * code INTERNAL_SERVER_ERROR(500), if there was problem while reading - * bitstreams from database. - * - * @param expand This string defines which additional optional fields will be added - * to bitstream response. Individual options are separated by commas without - * spaces. The options are: "all", "parent". - * @param limit How many bitstreams will be in the list. Default value is 100. - * @param offset On which offset (item) the list starts. Default value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return Returns an array of bistreams. Array doesn't contain bitstreams for - * which the user doesn't have read permission. - * @throws WebApplicationException Thrown in case of a problem with reading the database or with - * creating a context. - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Bitstream[] getBitstreams(@QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading bitstreams.(offset=" + offset + ",limit=" + limit + ")"); - org.dspace.core.Context context = null; - List bitstreams = new ArrayList(); - - try { - context = createContext(); - List dspaceBitstreams = bitstreamService.findAll(context); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Paging was badly set."); - limit = 100; - offset = 0; - } - - // TODO If bitstream doesn't exist, throws exception. - for (int i = offset; (i < (offset + limit)) && (i < dspaceBitstreams.size()); i++) { - if (authorizeService - .authorizeActionBoolean(context, dspaceBitstreams.get(i), org.dspace.core.Constants.READ)) { - if (bitstreamService.getParentObject(context, dspaceBitstreams - .get(i)) != null) { // To eliminate bitstreams which cause exception, because of - // reading under administrator permissions - bitstreams.add(new Bitstream(dspaceBitstreams.get(i), servletContext, expand, context)); - writeStats(dspaceBitstreams.get(i), UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - } - } - } - - context.complete(); - log.trace("Bitstreams were successfully read."); - - } catch (SQLException e) { - processException("Something went wrong while reading bitstreams from database!. Message: " + e, context); - } catch (ContextException e) { - processException( - "Something went wrong while reading bitstreams, ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - return bitstreams.toArray(new Bitstream[0]); - } - - /** - * Read bitstream data. May throw WebApplicationException with the - * INTERNAL_SERVER_ERROR(500) code. Caused by three exceptions: IOException if - * there was a problem with reading bitstream file. SQLException if there was - * a problem while reading from database. And AuthorizeException if there was - * a problem with authorization of user logged to DSpace context. - * - * @param bitstreamId Id of the bitstream, whose data will be read. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return Returns response with data with file content type. It can - * return the NOT_FOUND(404) response code in case of wrong bitstream - * id. Or response code UNAUTHORIZED(401) if user is not - * allowed to read bitstream. - * @throws WebApplicationException Thrown if there was a problem: reading the file data; or reading - * the database; or creating the context; or with authorization. - */ - @GET - @Path("/{bitstream_id}/retrieve") - public javax.ws.rs.core.Response getBitstreamData(@PathParam("bitstream_id") String bitstreamId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading data of bitstream(id=" + bitstreamId + ")."); - org.dspace.core.Context context = null; - InputStream inputStream = null; - String type = null; - String name = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.READ); - - writeStats(dspaceBitstream, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, - request, context); - - log.trace("Bitstream(id=" + bitstreamId + ") data was successfully read."); - inputStream = bitstreamService.retrieve(context, dspaceBitstream); - type = dspaceBitstream.getFormat(context).getMIMEType(); - name = dspaceBitstream.getName(); - - context.complete(); - } catch (IOException e) { - processException("Could not read file of bitstream(id=" + bitstreamId + ")! Message: " + e, context); - } catch (SQLException e) { - processException( - "Something went wrong while reading bitstream(id=" + bitstreamId + ") from database! Message: " + e, - context); - } catch (AuthorizeException e) { - processException( - "Could not retrieve file of bitstream(id=" + bitstreamId + "), AuthorizeException! Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not retrieve file of bitstream(id=" + bitstreamId + "), ContextException! Message: " + e - .getMessage(), - context); - } finally { - processFinally(context); - } - - return Response.ok(inputStream).type(type) - .header("Content-Disposition", "attachment; filename=\"" + name + "\"") - .build(); - } - - /** - * Add bitstream policy to all bundles containing the bitstream. - * - * @param bitstreamId Id of bitstream in DSpace. - * @param policy Policy to be added. The following attributes are not - * applied: epersonId, - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return Returns ok, if all was ok. Otherwise status code 500. - */ - @POST - @Path("/{bitstream_id}/policy") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public javax.ws.rs.core.Response addBitstreamPolicy(@PathParam("bitstream_id") String bitstreamId, - ResourcePolicy policy, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Adding bitstream(id=" + bitstreamId + ") " + policy - .getAction() + " policy with permission for group(id=" + policy.getGroupId() - + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, - request, context); - - addPolicyToBitstream(context, policy, dspaceBitstream); - - context.complete(); - log.trace("Policy for bitstream(id=" + bitstreamId + ") was successfully added."); - - } catch (SQLException e) { - processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId - + "), SQLException! Message: " + e, context); - } catch (ContextException e) { - processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId - + "), ContextException. Message: " + e.getMessage(), context); - } catch (AuthorizeException e) { - processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId - + "), AuthorizeException! Message: " + e, context); - } finally { - processFinally(context); - } - return Response.status(Status.OK).build(); - } - - /** - * Update bitstream metadata. Replaces everything on targeted bitstream. - * May throw WebApplicationException caused by two exceptions: - * SQLException, if there was a problem with the database. AuthorizeException if - * there was a problem with the authorization to edit bitstream metadata. - * - * @param bitstreamId Id of bistream to be updated. - * @param bitstream Bitstream with will be placed. It must have filled user - * credentials. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return Return response codes: OK(200), NOT_FOUND(404) if bitstream does - * not exist and UNAUTHORIZED(401) if user is not allowed to write - * to bitstream. - * @throws WebApplicationException Thrown when: Error reading from database; or error - * creating context; or error regarding bitstream authorization. - */ - @PUT - @Path("/{bitstream_id}") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response updateBitstream(@PathParam("bitstream_id") String bitstreamId, Bitstream bitstream, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Updating bitstream(id=" + bitstreamId + ") metadata."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - log.trace("Updating bitstream metadata."); - - dspaceBitstream.setDescription(context, bitstream.getDescription()); - if (getMimeType(bitstream.getName()) == null) { - BitstreamFormat unknownFormat = bitstreamFormatService.findUnknown(context); - bitstreamService.setFormat(context, dspaceBitstream, unknownFormat); - } else { - BitstreamFormat guessedFormat = bitstreamFormatService - .findByMIMEType(context, getMimeType(bitstream.getName())); - bitstreamService.setFormat(context, dspaceBitstream, guessedFormat); - } - dspaceBitstream.setName(context, bitstream.getName()); - Integer sequenceId = bitstream.getSequenceId(); - if (sequenceId != null && sequenceId.intValue() != -1) { - dspaceBitstream.setSequenceID(sequenceId); - } - - bitstreamService.update(context, dspaceBitstream); - - if (bitstream.getPolicies() != null) { - log.trace("Updating bitstream policies."); - - // Remove all old bitstream policies. - authorizeService.removeAllPolicies(context, dspaceBitstream); - - // Add all new bitstream policies - for (ResourcePolicy policy : bitstream.getPolicies()) { - addPolicyToBitstream(context, policy, dspaceBitstream); - } - } - - context.complete(); - - } catch (SQLException e) { - processException("Could not update bitstream(id=" + bitstreamId + ") metadata, SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException( - "Could not update bitstream(id=" + bitstreamId + ") metadata, AuthorizeException. Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not update bitstream(id=" + bitstreamId + ") metadata, ContextException. Message: " + e - .getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Bitstream metadata(id=" + bitstreamId + ") were successfully updated."); - return Response.ok().build(); - } - - /** - * Update bitstream data. Changes bitstream data by editing database rows. - * May throw WebApplicationException caused by: SQLException if there was - * a problem editing or reading the database, IOException if there was - * a problem with reading from InputStream, Exception if there was another - * problem. - * - * @param bitstreamId Id of bistream to be updated. - * @param is InputStream filled with new data. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return Return response if bitstream was updated. Response codes: - * OK(200), NOT_FOUND(404) if id of bitstream was bad. And - * UNAUTHORIZED(401) if user is not allowed to update bitstream. - * @throws WebApplicationException This exception can be thrown in this cases: Problem with - * reading or writing to database. Or problem with reading from - * InputStream. - */ - // TODO Change to better logic, without editing database. - @PUT - @Path("/{bitstream_id}/data") - public Response updateBitstreamData(@PathParam("bitstream_id") String bitstreamId, InputStream is, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Updating bitstream(id=" + bitstreamId + ") data."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - log.trace("Creating new bitstream."); - - UUID newBitstreamId = bitstreamStorageService.store(context, dspaceBitstream, is); - log.trace("Bitstream data stored: " + newBitstreamId); - context.complete(); - } catch (SQLException e) { - processException("Could not update bitstream(id=" + bitstreamId + ") data, SQLException. Message: " + e, - context); - } catch (IOException e) { - processException("Could not update bitstream(id=" + bitstreamId + ") data, IOException. Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not update bitstream(id=" + bitstreamId + ") data, ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Bitstream(id=" + bitstreamId + ") data was successfully updated."); - return Response.ok().build(); - } - - /** - * Delete bitstream from all bundles in DSpace. May throw - * WebApplicationException, which can be caused by three exceptions. - * SQLException if there was a problem reading from database or removing - * from database. AuthorizeException, if user doesn't have permission to delete - * the bitstream or file. IOException, if there was a problem deleting the file. - * - * @param bitstreamId Id of bitstream to be deleted. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return Return response codes: OK(200), NOT_FOUND(404) if bitstream of - * that id does not exist and UNAUTHORIZED(401) if user is not - * allowed to delete bitstream. - * @throws WebApplicationException Can be thrown if there was a problem reading or editing - * the database. Or problem deleting the file. Or problem with - * authorization to bitstream and bundles. Or problem with - * creating context. - */ - @DELETE - @Path("/{bitstream_id}") - public Response deleteBitstream(@PathParam("bitstream_id") String bitstreamId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting bitstream(id=" + bitstreamId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.DELETE); - - writeStats(dspaceBitstream, UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - log.trace("Deleting bitstream from all bundles."); - bitstreamService.delete(context, dspaceBitstream); - - context.complete(); - } catch (SQLException e) { - processException("Could not delete bitstream(id=" + bitstreamId + "), SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException("Could not delete bitstream(id=" + bitstreamId + "), AuthorizeException. Message: " + e, - context); - } catch (IOException e) { - processException("Could not delete bitstream(id=" + bitstreamId + "), IOException. Message: " + e, context); - } catch (ContextException e) { - processException( - "Could not delete bitstream(id=" + bitstreamId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Bitstream(id=" + bitstreamId + ") was successfully deleted."); - return Response.ok().build(); - } - - /** - * Delete policy. - * - * @param bitstreamId Id of the DSpace bitstream whose policy will be deleted. - * @param policyId Id of the policy to delete. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @return It returns Ok, if all was ok. Otherwise status code 500. - */ - @DELETE - @Path("/{bitstream_id}/policy/{policy_id}") - public javax.ws.rs.core.Response deleteBitstreamPolicy(@PathParam("bitstream_id") String bitstreamId, - @PathParam("policy_id") Integer policyId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - log.info("Deleting policy(id=" + policyId + ") from bitstream(id=" + bitstreamId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, - request, context); - - org.dspace.authorize.ResourcePolicy resourcePolicy = resourcePolicyService.find(context, policyId); - if (resourcePolicy.getdSpaceObject().getID().equals(dspaceBitstream.getID()) && authorizeService - .authorizeActionBoolean(context, dspaceBitstream, org.dspace.core.Constants.REMOVE)) { - - try { - resourcePolicyService.delete(context, resourcePolicy); - } catch (AuthorizeException e) { - processException( - "Someting went wrong while deleting policy(id=" + policyId + ") to bitstream(id=" + bitstreamId - + "), AuthorizeException! Message: " + e, context); - } - log.trace("Policy for bitstream(id=" + bitstreamId + ") was successfully removed."); - } - - context.complete(); - } catch (SQLException e) { - processException( - "Someting went wrong while deleting policy(id=" + policyId + ") to bitstream(id=" + bitstreamId - + "), SQLException! Message: " + e, context); - } catch (ContextException e) { - processException( - "Someting went wrong while deleting policy(id=" + policyId + ") to bitstream(id=" + bitstreamId - + "), ContextException. Message: " + e.getMessage(), context); - } finally { - processFinally(context); - } - - return Response.status(Status.OK).build(); - } - - /** - * Return the MIME type of the file, by file extension. - * - * @param name Name of file. - * @return String filled with type of file in MIME style. - */ - static String getMimeType(String name) { - return URLConnection.guessContentTypeFromName(name); - } - - /** - * Add policy(org.dspace.rest.common.ResourcePolicy) to bitstream. - * - * @param context Context to create DSpace ResourcePolicy. - * @param policy Policy which will be added to bitstream. - * @param dspaceBitstream DSpace Bitstream object. - * @throws SQLException An exception that provides information on a database access error or other errors. - * @throws AuthorizeException Exception indicating the current user of the context does not have permission - * to perform a particular action. - */ - private void addPolicyToBitstream(org.dspace.core.Context context, ResourcePolicy policy, - org.dspace.content.Bitstream dspaceBitstream) - throws SQLException, AuthorizeException { - org.dspace.authorize.ResourcePolicy dspacePolicy = resourcePolicyService.create(context); - dspacePolicy.setAction(policy.getActionInt()); - dspacePolicy.setGroup(groupService.findByIdOrLegacyId(context, policy.getGroupId())); - dspacePolicy.setdSpaceObject(dspaceBitstream); - dspacePolicy.setStartDate(policy.getStartDate()); - dspacePolicy.setEndDate(policy.getEndDate()); - dspacePolicy.setRpDescription(policy.getRpDescription()); - dspacePolicy.setRpName(policy.getRpName()); - - resourcePolicyService.update(context, dspacePolicy); - } - - /** - * Find bitstream from DSpace database. This encapsulates the - * org.dspace.content.Bitstream.find method with a check whether the item exists and - * whether the user logged into the context has permission to preform the requested action. - * - * @param context Context of actual logged user. - * @param id Id of bitstream in DSpace. - * @param action Constant from org.dspace.core.Constants. - * @return Returns DSpace bitstream. - * @throws WebApplicationException Is thrown when item with passed id is not exists and if user - * has no permission to do passed action. - */ - private org.dspace.content.Bitstream findBitstream(org.dspace.core.Context context, String id, int action) - throws WebApplicationException { - org.dspace.content.Bitstream bitstream = null; - try { - bitstream = bitstreamService.findByIdOrLegacyId(context, id); - - if ((bitstream == null) || (bitstreamService.getParentObject(context, bitstream) == null)) { - context.abort(); - log.warn("Bitstream(id=" + id + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } else if (!authorizeService.authorizeActionBoolean(context, bitstream, action)) { - context.abort(); - if (context.getCurrentUser() != null) { - log.error("User(" + context.getCurrentUser().getEmail() + ") doesn't have the permission to " - + getActionString(action) + " bitstream!"); - } else { - log.error( - "User(anonymous) doesn't have the permission to " + getActionString(action) + " bitsteam!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - } catch (SQLException e) { - processException("Something went wrong while finding bitstream. SQLException, Message:" + e, context); - } - return bitstream; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java b/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java deleted file mode 100644 index 395a0af766..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java +++ /dev/null @@ -1,755 +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.rest; - -import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_LICENSE; -import static org.dspace.content.service.DSpaceObjectService.MD_NAME; -import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION; -import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.ItemService; -import org.dspace.content.service.WorkspaceItemService; -import org.dspace.core.Constants; -import org.dspace.core.LogHelper; -import org.dspace.rest.common.Collection; -import org.dspace.rest.common.Item; -import org.dspace.rest.common.MetadataEntry; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.usage.UsageEvent; -import org.dspace.workflow.WorkflowService; -import org.dspace.workflow.factory.WorkflowServiceFactory; - -/** - * This class provides all CRUD operation over collections. - * - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -@Path("/collections") -public class CollectionsResource extends Resource { - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); - protected WorkflowService workflowService = WorkflowServiceFactory.getInstance().getWorkflowService(); - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(CollectionsResource.class); - - /** - * Return instance of collection with passed id. You can add more properties - * through expand parameter. - * - * @param collectionId Id of collection in DSpace. - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "items", "license" and "logo". If you want - * to use multiple options, it must be separated by commas. - * @param limit Limit value for items in list in collection. Default value is - * 100. - * @param offset Offset of start index in list of items of collection. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of collection. It can also return status code - * NOT_FOUND(404) if id of collection is incorrect or status code - * UNATHORIZED(401) if user has no permission to read collection. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). It is thrown by NOT_FOUND and - * UNATHORIZED status codes, too. - */ - @GET - @Path("/{collection_id}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.Collection getCollection(@PathParam("collection_id") String collectionId, - @QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading collection(id=" + collectionId + ")."); - org.dspace.core.Context context = null; - Collection collection = null; - - try { - context = createContext(); - - org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, - org.dspace.core.Constants.READ); - writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, - headers, request, context); - - collection = new Collection(dspaceCollection, servletContext, expand, context, limit, offset); - context.complete(); - - } catch (SQLException e) { - processException("Could not read collection(id=" + collectionId + "), SQLException. Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not read collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("Collection(id=" + collectionId + ") has been successfully read."); - return collection; - } - - /** - * Return array of all collections in DSpace. You can add more properties - * through expand parameter. - * - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "items", "license" and "logo". If you want - * to use multiple options, it must be separated by commas. - * @param limit Limit value for items in list in collection. Default value is - * 100. - * @param offset Offset of start index in list of items of collection. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collections as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of collection, on which has logged user permission - * to view. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.Collection[] getCollections(@QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading all collections.(offset=" + offset + ",limit=" + limit + ")"); - org.dspace.core.Context context = null; - List collections = new ArrayList<>(); - - try { - context = createContext(); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Paging was badly set."); - limit = 100; - offset = 0; - } - - List dspaceCollections = collectionService.findAll(context, limit, offset); - for (org.dspace.content.Collection dspaceCollection : dspaceCollections) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) { - Collection collection = new org.dspace.rest.common.Collection(dspaceCollection, servletContext, - null, context, limit, - offset); - collections.add(collection); - writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - } - } - context.complete(); - } catch (SQLException e) { - processException("Something went wrong while reading collections from database. Message: " + e, context); - } catch (ContextException e) { - processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("All collections were successfully read."); - return collections.toArray(new org.dspace.rest.common.Collection[0]); - } - - /** - * Return array of items in collection. You can add more properties to items - * with expand parameter. - * - * @param collectionId Id of collection in DSpace. - * @param expand String which define, what additional properties will be in - * returned item. Options are separeted by commas and are: "all", - * "metadata", "parentCollection", "parentCollectionList", - * "parentCommunityList" and "bitstreams". - * @param limit Limit value for items in array. Default value is 100. - * @param offset Offset of start index in array of items of collection. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of items, on which has logged user permission to - * read. It can also return status code NOT_FOUND(404) if id of - * collection is incorrect or status code UNATHORIZED(401) if user - * has no permission to read collection. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). It is thrown by NOT_FOUND and - * UNATHORIZED status codes, too. - */ - @GET - @Path("/{collection_id}/items") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.Item[] getCollectionItems(@PathParam("collection_id") String collectionId, - @QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading collection(id=" + collectionId + ") items."); - org.dspace.core.Context context = null; - List items = null; - - try { - context = createContext(); - - org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, - org.dspace.core.Constants.READ); - writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, - headers, request, context); - - items = new ArrayList<>(); - Iterator dspaceItems = itemService.findByCollection(context, dspaceCollection, - limit, offset); - - while (dspaceItems.hasNext()) { - org.dspace.content.Item dspaceItem = dspaceItems.next(); - - if (itemService.isItemListedForUser(context, dspaceItem)) { - items.add(new Item(dspaceItem, servletContext, expand, context)); - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, - headers, request, context); - } - } - - context.complete(); - } catch (SQLException e) { - processException("Could not read collection items, SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not read collection items, ContextException. Message: " + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("All items in collection(id=" + collectionId + ") were successfully read."); - return items.toArray(new Item[0]); - } - - /** - * Create item in collection. Item can be without filled metadata. - * - * @param collectionId Id of collection in which will be item created. - * @param item Item filled only with metadata, other variables are ignored. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return status code with item. Return status (OK)200 if item was - * created. NOT_FOUND(404) if id of collection does not exists. - * UNAUTHORIZED(401) if user have not permission to write items in - * collection. - * @throws WebApplicationException It is thrown when was problem with database reading or - * writing (SQLException) or problem with creating - * context(ContextException) or problem with authorization to - * collection or IOException or problem with index item into - * browse index. It is thrown by NOT_FOUND and UNATHORIZED - * status codes, too. - */ - @POST - @Path("/{collection_id}/items") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Item addCollectionItem(@PathParam("collection_id") String collectionId, Item item, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Create item in collection(id=" + collectionId + ")."); - org.dspace.core.Context context = null; - Item returnItem = null; - - try { - context = createContext(); - org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - log.trace("Creating item in collection(id=" + collectionId + ")."); - org.dspace.content.WorkspaceItem workspaceItem = workspaceItemService - .create(context, dspaceCollection, false); - org.dspace.content.Item dspaceItem = workspaceItem.getItem(); - - log.trace("Adding metadata to item(id=" + dspaceItem.getID() + ")."); - if (item.getMetadata() != null) { - for (MetadataEntry entry : item.getMetadata()) { - String data[] = mySplit(entry.getKey()); - itemService.addMetadata(context, dspaceItem, data[0], data[1], data[2], entry.getLanguage(), - entry.getValue()); - } - } - - workspaceItemService.update(context, workspaceItem); - - try { - // Must insert the item into workflow - log.trace("Starting workflow for item(id=" + dspaceItem.getID() + ")."); - workflowService.start(context, workspaceItem); - } catch (Exception e) { - log.error( - LogHelper.getHeader(context, "Error while starting workflow", - "Item id: " + dspaceItem.getID()), - e); - throw new ContextException("Error while starting workflow for item(id=" + dspaceItem.getID() + ")", e); - } - - returnItem = new Item(workspaceItem.getItem(), servletContext, "", context); - - context.complete(); - - } catch (SQLException e) { - processException("Could not add item into collection(id=" + collectionId + "), SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException( - "Could not add item into collection(id=" + collectionId + "), AuthorizeException. Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not add item into collection(id=" + collectionId + "), ContextException. Message: " + e - .getMessage(), - context); - } finally { - processFinally(context); - } - - log.info( - "Item successfully created in collection(id=" + collectionId + "). Item handle=" + returnItem.getHandle()); - return returnItem; - } - - /** - * Update collection. It replace all properties. - * - * @param collectionId Id of collection in DSpace. - * @param collection Collection which will replace properties of actual collection. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response 200 if was everything all right. Otherwise 400 - * when id of community was incorrect or 401 if was problem with - * permission to write into collection. - * @throws WebApplicationException It is thrown when was problem with database reading or - * writing. Or problem with authorization to collection. Or - * problem with creating context. - */ - @PUT - @Path("/{collection_id}") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response updateCollection(@PathParam("collection_id") String collectionId, - org.dspace.rest.common.Collection collection, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Updating collection(id=" + collectionId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_NAME, collection.getName(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_LICENSE, collection.getLicense(), null); - - // dspaceCollection.setLogo(collection.getLogo()); // TODO Add this option. - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_COPYRIGHT_TEXT, collection.getCopyrightText(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_INTRODUCTORY_TEXT, collection.getIntroductoryText(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_SHORT_DESCRIPTION, collection.getShortDescription(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_SIDEBAR_TEXT, collection.getSidebarText(), null); - collectionService.update(context, dspaceCollection); - - context.complete(); - - } catch (ContextException e) { - processException( - "Could not update collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(), - context); - } catch (SQLException e) { - processException("Could not update collection(id=" + collectionId + "), SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException("Could not update collection(id=" + collectionId + "), AuthorizeException. Message: " + e, - context); - } finally { - processFinally(context); - } - - log.info("Collection(id=" + collectionId + ") successfully updated."); - return Response.ok().build(); - } - - /** - * Delete collection. - * - * @param collectionId Id of collection which will be deleted. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response code OK(200) if was everything all right. - * Otherwise return NOT_FOUND(404) if was id of community or - * collection incorrect. Or (UNAUTHORIZED)401 if was problem with - * permission to community or collection. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or writing. Or problem with deleting - * collection caused by IOException or authorization. - */ - @DELETE - @Path("/{collection_id}") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response deleteCollection(@PathParam("collection_id") String collectionId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Delete collection(id=" + collectionId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId, - org.dspace.core.Constants.DELETE); - - writeStats(dspaceCollection, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - collectionService.delete(context, dspaceCollection); - collectionService.update(context, dspaceCollection); - - context.complete(); - } catch (ContextException e) { - processException( - "Could not delete collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(), - context); - } catch (SQLException e) { - processException("Could not delete collection(id=" + collectionId + "), SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException("Could not delete collection(id=" + collectionId + "), AuthorizeException. Message: " + e, - context); - } catch (IOException e) { - processException("Could not delete collection(id=" + collectionId + "), IOException. Message: " + e, - context); - } finally { - processFinally(context); - } - - log.info("Collection(id=" + collectionId + ") was successfully deleted."); - return Response.ok().build(); - } - - /** - * Delete item in collection. - * - * @param collectionId Id of collection which will be deleted. - * @param itemId Id of item in colletion. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return It returns status code: OK(200). NOT_FOUND(404) if item or - * collection was not found, UNAUTHORIZED(401) if user is not - * allowed to delete item or permission to write into collection. - * @throws WebApplicationException It can be thrown by: SQLException, when was problem with - * database reading or writting. AuthorizeException, when was - * problem with authorization to item or collection. - * IOException, when was problem with removing item. - * ContextException, when was problem with creating context of - * DSpace. - */ - @DELETE - @Path("/{collection_id}/items/{item_id}") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response deleteCollectionItem(@PathParam("collection_id") String collectionId, - @PathParam("item_id") String itemId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Delete item(id=" + itemId + ") in collection(id=" + collectionId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.Collection dspaceCollection = collectionService - .findByIdOrLegacyId(context, collectionId); - org.dspace.content.Item item = itemService.findByIdOrLegacyId(context, itemId); - - - if (dspaceCollection == null) { - //throw collection not exist - log.warn("Collection(id=" + itemId + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - - if (item == null) { - //throw item not exist - log.warn("Item(id=" + itemId + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - - if (!authorizeService.authorizeActionBoolean(context, item, Constants.REMOVE) - || !authorizeService.authorizeActionBoolean(context, dspaceCollection, Constants.REMOVE)) { - //throw auth - if (context.getCurrentUser() != null) { - log.error( - "User(" + context.getCurrentUser().getEmail() + ") does not have permission to delete item!"); - } else { - log.error("User(anonymous) has not permission to delete item!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - collectionService.removeItem(context, dspaceCollection, item); - collectionService.update(context, dspaceCollection); - itemService.update(context, item); - - writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - writeStats(item, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor, headers, request, context); - - context.complete(); - - } catch (ContextException e) { - processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId - + "), ContextException. Message: " + e.getMessage(), context); - } catch (SQLException e) { - processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId - + "), SQLException. Message: " + e, context); - } catch (AuthorizeException e) { - processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId - + "), AuthorizeException. Message: " + e, context); - } catch (IOException e) { - processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId - + "), IOException. Message: " + e, context); - } finally { - processFinally(context); - } - - log.info("Item(id=" + itemId + ") in collection(id=" + collectionId + ") was successfully deleted."); - return Response.ok().build(); - } - - /** - * Search for first collection with passed name. - * - * @param name Name of collection. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @return It returns null if collection was not found. Otherwise returns - * first founded collection. - * @throws WebApplicationException A general exception a servlet can throw when it encounters difficulty. - */ - @POST - @Path("/find-collection") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Collection findCollectionByName(String name, @Context HttpHeaders headers) throws WebApplicationException { - log.info("Searching for first collection with name=" + name + "."); - org.dspace.core.Context context = null; - Collection collection = null; - - try { - context = createContext(); - - List dspaceCollections = collectionService.findAll(context); - //TODO, this would be more efficient with a findByName query - - for (org.dspace.content.Collection dspaceCollection : dspaceCollections) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) { - if (dspaceCollection.getName().equals(name)) { - collection = new Collection(dspaceCollection, servletContext, "", context, 100, 0); - break; - } - } - } - - context.complete(); - - } catch (SQLException e) { - processException( - "Something went wrong while searching for collection(name=" + name + ") from database. Message: " - + e, context); - } catch (ContextException e) { - processException( - "Something went wrong while searching for collection(name=" + name + "), ContextError. Message: " - + e.getMessage(), context); - } finally { - processFinally(context); - } - - if (collection == null) { - log.info("Collection was not found."); - } else { - log.info("Collection was found with id(" + collection.getUUID() + ")."); - } - return collection; - } - - /** - * Find collection from DSpace database. It is encapsulation of method - * org.dspace.content.Collection.find with checking if item exist and if - * user logged into context has permission to do passed action. - * - * @param context Context of actual logged user. - * @param id Id of collection in DSpace. - * @param action Constant from org.dspace.core.Constants. - * @return It returns DSpace collection. - * @throws WebApplicationException Is thrown when item with passed id is not exists and if user - * has no permission to do passed action. - */ - private org.dspace.content.Collection findCollection(org.dspace.core.Context context, String id, int action) - throws WebApplicationException { - org.dspace.content.Collection collection = null; - try { - collection = collectionService.findByIdOrLegacyId(context, id); - - if (collection == null) { - context.abort(); - log.warn("Collection(id=" + id + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } else if (!authorizeService.authorizeActionBoolean(context, collection, action)) { - context.abort(); - if (context.getCurrentUser() != null) { - log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to " - + getActionString(action) + " collection!"); - } else { - log.error("User(anonymous) has not permission to " + getActionString(action) + " collection!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - } catch (SQLException e) { - processException("Something get wrong while finding collection(id=" + id + "). SQLException, Message: " + e, - context); - } - return collection; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/CommunitiesResource.java b/dspace-rest/src/main/java/org/dspace/rest/CommunitiesResource.java deleted file mode 100644 index c3d4840910..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/CommunitiesResource.java +++ /dev/null @@ -1,1052 +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.rest; - -import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_LICENSE; -import static org.dspace.content.service.DSpaceObjectService.MD_NAME; -import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION; -import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; -import org.dspace.rest.common.Collection; -import org.dspace.rest.common.Community; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.usage.UsageEvent; - -/** - * Class which provides CRUD methods over communities. - * - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -@Path("/communities") -public class CommunitiesResource extends Resource { - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(CommunitiesResource.class); - - /** - * Returns community with basic properties. If you want more, use expand - * parameter or method for community collections or subcommunities. - * - * @param communityId Id of community in DSpace. - * @param expand String in which is what you want to add to returned instance - * of community. Options are: "all", "parentCommunity", - * "collections", "subCommunities" and "logo". If you want to use - * multiple options, it must be separated by commas. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of org.dspace.rest.common.Community. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading. Also if id of community is incorrect - * or logged user into context has no permission to read. - */ - @GET - @Path("/{community_id}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Community getCommunity(@PathParam("community_id") String communityId, @QueryParam("expand") String expand, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading community(id=" + communityId + ")."); - org.dspace.core.Context context = null; - Community community = null; - - try { - context = createContext(); - - org.dspace.content.Community dspaceCommunity = findCommunity(context, communityId, - org.dspace.core.Constants.READ); - writeStats(dspaceCommunity, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, - request, context); - - community = new Community(dspaceCommunity, servletContext, expand, context); - context.complete(); - - } catch (SQLException e) { - processException("Could not read community(id=" + communityId + "), SQLException. Message:" + e, context); - } catch (ContextException e) { - processException( - "Could not read community(id=" + communityId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - - log.trace("Community(id=" + communityId + ") was successfully read."); - return community; - } - - /** - * Return all communities in DSpace. - * - * @param expand String in which is what you want to add to returned instance - * of community. Options are: "all", "parentCommunity", - * "collections", "subCommunities" and "logo". If you want to use - * multiple options, it must be separated by commas. - * @param limit Maximum communities in array. Default value is 100. - * @param offset Index from which will start array of communities. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of communities. - * @throws WebApplicationException It can be caused by creating context or while was problem - * with reading community from database(SQLException). - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Community[] getCommunities(@QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading all communities.(offset=" + offset + " ,limit=" + limit + ")."); - org.dspace.core.Context context = null; - ArrayList communities = null; - - try { - context = createContext(); - - List dspaceCommunities = communityService.findAll(context); - communities = new ArrayList<>(); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Paging was badly set, using default values."); - limit = 100; - offset = 0; - } - - for (int i = offset; (i < (offset + limit)) && i < dspaceCommunities.size(); i++) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCommunities.get(i), org.dspace.core.Constants.READ)) { - Community community = new Community(dspaceCommunities.get(i), servletContext, expand, context); - writeStats(dspaceCommunities.get(i), UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - communities.add(community); - } - } - - context.complete(); - } catch (SQLException e) { - processException("Could not read communities, SQLException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not read communities, ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("All communities successfully read."); - return communities.toArray(new Community[0]); - } - - /** - * Return all top communities in DSpace. Top communities are communities on - * the root of tree. - * - * @param expand String in which is what you want to add to returned instance - * of community. Options are: "all", "parentCommunity", - * "collections", "subCommunities" and "logo". If you want to use - * multiple options, it must be separated by commas. - * @param limit Maximum communities in array. Default value is 100. - * @param offset Index from which will start array of communities. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of top communities. - * @throws WebApplicationException It can be caused by creating context or while was problem - * with reading community from database(SQLException). - */ - @GET - @Path("/top-communities") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Community[] getTopCommunities(@QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("20") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading all top communities.(offset=" + offset + " ,limit=" + limit + ")."); - org.dspace.core.Context context = null; - ArrayList communities = null; - - try { - context = createContext(); - - List dspaceCommunities = communityService.findAllTop(context); - communities = new ArrayList<>(); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Paging was badly set, using default values."); - limit = 100; - offset = 0; - } - - for (int i = offset; (i < (offset + limit)) && i < dspaceCommunities.size(); i++) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCommunities.get(i), org.dspace.core.Constants.READ)) { - Community community = new Community(dspaceCommunities.get(i), servletContext, expand, context); - writeStats(dspaceCommunities.get(i), UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - communities.add(community); - } - } - - context.complete(); - } catch (SQLException e) { - processException("Could not read top communities, SQLException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not read top communities, ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("All top communities successfully read."); - return communities.toArray(new Community[0]); - } - - /** - * Return all collections of community. - * - * @param communityId Id of community in DSpace. - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "items", "license" and "logo". If you want - * to use multiple options, it must be separated by commas. - * @param limit Maximum collection in array. Default value is 100. - * @param offset Index from which will start array of collections. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of collections of community. - * @throws WebApplicationException It can be caused by creating context or while was problem - * with reading community from database(SQLException). - */ - @GET - @Path("/{community_id}/collections") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Collection[] getCommunityCollections(@PathParam("community_id") String communityId, - @QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading community(id=" + communityId + ") collections."); - org.dspace.core.Context context = null; - ArrayList collections = null; - - try { - context = createContext(); - - org.dspace.content.Community dspaceCommunity = findCommunity(context, communityId, - org.dspace.core.Constants.READ); - writeStats(dspaceCommunity, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, - request, context); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Pagging was badly set, using default values."); - limit = 100; - offset = 0; - } - - collections = new ArrayList<>(); - List dspaceCollections = dspaceCommunity.getCollections(); - for (int i = offset; (i < (offset + limit)) && (i < dspaceCollections.size()); i++) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCollections.get(i), org.dspace.core.Constants.READ)) { - collections.add(new Collection(dspaceCollections.get(i), servletContext, expand, context, 20, 0)); - writeStats(dspaceCollections.get(i), UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - } - } - - context.complete(); - } catch (SQLException e) { - processException("Could not read community(id=" + communityId + ") collections, SQLException. Message:" + e, - context); - } catch (ContextException e) { - processException( - "Could not read community(id=" + communityId + ") collections, ContextException. Message:" + e - .getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("Community(id=" + communityId + ") collections were successfully read."); - return collections.toArray(new Collection[0]); - } - - /** - * Return all subcommunities of community. - * - * @param communityId Id of community in DSpace. - * @param expand String in which is what you want to add to returned instance - * of community. Options are: "all", "parentCommunity", - * "collections", "subCommunities" and "logo". If you want to use - * multiple options, it must be separated by commas. - * @param limit Maximum communities in array. Default value is 20. - * @param offset Index from which will start array of communities. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of subcommunities of community. - * @throws WebApplicationException It can be caused by creating context or while was problem - * with reading community from database(SQLException). - */ - @GET - @Path("/{community_id}/communities") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Community[] getCommunityCommunities(@PathParam("community_id") String communityId, - @QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("20") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading community(id=" + communityId + ") subcommunities."); - org.dspace.core.Context context = null; - ArrayList communities = null; - - try { - context = createContext(); - - org.dspace.content.Community dspaceCommunity = findCommunity(context, communityId, - org.dspace.core.Constants.READ); - writeStats(dspaceCommunity, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, - request, context); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Pagging was badly set, using default values."); - limit = 100; - offset = 0; - } - - communities = new ArrayList<>(); - List dspaceCommunities = dspaceCommunity.getSubcommunities(); - for (int i = offset; (i < (offset + limit)) && (i < dspaceCommunities.size()); i++) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCommunities.get(i), org.dspace.core.Constants.READ)) { - communities.add(new Community(dspaceCommunities.get(i), servletContext, expand, context)); - writeStats(dspaceCommunities.get(i), UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - } - } - - context.complete(); - } catch (SQLException e) { - processException( - "Could not read community(id=" + communityId + ") subcommunities, SQLException. Message:" + e, - context); - } catch (ContextException e) { - processException( - "Could not read community(id=" + communityId + ") subcommunities, ContextException. Message:" - + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("Community(id=" + communityId + ") subcommunities were successfully read."); - return communities.toArray(new Community[0]); - } - - /** - * Create community at top level. Creating community at top level has - * permission only admin. - * - * @param community Community which will be created at top level of communities. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Returns response with handle of community, if was all ok. - * @throws WebApplicationException It can be thrown by SQLException, AuthorizeException and - * ContextException. - */ - @POST - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Community createCommunity(Community community, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Creating community at top level."); - org.dspace.core.Context context = null; - Community retCommunity = null; - - try { - context = createContext(); - if (!authorizeService.isAdmin(context)) { - context.abort(); - String user = "anonymous"; - if (context.getCurrentUser() != null) { - user = context.getCurrentUser().getEmail(); - } - log.error("User(" + user + ") has not permission to create community!"); - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - org.dspace.content.Community dspaceCommunity = communityService.create(null, context); - writeStats(dspaceCommunity, UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_NAME, community.getName(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_COPYRIGHT_TEXT, community.getCopyrightText(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_INTRODUCTORY_TEXT, community.getIntroductoryText(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_SHORT_DESCRIPTION, community.getShortDescription(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_SIDEBAR_TEXT, community.getSidebarText(), null); - communityService.update(context, dspaceCommunity); - - retCommunity = new Community(dspaceCommunity, servletContext, "", context); - context.complete(); - } catch (SQLException e) { - processException("Could not create new top community, SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not create new top community, ContextException. Message: " + e.getMessage(), - context); - } catch (AuthorizeException e) { - processException("Could not create new top community, AuthorizeException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - - log.info("Community at top level has been successfully created. Handle:" + retCommunity.getHandle()); - return retCommunity; - } - - /** - * Create collection in community. - * - * @param communityId Id of community in DSpace. - * @param collection Collection which will be added into community. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response 200 if was everything all right. Otherwise 400 - * when id of community was incorrect or 401 if was problem with - * permission to write into collection. - * @throws WebApplicationException It is thrown when was problem with database reading or - * writing. Or problem with authorization to community. Or - * problem with creating context. - */ - @POST - @Path("/{community_id}/collections") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Collection addCommunityCollection(@PathParam("community_id") String communityId, Collection collection, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Adding collection into community(id=" + communityId + ")."); - org.dspace.core.Context context = null; - Collection retCollection = null; - - try { - context = createContext(); - - org.dspace.content.Community dspaceCommunity = findCommunity(context, communityId, - org.dspace.core.Constants.WRITE); - writeStats(dspaceCommunity, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - org.dspace.content.Collection dspaceCollection = collectionService.create(context, dspaceCommunity); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_LICENSE, collection.getLicense(), null); - // dspaceCollection.setLogo(collection.getLogo()); // TODO Add this option. - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_NAME, collection.getName(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_COPYRIGHT_TEXT, collection.getCopyrightText(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_INTRODUCTORY_TEXT, collection.getIntroductoryText(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_SHORT_DESCRIPTION, collection.getShortDescription(), null); - collectionService.setMetadataSingleValue(context, dspaceCollection, - MD_SIDEBAR_TEXT, collection.getSidebarText(), null); - collectionService.update(context, dspaceCollection); - communityService.update(context, dspaceCommunity); - retCollection = new Collection(dspaceCollection, servletContext, "", context, 100, 0); - context.complete(); - - } catch (SQLException e) { - processException( - "Could not add collection into community(id=" + communityId + "), SQLException. Message:" + e, - context); - } catch (AuthorizeException e) { - processException( - "Could not add collection into community(id=" + communityId + "), AuthorizeException. Message:" + e, - context); - } catch (ContextException e) { - processException( - "Could not add collection into community(id=" + communityId + "), ContextException. Message:" - + e.getMessage(), context); - } finally { - processFinally(context); - } - - - log.info("Collection was successfully added into community(id=" + communityId + "). Collection handle=" - + retCollection.getHandle()); - return retCollection; - } - - /** - * Create subcommunity in community. - * - * @param communityId Id of community in DSpace, in which will be created - * subcommunity. - * @param community Community which will be added into community. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response 200 if was everything all right. Otherwise 400 - * when id of community was incorrect or 401 if was problem with - * permission to write into collection. - * @throws WebApplicationException It is thrown when was problem with database reading or - * writing. Or problem with authorization to community. Or - * problem with creating context. - */ - @POST - @Path("/{community_id}/communities") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Community addCommunityCommunity(@PathParam("community_id") String communityId, Community community, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Add subcommunity into community(id=" + communityId + ")."); - org.dspace.core.Context context = null; - Community retCommunity = null; - - try { - context = createContext(); - org.dspace.content.Community dspaceParentCommunity = findCommunity(context, communityId, - org.dspace.core.Constants.WRITE); - - writeStats(dspaceParentCommunity, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - org.dspace.content.Community dspaceCommunity = communityService - .createSubcommunity(context, dspaceParentCommunity); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_NAME, community.getName(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_COPYRIGHT_TEXT, community.getCopyrightText(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_INTRODUCTORY_TEXT, community.getIntroductoryText(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_SHORT_DESCRIPTION, community.getShortDescription(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_SIDEBAR_TEXT, community.getSidebarText(), null); - communityService.update(context, dspaceCommunity); - communityService.update(context, dspaceParentCommunity); - - retCommunity = new Community(dspaceCommunity, servletContext, "", context); - context.complete(); - - } catch (SQLException e) { - processException( - "Could not add subcommunity into community(id=" + communityId + "), SQLException. Message:" + e, - context); - } catch (AuthorizeException e) { - processException( - "Could not add subcommunity into community(id=" + communityId + "), AuthorizeException. Message:" - + e, context); - } catch (ContextException e) { - processException( - "Could not add subcommunity into community(id=" + communityId + "), ContextException. Message:" + e, - context); - } finally { - processFinally(context); - } - - - log.info("Subcommunity was successfully added in community(id=" + communityId + ")."); - return retCommunity; - } - - /** - * Update community. Replace all information about community except: id, - * handle and expandle items. - * - * @param communityId Id of community in DSpace. - * @param community Instance of community which will replace actual community in - * DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Response 200 if was all ok. Otherwise 400 if id was incorrect or - * 401 if logged user has no permission to delete community. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or writing. Or problem with writing to - * community caused by authorization. - */ - @PUT - @Path("/{community_id}") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response updateCommunity(@PathParam("community_id") String communityId, Community community, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Updating community(id=" + communityId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.Community dspaceCommunity = findCommunity(context, communityId, - org.dspace.core.Constants.WRITE); - writeStats(dspaceCommunity, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - // dspaceCommunity.setLogo(arg0); // TODO Add this option. - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_NAME, community.getName(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_COPYRIGHT_TEXT, community.getCopyrightText(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_INTRODUCTORY_TEXT, community.getIntroductoryText(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_SHORT_DESCRIPTION, community.getShortDescription(), null); - communityService.setMetadataSingleValue(context, dspaceCommunity, - MD_SIDEBAR_TEXT, community.getSidebarText(), null); - communityService.update(context, dspaceCommunity); - context.complete(); - - } catch (SQLException e) { - processException("Could not update community(id=" + communityId + "), AuthorizeException. Message:" + e, - context); - } catch (ContextException e) { - processException("Could not update community(id=" + communityId + "), ContextException Message:" + e, - context); - } catch (AuthorizeException e) { - processException("Could not update community(id=" + communityId + "), AuthorizeException Message:" + e, - context); - } finally { - processFinally(context); - } - - log.info("Community(id=" + communityId + ") has been successfully updated."); - return Response.ok().build(); - } - - /** - * Delete community from DSpace. It delete it everything with community! - * - * @param communityId Id of community in DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response code OK(200) if was everything all right. - * Otherwise return NOT_FOUND(404) if was id of community incorrect. - * Or (UNAUTHORIZED)401 if was problem with permission to community. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or deleting. Or problem with deleting - * community caused by IOException or authorization. - */ - @DELETE - @Path("/{community_id}") - public Response deleteCommunity(@PathParam("community_id") String communityId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting community(id=" + communityId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.Community community = findCommunity(context, communityId, - org.dspace.core.Constants.DELETE); - writeStats(community, UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, headers, - request, context); - - communityService.delete(context, community); - communityService.update(context, community); - context.complete(); - - } catch (SQLException e) { - processException("Could not delete community(id=" + communityId + "), SQLException. Message:" + e, context); - } catch (AuthorizeException e) { - processException("Could not delete community(id=" + communityId + "), AuthorizeException. Message:" + e, - context); - } catch (IOException e) { - processException("Could not delete community(id=" + communityId + "), IOException. Message:" + e, context); - } catch (ContextException e) { - processException( - "Could not delete community(id=" + communityId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - - log.info("Community(id=" + communityId + ") was successfully deleted."); - return Response.status(Response.Status.OK).build(); - } - - /** - * Delete collection in community. - * - * @param communityId Id of community in DSpace. - * @param collectionId Id of collection which will be deleted. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response code OK(200) if was everything all right. - * Otherwise return NOT_FOUND(404) if was id of community or - * collection incorrect. Or (UNAUTHORIZED)401 if was problem with - * permission to community or collection. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or deleting. Or problem with deleting - * collection caused by IOException or authorization. - */ - @DELETE - @Path("/{community_id}/collections/{collection_id}") - public Response deleteCommunityCollection(@PathParam("community_id") String communityId, - @PathParam("collection_id") String collectionId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting collection(id=" + collectionId + ") in community(id=" + communityId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.Community community = findCommunity(context, communityId, - org.dspace.core.Constants.WRITE); - org.dspace.content.Collection collection = collectionService.findByIdOrLegacyId(context, collectionId); - - if (collection == null) { - context.abort(); - log.warn("Collection(id=" + collectionId + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } else if (!authorizeService - .authorizeActionBoolean(context, collection, org.dspace.core.Constants.REMOVE)) { - context.abort(); - if (context.getCurrentUser() != null) { - log.error( - "User(" + context.getCurrentUser().getEmail() + ") has not permission to delete collection!"); - } else { - log.error("User(anonymous) has not permission to delete collection!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - communityService.removeCollection(context, community, collection); - communityService.update(context, community); - collectionService.update(context, collection); - - writeStats(community, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, - request, context); - writeStats(collection, UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, headers, - request, context); - - context.complete(); - - } catch (SQLException e) { - processException("Could not delete collection(id=" + collectionId + ") in community(id=" + communityId - + "), SQLException. Message:" + e, context); - } catch (AuthorizeException e) { - processException("Could not delete collection(id=" + collectionId + ") in community(id=" + communityId - + "), AuthorizeException. Message:" + e, context); - } catch (IOException e) { - processException("Could not delete collection(id=" + collectionId + ") in community(id=" + communityId - + "), IOException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not delete collection(id=" + collectionId + ") in community(id=" + communityId - + "), ContextExcpetion. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - - log.info("Collection(id=" + collectionId + ") in community(id=" + communityId + ") was successfully deleted."); - return Response.status(Response.Status.OK).build(); - } - - /** - * Delete subcommunity in community. - * - * @param parentCommunityId Id of community in DSpace. - * @param subcommunityId Id of community which will be deleted. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response code OK(200) if was everything all right. - * Otherwise return NOT_FOUND(404) if was id of community or - * subcommunity incorrect. Or (UNAUTHORIZED)401 if was problem with - * permission to community or subcommunity. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or deleting. Or problem with deleting - * subcommunity caused by IOException or authorization. - */ - @DELETE - @Path("/{community_id}/communities/{community_id2}") - public Response deleteCommunityCommunity(@PathParam("community_id") String parentCommunityId, - @PathParam("community_id2") String subcommunityId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting community(id=" + parentCommunityId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.Community parentCommunity = findCommunity(context, parentCommunityId, - org.dspace.core.Constants.WRITE); - org.dspace.content.Community subcommunity = communityService.findByIdOrLegacyId(context, subcommunityId); - - if (subcommunity == null) { - context.abort(); - log.warn("Subcommunity(id=" + subcommunityId + ") in community(id=" + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } else if (!authorizeService - .authorizeActionBoolean(context, subcommunity, org.dspace.core.Constants.REMOVE)) { - context.abort(); - if (context.getCurrentUser() != null) { - log.error( - "User(" + context.getCurrentUser().getEmail() + ") has not permission to delete community!"); - } else { - log.error("User(anonymous) has not permission to delete community!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - communityService.removeSubcommunity(context, parentCommunity, subcommunity); - communityService.update(context, parentCommunity); - communityService.update(context, subcommunity); - - writeStats(parentCommunity, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - writeStats(subcommunity, UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, headers, - request, context); - - context.complete(); - - } catch (SQLException e) { - processException( - "Could not delete subcommunity(id=" + subcommunityId + ") in community(id=" + parentCommunityId - + "), SQLException. Message:" + e, context); - } catch (AuthorizeException e) { - processException( - "Could not delete subcommunity(id=" + subcommunityId + ") in community(id=" + parentCommunityId - + "), AuthorizeException. Message:" + e, context); - } catch (IOException e) { - processException( - "Could not delete subcommunity(id=" + subcommunityId + ") in community(id=" + parentCommunityId - + "), IOException. Message:" + e, context); - } catch (ContextException e) { - processException( - "Could not delete subcommunity(id=" + subcommunityId + ") in community(id=" + parentCommunityId - + "), ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - - log.info("Subcommunity(id=" + subcommunityId + ") from community(id=" + parentCommunityId - + ") was successfully deleted."); - return Response.status(Response.Status.OK).build(); - } - - /** - * Find community from DSpace database. It is encapsulation of method - * org.dspace.content.Community.find with checking if item exist and if user - * logged into context has permission to do passed action. - * - * @param context Context of actual logged user. - * @param id Id of community in DSpace. - * @param action Constant from org.dspace.core.Constants. - * @return It returns DSpace collection. - * @throws WebApplicationException Is thrown when item with passed id is not exists and if user - * has no permission to do passed action. - */ - private org.dspace.content.Community findCommunity(org.dspace.core.Context context, String id, int action) - throws WebApplicationException { - org.dspace.content.Community community = null; - try { - community = communityService.findByIdOrLegacyId(context, id); - - if (community == null) { - context.abort(); - log.warn("Community(id=" + id + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } else if (!authorizeService.authorizeActionBoolean(context, community, action)) { - context.abort(); - if (context.getCurrentUser() != null) { - log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to " - + getActionString(action) + " community!"); - } else { - log.error("User(anonymous) has not permission to " + getActionString(action) + " community!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - } catch (SQLException e) { - processException("Something get wrong while finding community(id=" + id + "). SQLException, Message:" + e, - context); - } - return community; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/DSpaceRestApplication.java b/dspace-rest/src/main/java/org/dspace/rest/DSpaceRestApplication.java deleted file mode 100644 index baa5c8555b..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/DSpaceRestApplication.java +++ /dev/null @@ -1,19 +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.rest; - -import org.glassfish.jersey.jackson.JacksonFeature; -import org.glassfish.jersey.server.ResourceConfig; - -public class DSpaceRestApplication extends ResourceConfig { - - public DSpaceRestApplication() { - register(JacksonFeature.class); - packages("org.dspace.rest"); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/FilteredCollectionsResource.java b/dspace-rest/src/main/java/org/dspace/rest/FilteredCollectionsResource.java deleted file mode 100644 index 133ed50d9c..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/FilteredCollectionsResource.java +++ /dev/null @@ -1,215 +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.rest; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.rest.common.FilteredCollection; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.dspace.usage.UsageEvent; - -/* - * This class provides the items within a collection evaluated against a set of Item Filters. - * - * @author Terry Brady, Georgetown University - */ -@Path("/filtered-collections") -public class FilteredCollectionsResource extends Resource { - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(FilteredCollectionsResource.class); - - /** - * Return array of all collections in DSpace. You can add more properties - * through expand parameter. - * - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "topCommunity", "items", "license" and "logo". - * If you want to use multiple options, it must be separated by commas. - * @param limit Limit value for items in list in collection. Default value is - * 100. - * @param offset Offset of start index in list of items of collection. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param filters Comma separated list of Item Filters to use to evaluate against - * the items in a collection - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param servletContext Context of the servlet container. - * @param headers If you want to access the collections as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of collection, on which has logged user permission - * to view. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.FilteredCollection[] getCollections(@QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") - Integer limit, - @QueryParam("offset") @DefaultValue("0") - Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("filters") @DefaultValue("is_item") - String filters, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context ServletContext servletContext, - @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading all filtered collections.(offset=" + offset + ",limit=" + limit + ")"); - org.dspace.core.Context context = null; - List collections = new ArrayList(); - - try { - context = createContext(); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Paging was badly set."); - limit = 100; - offset = 0; - } - - List dspaceCollections = collectionService.findAll(context, limit, offset); - for (org.dspace.content.Collection dspaceCollection : dspaceCollections) { - if (authorizeService - .authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) { - FilteredCollection collection = new org.dspace.rest.common.FilteredCollection(dspaceCollection, - servletContext, - filters, expand, - context, limit, - offset); - collections.add(collection); - writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, - xforwardedfor, headers, request, context); - } - } - context.complete(); - } catch (SQLException e) { - processException("Something went wrong while reading collections from database. Message: " + e, context); - } catch (ContextException e) { - processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("All collections were successfully read."); - return collections.toArray(new org.dspace.rest.common.FilteredCollection[0]); - } - - /** - * Return instance of collection with passed id. You can add more properties - * through expand parameter. - * - * @param collection_id Id of collection in DSpace. - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "topCommunity", "items", "license" and "logo". - * If you want to use multiple options, it must be separated by commas. - * @param limit Limit value for items in list in collection. Default value is - * 100. - * @param offset Offset of start index in list of items of collection. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param filters Comma separated list of Item Filters to use to evaluate against - * the items in a collection - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @param servletContext Context of the servlet container. - * @return Return instance of collection. It can also return status code - * NOT_FOUND(404) if id of collection is incorrect or status code - * UNATHORIZED(401) if user has no permission to read collection. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). It is thrown by NOT_FOUND and - * UNATHORIZED status codes, too. - */ - @GET - @Path("/{collection_id}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.FilteredCollection getCollection(@PathParam("collection_id") String collection_id, - @QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("1000") Integer - limit, - @QueryParam("offset") @DefaultValue("0") Integer - offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @QueryParam("filters") @DefaultValue("is_item") - String filters, - @Context HttpHeaders headers, - @Context HttpServletRequest request, - @Context ServletContext servletContext) { - org.dspace.core.Context context = null; - FilteredCollection retColl = new org.dspace.rest.common.FilteredCollection(); - try { - context = createContext(); - - org.dspace.content.Collection collection = collectionService.findByIdOrLegacyId(context, collection_id); - if (authorizeService.authorizeActionBoolean(context, collection, org.dspace.core.Constants.READ)) { - writeStats(collection, UsageEvent.Action.VIEW, user_ip, - user_agent, xforwardedfor, headers, request, context); - retColl = new org.dspace.rest.common.FilteredCollection( - collection, servletContext, filters, expand, context, limit, offset); - } else { - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - context.complete(); - } catch (SQLException e) { - processException(e.getMessage(), context); - } catch (ContextException e) { - processException(String.format("Could not read collection %s. %s", collection_id, e.getMessage()), - context); - } finally { - processFinally(context); - } - return retColl; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/FilteredItemsResource.java b/dspace-rest/src/main/java/org/dspace/rest/FilteredItemsResource.java deleted file mode 100644 index 0f4331adc5..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/FilteredItemsResource.java +++ /dev/null @@ -1,217 +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.rest; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; -import org.dspace.content.Item; -import org.dspace.content.MetadataField; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.ItemService; -import org.dspace.content.service.MetadataFieldService; -import org.dspace.content.service.MetadataSchemaService; -import org.dspace.content.service.SiteService; -import org.dspace.rest.common.ItemFilter; -import org.dspace.rest.common.ItemFilterQuery; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.rest.filter.ItemFilterSet; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.dspace.usage.UsageEvent; - -/* - * This class retrieves items by a constructed metadata query evaluated against a set of Item Filters. - * - * @author Terry Brady, Georgetown University - */ -@Path("/filtered-items") -public class FilteredItemsResource extends Resource { - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService(); - protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance() - .getMetadataSchemaService(); - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService(); - protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(FilteredItemsResource.class); - - /** - * Return instance of collection with passed id. You can add more properties - * through expand parameter. - * - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "items", "license" and "logo". If you want - * to use multiple options, it must be separated by commas. - * @param limit Limit value for items in list in collection. Default value is - * 100. - * @param offset Offset of start index in list of items of collection. Default - * value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param filters Comma separated list of Item Filters to use to evaluate against - * the items in a collection - * @param query_field List of metadata fields to evaluate in a metadata query. - * Each list value is used in conjunction with a query_op and query_field. - * @param query_op List of metadata operators to use in a metadata query. - * Each list value is used in conjunction with a query_field and query_field. - * @param query_val List of metadata values to evaluate in a metadata query. - * Each list value is used in conjunction with a query_value and query_op. - * @param collSel List of collections to query. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @param servletContext Context of the servlet container. - * @return Return instance of collection. It can also return status code - * NOT_FOUND(404) if id of collection is incorrect or status code - * UNATHORIZED(401) if user has no permission to read collection. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). It is thrown by NOT_FOUND and - * UNATHORIZED status codes, too. - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.ItemFilter getItemQuery(@QueryParam("expand") String expand, - @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @QueryParam("filters") @DefaultValue("is_item,all_filters") - String filters, - @QueryParam("query_field[]") @DefaultValue("dc.title") - List query_field, - @QueryParam("query_op[]") @DefaultValue("exists") - List query_op, - @QueryParam("query_val[]") @DefaultValue("") List - query_val, - @QueryParam("collSel[]") @DefaultValue("") List - collSel, - @Context HttpHeaders headers, - @Context HttpServletRequest request, - @Context ServletContext servletContext) { - org.dspace.core.Context context = null; - ItemFilterSet itemFilterSet = new ItemFilterSet(filters, true); - ItemFilter result = itemFilterSet.getAllFiltersFilter(); - try { - context = createContext(); - - int index = Math.min(query_field.size(), Math.min(query_op.size(), query_val.size())); - List itemFilterQueries = new ArrayList(); - for (int i = 0; i < index; i++) { - itemFilterQueries.add(new ItemFilterQuery(query_field.get(i), query_op.get(i), query_val.get(i))); - } - - String regexClause = configurationService.getProperty("rest.regex-clause"); - if (regexClause == null) { - regexClause = ""; - } - - List uuids = getUuidsFromStrings(collSel); - List> listFieldList = getMetadataFieldsList(context, query_field); - - Iterator childItems = itemService - .findByMetadataQuery(context, listFieldList, query_op, query_val, uuids, regexClause, offset, limit); - - int count = itemFilterSet.processSaveItems(context, servletContext, childItems, true, expand); - writeStats(siteService.findSite(context), UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, - headers, request, context); - result.annotateQuery(query_field, query_op, query_val); - result.setUnfilteredItemCount(count); - context.complete(); - } catch (IOException e) { - processException(e.getMessage(), context); - } catch (SQLException e) { - processException(e.getMessage(), context); - } catch (AuthorizeException e) { - processException(e.getMessage(), context); - } catch (ContextException e) { - processException("Unauthorized filtered item query. " + e.getMessage(), context); - } finally { - processFinally(context); - } - return result; - } - - private List> getMetadataFieldsList(org.dspace.core.Context context, List query_field) - throws SQLException { - List> listFieldList = new ArrayList>(); - for (String s : query_field) { - ArrayList fields = new ArrayList(); - listFieldList.add(fields); - if (s.equals("*")) { - continue; - } - String schema = ""; - String element = ""; - String qualifier = null; - String[] parts = s.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)) { - for (MetadataField mf : metadataFieldService - .findFieldsByElementNameUnqualified(context, schema, element)) { - fields.add(mf); - } - } else { - MetadataField mf = metadataFieldService.findByElement(context, schema, element, qualifier); - if (mf != null) { - fields.add(mf); - } - } - } - return listFieldList; - } - - private List getUuidsFromStrings(List collSel) { - List uuids = new ArrayList(); - for (String s : collSel) { - try { - uuids.add(UUID.fromString(s)); - } catch (IllegalArgumentException e) { - log.warn("Invalid collection UUID: " + s); - } - } - return uuids; - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/FiltersResource.java b/dspace-rest/src/main/java/org/dspace/rest/FiltersResource.java deleted file mode 100644 index bff755f2de..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/FiltersResource.java +++ /dev/null @@ -1,60 +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.rest; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; - -import org.apache.logging.log4j.Logger; -import org.dspace.rest.common.ItemFilter; - -/** - * Class which provides read methods over the metadata registry. - * - * @author Terry Brady, Georgetown University - */ -@Path("/filters") -public class FiltersResource { - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(FiltersResource.class); - - /** - * Return all Use Case Item Filters in DSpace. - * - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of metadata schemas. - * @throws WebApplicationException It can be caused by creating context or while was problem - * with reading community from database(SQLException). - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public ItemFilter[] getFilters(@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading all Item Filters."); - return ItemFilter.getItemFilters(ItemFilter.ALL, false).toArray(new ItemFilter[0]); - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/HandleResource.java b/dspace-rest/src/main/java/org/dspace/rest/HandleResource.java deleted file mode 100644 index 51436a1c00..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/HandleResource.java +++ /dev/null @@ -1,109 +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.rest; - -import java.sql.SQLException; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.DSpaceObjectService; -import org.dspace.core.Constants; -import org.dspace.handle.factory.HandleServiceFactory; -import org.dspace.handle.service.HandleService; -import org.dspace.rest.common.Collection; -import org.dspace.rest.common.Community; -import org.dspace.rest.common.DSpaceObject; -import org.dspace.rest.common.Item; -import org.dspace.rest.exceptions.ContextException; - -/** - * Created with IntelliJ IDEA. - * User: peterdietz - * Date: 10/7/13 - * Time: 1:54 PM - * To change this template use File | Settings | File Templates. - */ -@Path("/handle") -public class HandleResource extends Resource { - protected HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(HandleResource.class); - - @GET - @Path("/{prefix}/{suffix}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public org.dspace.rest.common.DSpaceObject getObject(@PathParam("prefix") String prefix, - @PathParam("suffix") String suffix, - @QueryParam("expand") String expand, - @javax.ws.rs.core.Context HttpHeaders headers) { - DSpaceObject dSpaceObject = new DSpaceObject(); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.DSpaceObject dso = handleService.resolveToObject(context, prefix + "/" + suffix); - - if (dso == null) { - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - DSpaceObjectService dSpaceObjectService = ContentServiceFactory.getInstance().getDSpaceObjectService(dso); - log.info("DSO Lookup by handle: [" + prefix + "] / [" + suffix + "] got result of: " + dSpaceObjectService - .getTypeText(dso) + "_" + dso.getID()); - - if (authorizeService.authorizeActionBoolean(context, dso, org.dspace.core.Constants.READ)) { - switch (dso.getType()) { - case Constants.COMMUNITY: - dSpaceObject = new Community((org.dspace.content.Community) dso, servletContext, expand, - context); - break; - case Constants.COLLECTION: - dSpaceObject = new Collection((org.dspace.content.Collection) dso, servletContext, expand, - context, null, null); - break; - case Constants.ITEM: - dSpaceObject = new Item((org.dspace.content.Item) dso, servletContext, expand, context); - break; - default: - dSpaceObject = new DSpaceObject(dso, servletContext); - break; - } - } else { - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - context.complete(); - - } catch (SQLException e) { - log.error(e.getMessage()); - throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); - } catch (ContextException e) { - processException( - "Could not read handle(prefix=" + prefix + "), (suffix=" + suffix + ") ContextException. Message:" + e - .getMessage(), - context); - } finally { - processFinally(context); - } - - return dSpaceObject; - - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/HierarchyResource.java b/dspace-rest/src/main/java/org/dspace/rest/HierarchyResource.java deleted file mode 100644 index b2ffc559b0..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/HierarchyResource.java +++ /dev/null @@ -1,140 +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.rest; - -import java.io.UnsupportedEncodingException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.Collection; -import org.dspace.content.Community; -import org.dspace.content.Site; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CommunityService; -import org.dspace.content.service.SiteService; -import org.dspace.rest.common.HierarchyCollection; -import org.dspace.rest.common.HierarchyCommunity; -import org.dspace.rest.common.HierarchySite; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; - - -/* - * This class retrieves the community hierarchy in an optimized format. - * - * @author Terry Brady, Georgetown University - */ -@Path("/hierarchy") -@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) -public class HierarchyResource extends Resource { - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(HierarchyResource.class); - protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService(); - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - - /** - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the collection as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of collection. It can also return status code - * NOT_FOUND(404) if id of collection is incorrect or status code - * @throws UnsupportedEncodingException The Character Encoding is not supported. - * @throws WebApplicationException It is thrown when was problem with database reading - * (SQLException) or problem with creating - * context(ContextException). It is thrown by NOT_FOUND and - * UNATHORIZED status codes, too. - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public HierarchySite getHierarchy( - @QueryParam("userAgent") String user_agent, @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws UnsupportedEncodingException, WebApplicationException { - - org.dspace.core.Context context = null; - HierarchySite repo = new HierarchySite(); - - try { - context = createContext(); - - Site site = siteService.findSite(context); - repo.setId(site.getID().toString()); - repo.setName(site.getName()); - repo.setHandle(site.getHandle()); - List dspaceCommunities = communityService.findAllTop(context); - processCommunity(context, repo, dspaceCommunities); - } catch (Exception e) { - processException(e.getMessage(), context); - } finally { - if (context != null) { - try { - context.complete(); - } catch (SQLException e) { - log.error(e.getMessage() + " occurred while trying to close"); - } - } - } - return repo; - } - - - private void processCommunity(org.dspace.core.Context context, HierarchyCommunity parent, - List communities) throws SQLException { - if (communities == null) { - return; - } - if (communities.size() == 0) { - return; - } - List parentComms = new ArrayList(); - parent.setCommunities(parentComms); - for (Community comm : communities) { - if (!authorizeService.authorizeActionBoolean(context, comm, org.dspace.core.Constants.READ)) { - continue; - } - HierarchyCommunity mycomm = new HierarchyCommunity(comm.getID().toString(), comm.getName(), - comm.getHandle()); - parentComms.add(mycomm); - List colls = comm.getCollections(); - if (colls.size() > 0) { - List myColls = new ArrayList(); - mycomm.setCollections(myColls); - for (Collection coll : colls) { - if (!authorizeService.authorizeActionBoolean(context, coll, org.dspace.core.Constants.READ)) { - continue; - } - HierarchyCollection mycoll = new HierarchyCollection(coll.getID().toString(), coll.getName(), - coll.getHandle()); - myColls.add(mycoll); - } - } - processCommunity(context, mycomm, comm.getSubcommunities()); - } - - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java b/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java deleted file mode 100644 index 615aacac21..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/ItemsResource.java +++ /dev/null @@ -1,1007 +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.rest; - -import java.io.IOException; -import java.io.InputStream; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.authorize.service.ResourcePolicyService; -import org.dspace.content.Bundle; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.BitstreamFormatService; -import org.dspace.content.service.BitstreamService; -import org.dspace.content.service.BundleService; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.ItemService; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.GroupService; -import org.dspace.rest.common.Bitstream; -import org.dspace.rest.common.Item; -import org.dspace.rest.common.MetadataEntry; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.usage.UsageEvent; - -/** - * Class which provide all CRUD methods over items. - * - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -// Every DSpace class used without namespace is from package org.dspace.rest.common.*. Otherwise namespace is defined. -@SuppressWarnings("deprecation") -@Path("/items") -public class ItemsResource extends Resource { - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() - .getBitstreamFormatService(); - protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService(); - protected ResourcePolicyService resourcePolicyService = AuthorizeServiceFactory.getInstance() - .getResourcePolicyService(); - protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemsResource.class); - - /** - * Return item properties without metadata and bitstreams. You can add - * additional properties by parameter expand. - * - * @param itemId Id of item in DSpace. - * @param expand String which define, what additional properties will be in - * returned item. Options are separeted by commas and are: "all", - * "metadata", "parentCollection", "parentCollectionList", - * "parentCommunityList" and "bitstreams". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return If user is allowed to read item, it returns item. Otherwise is - * thrown WebApplicationException with response status - * UNAUTHORIZED(401) or NOT_FOUND(404) if was id incorrect. - * @throws WebApplicationException This exception can be throw by NOT_FOUND(bad id of item), - * UNAUTHORIZED, SQLException if wasproblem with reading from - * database and ContextException, if there was problem with - * creating context of DSpace. - */ - @GET - @Path("/{item_id}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Item getItem(@PathParam("item_id") String itemId, @QueryParam("expand") String expand, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - Item item = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.READ); - - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, request, - context); - - item = new Item(dspaceItem, servletContext, expand, context); - context.complete(); - log.trace("Item(id=" + itemId + ") was successfully read."); - - } catch (SQLException e) { - processException("Could not read item(id=" + itemId + "), SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not read item(id=" + itemId + "), ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - return item; - } - - /** - * It returns an array of items in DSpace. You can define how many items in - * list will be and from which index will start. Items in list are sorted by - * handle, not by id. - * - * @param expand String which define, what additional properties will be in - * returned item. Options are separeted by commas and are: "all", - * "metadata", "parentCollection", "parentCollectionList", - * "parentCommunityList" and "bitstreams". - * @param limit How many items in array will be. Default value is 100. - * @param offset On which index will array start. Default value is 0. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return Return array of items, on which has logged user into context - * permission. - * @throws WebApplicationException It can be thrown by SQLException, when was problem with - * reading items from database or ContextException, when was - * problem with creating context of DSpace. - */ - @GET - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Item[] getItems(@QueryParam("expand") String expand, @QueryParam("limit") @DefaultValue("100") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading items.(offset=" + offset + ",limit=" + limit + ")."); - org.dspace.core.Context context = null; - List items = null; - - try { - context = createContext(); - - Iterator dspaceItems = itemService.findAllUnfiltered(context); - items = new ArrayList(); - - if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) { - log.warn("Paging was badly set, using default values."); - limit = 100; - offset = 0; - } - - for (int i = 0; (dspaceItems.hasNext()) && (i < (limit + offset)); i++) { - org.dspace.content.Item dspaceItem = dspaceItems.next(); - if (i >= offset) { - if (itemService.isItemListedForUser(context, dspaceItem)) { - items.add(new Item(dspaceItem, servletContext, expand, context)); - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, - headers, request, context); - } - } - } - context.complete(); - } catch (SQLException e) { - processException("Something went wrong while reading items from database. Message: " + e, context); - } catch (ContextException e) { - processException("Something went wrong while reading items, ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("Items were successfully read."); - return items.toArray(new Item[0]); - } - - /** - * Returns item metadata in list. - * - * @param itemId Id of item in DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return Return list of metadata fields if was everything ok. Otherwise it - * throw WebApplication exception with response code NOT_FOUND(404) - * or UNAUTHORIZED(401). - * @throws WebApplicationException It can be thrown by two exceptions: SQLException if was - * problem wtih reading item from database and ContextException, - * if was problem with creating context of DSpace. And can be - * thrown by NOT_FOUND and UNAUTHORIZED too. - */ - @GET - @Path("/{item_id}/metadata") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataEntry[] getItemMetadata(@PathParam("item_id") String itemId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading item(id=" + itemId + ") metadata."); - org.dspace.core.Context context = null; - List metadata = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.READ); - - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, request, - context); - - metadata = new org.dspace.rest.common.Item(dspaceItem, servletContext, "metadata", context).getMetadata(); - context.complete(); - } catch (SQLException e) { - processException("Could not read item(id=" + itemId + "), SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not read item(id=" + itemId + "), ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("Item(id=" + itemId + ") metadata were successfully read."); - return metadata.toArray(new MetadataEntry[0]); - } - - /** - * Return array of bitstreams in item. It can be paged. - * - * @param itemId Id of item in DSpace. - * @param limit How many items will be in array. - * @param offset On which index will start array. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return Return paged array of bitstreams in item. - * @throws WebApplicationException It can be throw by NOT_FOUND, UNAUTHORIZED, SQLException if - * was problem with reading from database and ContextException - * if was problem with creating context of DSpace. - */ - @GET - @Path("/{item_id}/bitstreams") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Bitstream[] getItemBitstreams(@PathParam("item_id") String itemId, - @QueryParam("limit") @DefaultValue("20") Integer limit, - @QueryParam("offset") @DefaultValue("0") Integer offset, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading item(id=" + itemId + ") bitstreams.(offset=" + offset + ",limit=" + limit + ")"); - org.dspace.core.Context context = null; - List bitstreams = null; - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.READ); - - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, request, - context); - - List itemBitstreams = new Item(dspaceItem, servletContext, "bitstreams", context) - .getBitstreams(); - - if ((offset + limit) > (itemBitstreams.size() - offset)) { - bitstreams = itemBitstreams.subList(offset, itemBitstreams.size()); - } else { - bitstreams = itemBitstreams.subList(offset, offset + limit); - } - context.complete(); - } catch (SQLException e) { - processException("Could not read item(id=" + itemId + ") bitstreams, SQLExcpetion. Message: " + e, context); - } catch (ContextException e) { - processException( - "Could not read item(id=" + itemId + ") bitstreams, ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.trace("Item(id=" + itemId + ") bitstreams were successfully read."); - return bitstreams.toArray(new Bitstream[0]); - } - - /** - * Adding metadata fields to item. If metadata key is in item, it will be - * added, NOT REPLACED! - * - * @param itemId Id of item in DSpace. - * @param metadata List of metadata fields, which will be added into item. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return It returns status code OK(200) if all was ok. UNAUTHORIZED(401) - * if user is not allowed to write to item. NOT_FOUND(404) if id of - * item is incorrect. - * @throws WebApplicationException It is throw by these exceptions: SQLException, if was problem - * with reading from database or writing to database. - * AuthorizeException, if was problem with authorization to item - * fields. ContextException, if was problem with creating - * context of DSpace. - */ - @POST - @Path("/{item_id}/metadata") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response addItemMetadata(@PathParam("item_id") String itemId, - List metadata, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Adding metadata to item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.WRITE); - - writeStats(dspaceItem, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, request, - context); - - for (MetadataEntry entry : metadata) { - // TODO Test with Java split - String data[] = mySplit(entry.getKey()); // Done by my split, because of java split was not function. - if ((data.length >= 2) && (data.length <= 3)) { - itemService.addMetadata(context, dspaceItem, data[0], data[1], data[2], entry.getLanguage(), - entry.getValue()); - } - } - context.complete(); - - } catch (SQLException e) { - processException("Could not write metadata to item(id=" + itemId + "), SQLException. Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not write metadata to item(id=" + itemId + "), ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Metadata to item(id=" + itemId + ") were successfully added."); - return Response.status(Status.OK).build(); - } - - /** - * Create bitstream in item. - * - * @param name Btstream name to set. - * @param description Btstream description to set. - * @param groupId ResourcePolicy group (allowed to READ). - * @param year ResourcePolicy start date year. - * @param month ResourcePolicy start date month. - * @param day ResourcePolicy start date day. - * @param itemId Id of item in DSpace. - * @param inputStream Data of bitstream in inputStream. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return Returns bitstream with status code OK(200). If id of item is - * invalid , it returns status code NOT_FOUND(404). If user is not - * allowed to write to item, UNAUTHORIZED(401). - * @throws WebApplicationException It is thrown by these exceptions: SQLException, when was - * problem with reading/writing from/to database. - * AuthorizeException, when was problem with authorization to - * item and add bitstream to item. IOException, when was problem - * with creating file or reading from inpustream. - * ContextException. When was problem with creating context of - * DSpace. - */ - // TODO Add option to add bitstream by URI.(for very big files) - @POST - @Path("/{item_id}/bitstreams") - public Bitstream addItemBitstream(@PathParam("item_id") String itemId, InputStream inputStream, - @QueryParam("name") String name, @QueryParam("description") String description, - @QueryParam("groupId") String groupId, @QueryParam("year") Integer year, - @QueryParam("month") Integer month, - @QueryParam("day") Integer day, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Adding bitstream to item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - Bitstream bitstream = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.WRITE); - - writeStats(dspaceItem, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, request, - context); - - // Is better to add bitstream to ORIGINAL bundle or to item own? - log.trace("Creating bitstream in item."); - org.dspace.content.Bundle bundle = null; - org.dspace.content.Bitstream dspaceBitstream = null; - List bundles = itemService.getBundles(dspaceItem, org.dspace.core.Constants.CONTENT_BUNDLE_NAME); - - if (bundles != null && bundles.size() != 0) { - bundle = bundles.get(0); // There should be only one bundle ORIGINAL. - } - if (bundle == null) { - log.trace("Creating bundle in item."); - dspaceBitstream = itemService.createSingleBitstream(context, inputStream, dspaceItem); - } else { - log.trace("Getting bundle from item."); - dspaceBitstream = bitstreamService.create(context, bundle, inputStream); - } - - dspaceBitstream.setSource(context, "DSpace REST API"); - - // Set bitstream name and description - if (name != null) { - if (BitstreamResource.getMimeType(name) == null) { - dspaceBitstream.setFormat(context, bitstreamFormatService.findUnknown(context)); - } else { - bitstreamService.setFormat(context, dspaceBitstream, bitstreamFormatService - .findByMIMEType(context, BitstreamResource.getMimeType(name))); - } - - dspaceBitstream.setName(context, name); - } - if (description != null) { - dspaceBitstream.setDescription(context, description); - } - - // Create policy for bitstream - if (groupId != null) { - bundles = dspaceBitstream.getBundles(); - for (Bundle dspaceBundle : bundles) { - List bitstreamsPolicies = bundleService - .getBitstreamPolicies(context, dspaceBundle); - - // Remove default bitstream policies - List policiesToRemove = new ArrayList(); - for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies) { - if (policy.getdSpaceObject().getID().equals(dspaceBitstream.getID())) { - policiesToRemove.add(policy); - } - } - for (org.dspace.authorize.ResourcePolicy policy : policiesToRemove) { - bitstreamsPolicies.remove(policy); - } - - org.dspace.authorize.ResourcePolicy dspacePolicy = resourcePolicyService.create(context); - dspacePolicy.setAction(org.dspace.core.Constants.READ); - dspacePolicy.setGroup(groupService.findByIdOrLegacyId(context, groupId)); - dspacePolicy.setdSpaceObject(dspaceBitstream); - if ((year != null) || (month != null) || (day != null)) { - Date date = new Date(); - if (year != null) { - date.setYear(year - 1900); - } - if (month != null) { - date.setMonth(month - 1); - } - if (day != null) { - date.setDate(day); - } - date.setHours(0); - date.setMinutes(0); - date.setSeconds(0); - dspacePolicy.setStartDate(date); - } - - resourcePolicyService.update(context, dspacePolicy); - - bitstreamService.updateLastModified(context, dspaceBitstream); - } - } - - dspaceBitstream = bitstreamService.find(context, dspaceBitstream.getID()); - bitstream = new Bitstream(dspaceBitstream, servletContext, "", context); - - context.complete(); - - } catch (SQLException e) { - processException("Could not create bitstream in item(id=" + itemId + "), SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException("Could not create bitstream in item(id=" + itemId + "), AuthorizeException. Message: " + e, - context); - } catch (IOException e) { - processException("Could not create bitstream in item(id=" + itemId + "), IOException Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not create bitstream in item(id=" + itemId + "), ContextException Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Bitstream(id=" + bitstream.getUUID() + ") was successfully added into item(id=" + itemId + ")."); - return bitstream; - } - - /** - * Replace all metadata in item with new passed metadata. - * - * @param itemId Id of item in DSpace. - * @param metadata List of metadata fields, which will replace old metadata in - * item. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return It returns status code: OK(200). NOT_FOUND(404) if item was not - * found, UNAUTHORIZED(401) if user is not allowed to write to item. - * @throws WebApplicationException It is thrown by: SQLException, when was problem with database - * reading or writting, AuthorizeException when was problem with - * authorization to item and metadata fields. And - * ContextException, when was problem with creating context of - * DSpace. - */ - @PUT - @Path("/{item_id}/metadata") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response updateItemMetadata(@PathParam("item_id") String itemId, MetadataEntry[] metadata, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Updating metadata in item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.WRITE); - - writeStats(dspaceItem, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, request, - context); - - log.trace("Deleting original metadata from item."); - for (MetadataEntry entry : metadata) { - String data[] = mySplit(entry.getKey()); - if ((data.length >= 2) && (data.length <= 3)) { - itemService - .clearMetadata(context, dspaceItem, data[0], data[1], data[2], org.dspace.content.Item.ANY); - } - } - - log.trace("Adding new metadata to item."); - for (MetadataEntry entry : metadata) { - String data[] = mySplit(entry.getKey()); - if ((data.length >= 2) && (data.length <= 3)) { - itemService.addMetadata(context, dspaceItem, data[0], data[1], data[2], entry.getLanguage(), - entry.getValue()); - } - } - //Update the item to ensure that all the events get fired. - itemService.update(context, dspaceItem); - - context.complete(); - - } catch (SQLException e) { - processException("Could not update metadata in item(id=" + itemId + "), SQLException. Message: " + e, - context); - } catch (ContextException e) { - processException( - "Could not update metadata in item(id=" + itemId + "), ContextException. Message: " + e.getMessage(), - context); - } catch (AuthorizeException e) { - processException( - "Could not update metadata in item(id=" + itemId + "), AuthorizeException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Metadata of item(id=" + itemId + ") were successfully updated."); - return Response.status(Status.OK).build(); - } - - /** - * Delete item from DSpace. It delete bitstreams only from item bundle. - * - * @param itemId Id of item which will be deleted. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return It returns status code: OK(200). NOT_FOUND(404) if item was not - * found, UNAUTHORIZED(401) if user is not allowed to delete item - * metadata. - * @throws WebApplicationException It can be thrown by: SQLException, when was problem with - * database reading. AuthorizeException, when was problem with - * authorization to item.(read and delete) IOException, when was - * problem with deleting bitstream file. ContextException, when - * was problem with creating context of DSpace. - */ - @DELETE - @Path("/{item_id}") - public Response deleteItem(@PathParam("item_id") String itemId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.DELETE); - - writeStats(dspaceItem, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor, headers, request, - context); - - log.trace("Deleting item."); - itemService.delete(context, dspaceItem); - context.complete(); - - } catch (SQLException e) { - processException("Could not delete item(id=" + itemId + "), SQLException. Message: " + e, context); - } catch (AuthorizeException e) { - processException("Could not delete item(id=" + itemId + "), AuthorizeException. Message: " + e, context); - throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); - } catch (IOException e) { - processException("Could not delete item(id=" + itemId + "), IOException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not delete item(id=" + itemId + "), ContextException. Message: " + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Item(id=" + itemId + ") was successfully deleted."); - return Response.status(Status.OK).build(); - } - - /** - * Delete all item metadata. - * - * @param itemId Id of item in DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return It returns status code: OK(200). NOT_FOUND(404) if item was not - * found, UNAUTHORIZED(401) if user is not allowed to delete item - * metadata. - * @throws WebApplicationException Thrown by three exceptions. SQLException, when there was - * a problem reading item from database or editing metadata - * fields. AuthorizeException, when there was a problem with - * authorization to item. And ContextException, when there was a problem - * with creating a DSpace context. - */ - @DELETE - @Path("/{item_id}/metadata") - public Response deleteItemMetadata(@PathParam("item_id") String itemId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting metadata in item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.WRITE); - - writeStats(dspaceItem, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, request, - context); - - log.trace("Deleting metadata."); - // TODO Rewrite without deprecated object. Leave there only generated metadata. - - String valueAccessioned = itemService - .getMetadataFirstValue(dspaceItem, "dc", "date", "accessioned", org.dspace.content.Item.ANY); - String valueAvailable = itemService - .getMetadataFirstValue(dspaceItem, "dc", "date", "available", org.dspace.content.Item.ANY); - String valueURI = itemService - .getMetadataFirstValue(dspaceItem, "dc", "identifier", "uri", org.dspace.content.Item.ANY); - String valueProvenance = itemService - .getMetadataFirstValue(dspaceItem, "dc", "description", "provenance", org.dspace.content.Item.ANY); - - itemService.clearMetadata(context, dspaceItem, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, - org.dspace.content.Item.ANY, - org.dspace.content.Item.ANY); - - // Add their generated metadata - itemService.addMetadata(context, dspaceItem, "dc", "date", "accessioned", null, valueAccessioned); - itemService.addMetadata(context, dspaceItem, "dc", "date", "available", null, valueAvailable); - itemService.addMetadata(context, dspaceItem, "dc", "identifier", "uri", null, valueURI); - itemService.addMetadata(context, dspaceItem, "dc", "description", "provenance", null, valueProvenance); - - context.complete(); - } catch (SQLException e) { - processException("Could not delete item(id=" + itemId + "), SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not delete item(id=" + itemId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Item(id=" + itemId + ") metadata were successfully deleted."); - return Response.status(Status.OK).build(); - } - - /** - * Delete bitstream from item bundle. - * - * @param itemId Id of item in DSpace. - * @param bitstreamId Id of bitstream, which will be deleted from bundle. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the context. - * The value of the "rest-dspace-token" header must be set with passed - * token from login method. - * @param request Servlet's HTTP request object. - * @return Return status code OK(200) if is all ok. NOT_FOUND(404) if item - * or bitstream was not found. UNAUTHORIZED(401) if user is not - * allowed to delete bitstream. - * @throws WebApplicationException It is thrown, when: Was problem with edditting database, - * SQLException. Or problem with authorization to item, bundle - * or bitstream, AuthorizeException. When was problem with - * deleting file IOException. Or problem with creating context - * of DSpace, ContextException. - */ - @DELETE - @Path("/{item_id}/bitstreams/{bitstream_id}") - public Response deleteItemBitstream(@PathParam("item_id") String itemId, - @PathParam("bitstream_id") String bitstreamId, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting bitstream in item(id=" + itemId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - org.dspace.content.Item item = findItem(context, itemId, org.dspace.core.Constants.WRITE); - - org.dspace.content.Bitstream bitstream = bitstreamService.findByIdOrLegacyId(context, bitstreamId); - if (bitstream == null) { - context.abort(); - log.warn("Bitstream(id=" + bitstreamId + ") was not found."); - return Response.status(Status.NOT_FOUND).build(); - } else if (!authorizeService.authorizeActionBoolean(context, bitstream, org.dspace.core.Constants.DELETE)) { - context.abort(); - log.error("User(" + context.getCurrentUser() - .getEmail() + ") is not allowed to delete bitstream(id=" + bitstreamId + - ")."); - return Response.status(Status.UNAUTHORIZED).build(); - } - - writeStats(item, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, request, context); - writeStats(bitstream, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor, headers, - request, context); - - log.trace("Deleting bitstream..."); - bitstreamService.delete(context, bitstream); - - context.complete(); - - } catch (SQLException e) { - processException("Could not delete bitstream(id=" + bitstreamId + "), SQLException. Message: " + e, - context); - } catch (AuthorizeException e) { - processException("Could not delete bitstream(id=" + bitstreamId + "), AuthorizeException. Message: " + e, - context); - } catch (IOException e) { - processException("Could not delete bitstream(id=" + bitstreamId + "), IOException. Message: " + e, context); - } catch (ContextException e) { - processException( - "Could not delete bitstream(id=" + bitstreamId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - log.info("Bitstream(id=" + bitstreamId + ") from item(id=" + itemId + ") was successfuly deleted ."); - return Response.status(Status.OK).build(); - } - - /** - * Find items by one metadata field. - * - * @param metadataEntry Metadata field to search by. - * @param expand String which define, what additional properties will be in - * returned item. Options are separeted by commas and are: "all", - * "metadata", "parentCollection", "parentCollectionList", - * "parentCommunityList" and "bitstreams". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into context, - * header "rest-dspace-token" must be set to token value retrieved - * from the login method. - * @param request Servlet's HTTP request object. - * @return Return array of found items. - * @throws WebApplicationException Can be thrown: SQLException - problem with - * database reading. AuthorizeException - problem with - * authorization to item. IOException - problem with - * reading from metadata field. ContextException - - * problem with creating DSpace context. - */ - @POST - @Path("/find-by-metadata-field") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Item[] findItemsByMetadataField(MetadataEntry metadataEntry, @QueryParam("expand") String expand, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Looking for item with metadata(key=" + metadataEntry.getKey() + ",value=" + metadataEntry.getValue() - + ", language=" + metadataEntry.getLanguage() + ")."); - org.dspace.core.Context context = null; - - List items = new ArrayList(); - String[] metadata = mySplit(metadataEntry.getKey()); - - // Must used own style. - if ((metadata.length < 2) || (metadata.length > 3)) { - log.error("Finding failed, bad metadata key."); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - - try { - context = createContext(); - - Iterator itemIterator = itemService - .findByMetadataField(context, metadataEntry.getSchema(), - metadataEntry.getElement(), metadataEntry.getQualifier(), - metadataEntry.getValue()); - - while (itemIterator.hasNext()) { - org.dspace.content.Item dspaceItem = itemIterator.next(); - //Only return items that are available for the current user - if (itemService.isItemListedForUser(context, dspaceItem)) { - Item item = new Item(dspaceItem, servletContext, expand, context); - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers, - request, context); - items.add(item); - } - } - - context.complete(); - } catch (SQLException e) { - processException("Something went wrong while finding item. SQLException, Message: " + e, context); - } catch (ContextException e) { - processException("Context error:" + e.getMessage(), context); - } catch (AuthorizeException e) { - processException("Authorize error:" + e.getMessage(), context); - } catch (IOException e) { - processException("IO error:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - if (items.size() == 0) { - log.info("Items not found."); - } else { - log.info("Items were found."); - } - - return items.toArray(new Item[0]); - } - - /** - * Find item from DSpace database. It is encapsulation of method - * org.dspace.content.Item.find with checking if item exist and if user - * logged into context has permission to do passed action. - * - * @param context Context of actual logged user. - * @param id Id of item in DSpace. - * @param action Constant from org.dspace.core.Constants. - * @return It returns DSpace item. - * @throws WebApplicationException Is thrown when item with passed id is not exists and if user - * has no permission to do passed action. - */ - private org.dspace.content.Item findItem(org.dspace.core.Context context, String id, int action) - throws WebApplicationException { - org.dspace.content.Item item = null; - try { - item = itemService.findByIdOrLegacyId(context, id); - - if (item == null) { - context.abort(); - log.warn("Item(id=" + id + ") was not found!"); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } else if (!authorizeService.authorizeActionBoolean(context, item, action)) { - context.abort(); - if (context.getCurrentUser() != null) { - log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to " - + getActionString(action) + " item!"); - } else { - log.error("User(anonymous) has not permission to " + getActionString(action) + " item!"); - } - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - } catch (SQLException e) { - processException("Something get wrong while finding item(id=" + id + "). SQLException, Message: " + e, - context); - } - return item; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/MetadataRegistryResource.java b/dspace-rest/src/main/java/org/dspace/rest/MetadataRegistryResource.java deleted file mode 100644 index 79e655e63d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/MetadataRegistryResource.java +++ /dev/null @@ -1,738 +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.rest; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.NonUniqueMetadataException; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.MetadataFieldService; -import org.dspace.content.service.MetadataSchemaService; -import org.dspace.content.service.SiteService; -import org.dspace.rest.common.MetadataField; -import org.dspace.rest.common.MetadataSchema; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.usage.UsageEvent; - -/** - * Class which provides read methods over the metadata registry. - * - * @author Terry Brady, Georgetown University - * - * GET /registries/schema - Return the list of schemas in the registry - * GET /registries/schema/{schema_prefix} - Returns the specified schema - * GET /registries/schema/{schema_prefix}/metadata-fields/{element} - Returns the metadata field within a schema - * with an unqualified element name - * GET /registries/schema/{schema_prefix}/metadata-fields/{element}/{qualifier} - Returns the metadata field - * within a schema with a qualified element name - * POST /registries/schema/ - Add a schema to the schema registry - * POST /registries/schema/{schema_prefix}/metadata-fields - Add a metadata field to the specified schema - * GET /registries/metadata-fields/{field_id} - Return the specified metadata field - * PUT /registries/metadata-fields/{field_id} - Update the specified metadata field - * DELETE /registries/metadata-fields/{field_id} - Delete the specified metadata field from the metadata field registry - * DELETE /registries/schema/{schema_id} - Delete the specified schema from the schema registry - * - * Note: intentionally not providing since there is no date to update other than the namespace - * PUT /registries/schema/{schema_id} - */ -@Path("/registries") -public class MetadataRegistryResource extends Resource { - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService(); - protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance() - .getMetadataSchemaService(); - protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService(); - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataRegistryResource.class); - - /** - * Return all metadata registry items in DSpace. - * - * @param expand String in which is what you want to add to returned instance - * of metadata schema. Options are: "all", "fields". Default value "fields". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the metadata schema as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return array of metadata schemas. - * @throws WebApplicationException It can be caused by creating context or while was problem - * with reading schema from database(SQLException). - */ - @GET - @Path("/schema") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataSchema[] getSchemas(@QueryParam("expand") @DefaultValue("fields") String expand, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading all metadata schemas."); - org.dspace.core.Context context = null; - ArrayList metadataSchemas = null; - - try { - context = createContext(); - - List schemas = metadataSchemaService.findAll(context); - metadataSchemas = new ArrayList(); - for (org.dspace.content.MetadataSchema schema : schemas) { - metadataSchemas.add(new MetadataSchema(schema, expand, context)); - } - - context.complete(); - } catch (SQLException e) { - processException("Could not read metadata schemas, SQLException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not read metadata schemas, ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("All metadata schemas successfully read."); - return metadataSchemas.toArray(new MetadataSchema[0]); - } - - /** - * Returns metadata schema with basic properties. If you want more, use expand - * parameter or method for metadata fields. - * - * @param schemaPrefix Prefix for schema in DSpace. - * @param expand String in which is what you want to add to returned instance - * of metadata schema. Options are: "all", "fields". Default value "fields". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the metadata schema as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of org.dspace.rest.common.MetadataSchema. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading. Also if id/prefix of schema is incorrect - * or logged user into context has no permission to read. - */ - @GET - @Path("/schema/{schema_prefix}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataSchema getSchema(@PathParam("schema_prefix") String schemaPrefix, - @QueryParam("expand") @DefaultValue("fields") String expand, - @QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading metadata schemas."); - org.dspace.core.Context context = null; - MetadataSchema metadataSchema = null; - - try { - context = createContext(); - - org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix); - metadataSchema = new MetadataSchema(schema, expand, context); - if (schema == null) { - processException(String.format("Schema not found for index %s", schemaPrefix), context); - } - - context.complete(); - } catch (SQLException e) { - processException("Could not read metadata schema, SQLException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not read metadata schema, ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("Metadata schemas successfully read."); - return metadataSchema; - } - - /** - * Returns metadata field with basic properties. - * - * @param schemaPrefix Prefix for schema in DSpace. - * @param element Unqualified element name for field in the metadata registry. - * @param expand String in which is what you want to add to returned instance - * of the metadata field. Options are: "all", "parentSchema". Default value "". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of org.dspace.rest.common.MetadataField. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading. Also if id of field is incorrect - * or logged user into context has no permission to read. - */ - @GET - @Path("/schema/{schema_prefix}/metadata-fields/{element}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataField getMetadataFieldUnqualified(@PathParam("schema_prefix") String schemaPrefix, - @PathParam("element") String element, - @QueryParam("expand") String expand, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - return getMetadataFieldQualified(schemaPrefix, element, "", expand, user_ip, user_agent, xforwardedfor, headers, - request); - } - - /** - * Returns metadata field with basic properties. - * - * @param schemaPrefix Prefix for schema in DSpace. - * @param element Element name for field in the metadata registry. - * @param qualifier Element name qualifier for field in the metadata registry. - * @param expand String in which is what you want to add to returned instance - * of the metadata field. Options are: "all", "parentSchema". Default value "". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of org.dspace.rest.common.MetadataField. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading. Also if id of field is incorrect - * or logged user into context has no permission to read. - */ - @GET - @Path("/schema/{schema_prefix}/metadata-fields/{element}/{qualifier}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataField getMetadataFieldQualified(@PathParam("schema_prefix") String schemaPrefix, - @PathParam("element") String element, - @PathParam("qualifier") @DefaultValue("") String qualifier, - @QueryParam("expand") String expand, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading metadata field."); - org.dspace.core.Context context = null; - MetadataField metadataField = null; - - try { - context = createContext(); - - org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix); - - if (schema == null) { - log.error(String.format("Schema not found for prefix %s", schemaPrefix)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - - org.dspace.content.MetadataField field = metadataFieldService - .findByElement(context, schema, element, qualifier); - if (field == null) { - log.error(String.format("Field %s.%s.%s not found", schemaPrefix, element, qualifier)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - metadataField = new MetadataField(schema, field, expand, context); - - context.complete(); - } catch (SQLException e) { - processException("Could not read metadata field, SQLException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not read metadata field, ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("Metadata field successfully read."); - return metadataField; - } - - /** - * Returns metadata field with basic properties. - * - * @param fieldId Id of metadata field in DSpace. - * @param expand String in which is what you want to add to returned instance - * of the metadata field. Options are: "all", "parentSchema". Default value "parentSchema". - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the community as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return instance of org.dspace.rest.common.MetadataField. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading. Also if id of field is incorrect - * or logged user into context has no permission to read. - */ - @GET - @Path("/metadata-fields/{field_id}") - @Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataField getMetadataField(@PathParam("field_id") Integer fieldId, - @QueryParam("expand") @DefaultValue("parentSchema") String expand, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Reading metadata field."); - org.dspace.core.Context context = null; - MetadataField metadataField = null; - - try { - context = createContext(); - - org.dspace.content.MetadataField field = metadataFieldService.find(context, fieldId); - if (field == null) { - log.error(String.format("Metadata Field %d not found", fieldId)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - org.dspace.content.MetadataSchema schema = field.getMetadataSchema(); - if (schema == null) { - log.error(String.format("Parent Schema not found for Metadata Field %d not found", fieldId)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - metadataField = new MetadataField(schema, field, expand, context); - - context.complete(); - } catch (SQLException e) { - processException("Could not read metadata field, SQLException. Message:" + e, context); - } catch (ContextException e) { - processException("Could not read metadata field, ContextException. Message:" + e.getMessage(), context); - } finally { - processFinally(context); - } - - log.trace("Metadata field successfully read."); - return metadataField; - } - - /** - * Create schema in the schema registry. Creating a schema is restricted to admin users. - * - * @param schema Schema that will be added to the metadata registry. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the schema as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response 200 if was everything all right. Otherwise 400 - * when id of community was incorrect or 401 if was problem with - * permission to write into collection. - * Returns the schema (schemaId), if was all ok. - * @throws WebApplicationException It can be thrown by SQLException, AuthorizeException and - * ContextException. - */ - @POST - @Path("/schema") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataSchema createSchema(MetadataSchema schema, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Creating a schema."); - org.dspace.core.Context context = null; - MetadataSchema retSchema = null; - - try { - context = createContext(); - - if (!authorizeService.isAdmin(context)) { - context.abort(); - String user = "anonymous"; - if (context.getCurrentUser() != null) { - user = context.getCurrentUser().getEmail(); - } - log.error("User(" + user + ") does not have permission to create a metadata schema!"); - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - log.debug(String.format("Admin user creating schema with namespace %s and prefix %s", schema.getNamespace(), - schema.getPrefix())); - - org.dspace.content.MetadataSchema dspaceSchema = metadataSchemaService - .create(context, schema.getPrefix(), schema.getNamespace()); - log.debug("Creating return object."); - retSchema = new MetadataSchema(dspaceSchema, "", context); - - writeStats(siteService.findSite(context), UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - context.complete(); - log.info("Schema created" + retSchema.getPrefix()); - - } catch (SQLException e) { - processException("Could not create new metadata schema, SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not create new metadata schema, ContextException. Message: " + e.getMessage(), - context); - } catch (AuthorizeException e) { - processException("Could not create new metadata schema, AuthorizeException. Message: " + e.getMessage(), - context); - } catch (NonUniqueMetadataException e) { - processException( - "Could not create new metadata schema, NonUniqueMetadataException. Message: " + e.getMessage(), - context); - } catch (Exception e) { - processException("Could not create new metadata schema, Exception. Class: " + e.getClass(), context); - } finally { - processFinally(context); - } - - return retSchema; - } - - - /** - * Create a new metadata field within a schema. - * Creating a metadata field is restricted to admin users. - * - * @param schemaPrefix Prefix for schema in DSpace. - * @param field Field that will be added to the metadata registry for a schema. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the schema as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response 200 if was everything all right. Otherwise 400 - * when id of community was incorrect or 401 if was problem with - * permission to write into collection. - * Returns the field (with fieldId), if was all ok. - * @throws WebApplicationException It can be thrown by SQLException, AuthorizeException and - * ContextException. - */ - @POST - @Path("/schema/{schema_prefix}/metadata-fields") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public MetadataField createMetadataField(@PathParam("schema_prefix") String schemaPrefix, - MetadataField field, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info(String.format("Creating metadataField within schema %s.", schemaPrefix)); - org.dspace.core.Context context = null; - MetadataField retField = null; - - try { - context = createContext(); - - if (!authorizeService.isAdmin(context)) { - context.abort(); - String user = "anonymous"; - if (context.getCurrentUser() != null) { - user = context.getCurrentUser().getEmail(); - } - log.error("User(" + user + ") does not have permission to create a metadata field!"); - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - } - - org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix); - if (schema == null) { - log.error(String.format("Schema not found for prefix %s", schemaPrefix)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - org.dspace.content.MetadataField dspaceField = metadataFieldService - .create(context, schema, field.getElement(), field.getQualifier(), field.getDescription()); - writeStats(siteService.findSite(context), UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - retField = new MetadataField(schema, dspaceField, "", context); - context.complete(); - log.info("Metadata field created within schema" + retField.getName()); - } catch (SQLException e) { - processException("Could not create new metadata field, SQLException. Message: " + e, context); - } catch (ContextException e) { - processException("Could not create new metadata field, ContextException. Message: " + e.getMessage(), - context); - } catch (AuthorizeException e) { - processException("Could not create new metadata field, AuthorizeException. Message: " + e.getMessage(), - context); - } catch (NonUniqueMetadataException e) { - processException( - "Could not create new metadata field, NonUniqueMetadataException. Message: " + e.getMessage(), context); - } catch (Exception e) { - processException("Could not create new metadata field, Exception. Message: " + e.getMessage(), context); - } finally { - processFinally(context); - } - - return retField; - } - - //@PUT - //@Path("/schema/{schema_prefix}") - //Assumption - there are no meaningful fields to update for a schema - - /** - * Update metadata field. Replace all information about community except the id and the containing schema. - * - * @param fieldId Id of the field in the DSpace metdata registry. - * @param field Instance of the metadata field which will replace actual metadata field in - * DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the metadata field as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Response 200 if was all ok. Otherwise 400 if was id incorrect or - * 401 if logged user has no permission to update the metadata field. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or writing. Or problem with writing to - * community caused by authorization. - */ - @PUT - @Path("/metadata-fields/{field_id}") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response updateMetadataField(@PathParam("field_id") Integer fieldId, MetadataField field, - @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, - @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Updating metadata field(id=" + fieldId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.MetadataField dspaceField = metadataFieldService.find(context, fieldId); - if (field == null) { - log.error(String.format("Metadata Field %d not found", fieldId)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - - writeStats(siteService.findSite(context), UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, - headers, request, context); - - dspaceField.setElement(field.getElement()); - dspaceField.setQualifier(field.getQualifier()); - dspaceField.setScopeNote(field.getDescription()); - metadataFieldService.update(context, dspaceField); - - context.complete(); - - } catch (SQLException e) { - processException("Could not update metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e, - context); - } catch (ContextException e) { - processException("Could not update metadata field(id=" + fieldId + "), ContextException Message:" + e, - context); - } catch (AuthorizeException e) { - processException("Could not update metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e, - context); - } catch (NonUniqueMetadataException e) { - processException( - "Could not update metadata field(id=" + fieldId + "), NonUniqueMetadataException. Message:" + e, - context); - } catch (IOException e) { - processException("Could not update metadata field(id=" + fieldId + "), IOException. Message:" + e, context); - } finally { - processFinally(context); - } - - log.info("Metadata Field(id=" + fieldId + ") has been successfully updated."); - return Response.ok().build(); - } - - /** - * Delete metadata field from the DSpace metadata registry - * - * @param fieldId Id of the metadata field in DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the metadata field as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response code OK(200) if was everything all right. - * Otherwise return NOT_FOUND(404) if was id of metadata field is incorrect. - * Or (UNAUTHORIZED)401 if was problem with permission to metadata field. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or deleting. Or problem with deleting - * metadata field caused by IOException or authorization. - */ - @DELETE - @Path("/metadata-fields/{field_id}") - public Response deleteMetadataField(@PathParam("field_id") Integer fieldId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting metadata field(id=" + fieldId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.MetadataField dspaceField = metadataFieldService.find(context, fieldId); - if (dspaceField == null) { - log.error(String.format("Metadata Field %d not found", fieldId)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - writeStats(siteService.findSite(context), UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, - headers, - request, context); - - metadataFieldService.delete(context, dspaceField); - context.complete(); - - } catch (SQLException e) { - processException("Could not delete metadata field(id=" + fieldId + "), SQLException. Message:" + e, - context); - } catch (AuthorizeException e) { - processException("Could not delete metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e, - context); - } catch (ContextException e) { - processException( - "Could not delete metadata field(id=" + fieldId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - - log.info("Metadata field(id=" + fieldId + ") was successfully deleted."); - return Response.status(Response.Status.OK).build(); - } - - /** - * Delete metadata schema from the DSpace metadata registry - * - * @param schemaId Id of the metadata schema in DSpace. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the metadata schema as the user logged into the - * context. The value of the "rest-dspace-token" header must be set - * to the token received from the login method response. - * @param request Servlet's HTTP request object. - * @return Return response code OK(200) if was everything all right. - * Otherwise return NOT_FOUND(404) if was id of metadata schema is incorrect. - * Or (UNAUTHORIZED)401 if was problem with permission to metadata schema. - * @throws WebApplicationException Thrown if there was a problem with creating context or problem - * with database reading or deleting. Or problem with deleting - * metadata schema caused by IOException or authorization. - */ - @DELETE - @Path("/schema/{schema_id}") - public Response deleteSchema(@PathParam("schema_id") Integer schemaId, @QueryParam("userIP") String user_ip, - @QueryParam("userAgent") String user_agent, - @QueryParam("xforwardedfor") String xforwardedfor, - @Context HttpHeaders headers, @Context HttpServletRequest request) - throws WebApplicationException { - - log.info("Deleting metadata schema(id=" + schemaId + ")."); - org.dspace.core.Context context = null; - - try { - context = createContext(); - - org.dspace.content.MetadataSchema dspaceSchema = metadataSchemaService.find(context, schemaId); - if (dspaceSchema == null) { - log.error(String.format("Metadata Schema %d not found", schemaId)); - throw new WebApplicationException(Response.Status.NOT_FOUND); - } - writeStats(siteService.findSite(context), UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, - headers, - request, context); - - metadataSchemaService.delete(context, dspaceSchema); - context.complete(); - - } catch (SQLException e) { - processException("Could not delete metadata schema(id=" + schemaId + "), SQLException. Message:" + e, - context); - } catch (AuthorizeException e) { - processException("Could not delete metadata schema(id=" + schemaId + "), AuthorizeException. Message:" + e, - context); - } catch (ContextException e) { - processException( - "Could not delete metadata schema(id=" + schemaId + "), ContextException. Message:" + e.getMessage(), - context); - } finally { - processFinally(context); - } - - - log.info("Metadata schema(id=" + schemaId + ") was successfully deleted."); - return Response.status(Response.Status.OK).build(); - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/Resource.java b/dspace-rest/src/main/java/org/dspace/rest/Resource.java deleted file mode 100644 index 7a7624fef0..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/Resource.java +++ /dev/null @@ -1,212 +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.rest; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response; - -import org.apache.logging.log4j.Logger; -import org.dspace.content.DSpaceObject; -import org.dspace.core.Context; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.dspace.usage.UsageEvent; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * Superclass of all resource classes in REST API. It has methods for creating - * context, write statistics, processsing exceptions, splitting a key of - * metadata, string representation of action and method for getting the logged - * in user from the token in request header. - * - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -public class Resource { - - @javax.ws.rs.core.Context - public ServletContext servletContext; - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(Resource.class); - - private static final boolean writeStatistics; - - static { - writeStatistics = DSpaceServicesFactory.getInstance().getConfigurationService() - .getBooleanProperty("rest.stats", false); - } - - /** - * Create context to work with DSpace database. It can create context - * with or without a logged in user (retrieved from SecurityContextHolder). Throws - * WebApplicationException caused by: SQLException if there was a problem - * with reading from database. Throws AuthorizeException if there was - * a problem with authorization to read from the database. Throws Exception - * if there was a problem creating context. - * - * @return Newly created context with the logged in user unless the specified user was null. - * If user is null, create the context without a logged in user. - * @throws ContextException Thrown in case of a problem creating context. Can be caused by - * SQLException error in creating context or finding the user to - * log in. Can be caused by AuthorizeException if there was a - * problem authorizing the found user. - * @throws SQLException An exception that provides information on a database access error or other errors. - */ - protected static org.dspace.core.Context createContext() throws ContextException, SQLException { - org.dspace.core.Context context = new org.dspace.core.Context(); - //context.getDBConnection().setAutoCommit(false); // Disable autocommit. - - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null) { - Collection specialGroups = (Collection) authentication - .getAuthorities(); - for (SimpleGrantedAuthority grantedAuthority : specialGroups) { - context.setSpecialGroup(EPersonServiceFactory.getInstance().getGroupService() - .findByName(context, grantedAuthority.getAuthority()) - .getID()); - } - context.setCurrentUser( - EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, authentication.getName())); - } - - return context; - } - - /** - * Records a statistics event about an object used via REST API. - * - * @param dspaceObject DSpace object on which a request was performed. - * @param action Action that was performed. - * @param user_ip User's IP address. - * @param user_agent User agent string (specifies browser used and its version). - * @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the - * source of the request. The proxy may be configured to add the - * "X-Forwarded-For" HTTP header containing the original IP of the client - * so that the reverse-proxied application can get the client's IP. - * @param headers If you want to access the item as the user logged into the - * context. The header "rest-dspace-token" with the token passed - * from the login method must be set. - * @param request Servlet's HTTP request object. - * @param context Context which must be aborted. - */ - protected void writeStats(DSpaceObject dspaceObject, UsageEvent.Action action, - String user_ip, String user_agent, String xforwardedfor, HttpHeaders headers, - HttpServletRequest request, Context context) { - if (!writeStatistics) { - return; - } - - if ((user_ip == null) || (user_ip.length() == 0)) { - DSpaceServicesFactory.getInstance().getEventService() - .fireEvent(new UsageEvent(action, request, context, dspaceObject)); - } else { - DSpaceServicesFactory.getInstance().getEventService().fireEvent( - new UsageEvent(action, user_ip, user_agent, xforwardedfor, context, dspaceObject)); - } - - log.debug("fired event"); - } - - /** - * Process exception, print message to logger error stream and abort DSpace - * context. - * - * @param message Message, which will be printed to error stream. - * @param context Context which must be aborted. - * @throws WebApplicationException This exception is throw for user of REST api. - */ - protected static void processException(String message, org.dspace.core.Context context) - throws WebApplicationException { - if ((context != null) && (context.isValid())) { - context.abort(); - } - log.error(message); - throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); - } - - /** - * Process finally statement. It will print message to logger error stream - * and abort DSpace context, if was not properly ended. - * - * @param context Context which must be aborted. - * @throws WebApplicationException This exception is thrown for user of REST API. - */ - protected void processFinally(org.dspace.core.Context context) throws WebApplicationException { - if ((context != null) && (context.isValid())) { - context.abort(); - log.error("Something get wrong. Aborting context in finally statement."); - throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); - } - } - - /** - * Split string with regex ".". - * - * @param key String which will be splitted. - * @return String array filed with separated string. - */ - protected String[] mySplit(String key) { - ArrayList list = new ArrayList(); - int prev = 0; - for (int i = 0; i < key.length(); i++) { - if (key.charAt(i) == '.') { - list.add(key.substring(prev, i)); - prev = i + 1; - } else if (i + 1 == key.length()) { - list.add(key.substring(prev, i + 1)); - } - } - - if (list.size() == 2) { - list.add(null); - } - - return list.toArray(new String[0]); - } - - /** - * Return string representation of values - * org.dspace.core.Constants.{READ,WRITE,DELETE}. - * - * @param action Constant from org.dspace.core.Constants.* - * @return String representation. read or write or delete. - */ - protected String getActionString(int action) { - String actionStr; - switch (action) { - case org.dspace.core.Constants.READ: - actionStr = "read"; - break; - case org.dspace.core.Constants.WRITE: - actionStr = "write"; - break; - case org.dspace.core.Constants.DELETE: - actionStr = "delete"; - break; - case org.dspace.core.Constants.REMOVE: - actionStr = "remove"; - break; - case org.dspace.core.Constants.ADD: - actionStr = "add"; - break; - default: - actionStr = "(?action?)"; - break; - } - return actionStr; - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java b/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java deleted file mode 100644 index 26b1150229..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java +++ /dev/null @@ -1,301 +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.rest; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.sql.SQLException; -import java.util.Iterator; -import javax.servlet.ServletContext; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; -import org.dspace.authenticate.AuthenticationMethod; -import org.dspace.authenticate.ShibAuthentication; -import org.dspace.authenticate.factory.AuthenticateServiceFactory; -import org.dspace.authenticate.service.AuthenticationService; -import org.dspace.eperson.EPerson; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.EPersonService; -import org.dspace.rest.common.Status; -import org.dspace.rest.exceptions.ContextException; -import org.dspace.utils.DSpace; - -/** - * Root of RESTful api. It provides login and logout. Also have method for - * printing every method which is provides by RESTful api. - * - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -@Path("/") -public class RestIndex { - protected EPersonService epersonService = EPersonServiceFactory.getInstance().getEPersonService(); - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RestIndex.class); - - /** - * Return html page with information about REST api. It contains methods all - * methods provide by REST api. - * - * @param servletContext Context of the servlet container. - * @return HTML page which has information about all methods of REST API. - */ - @GET - @Produces(MediaType.TEXT_HTML) - public String sayHtmlHello(@Context ServletContext servletContext) { - // TODO Better graphics, add arguments to all methods. (limit, offset, item and so on) - return "DSpace REST - index" + - "" - + "

DSpace REST API (Deprecated)

" + - "This REST API is deprecated and will be removed in v8." + - " Please use the new Server API webapp instead.
" + - "Server path: " + servletContext.getContextPath() + - "

Index

" + - "
    " + - "
  • GET / - Return this page.
  • " + - "
  • GET /test - Return the string \"REST api is running\" for testing purposes.
  • " + - "
  • POST /login - Method for logging into the DSpace RESTful API. You must post the parameters \"email\"" + - " and \"password\". Example: \"email=test@dspace&password=pass\". Returns a JSESSIONID cookie which can " + - "be used for future authenticated requests.
  • " + - "
  • POST /logout - Method for logging out of the DSpace RESTful API. The request must include the " + - "\"rest-dspace-token\" token
  • header." + - "
" + - "

Communities

" + - "
    " + - "
  • GET /communities - Return an array of all communities in DSpace.
  • " + - "
  • GET /communities/top-communities - Returns an array of all top-leve communities in DSpace.
  • " + - "
  • GET /communities/{communityId} - Returns a community with the specified ID.
  • " + - "
  • GET /communities/{communityId}/collections - Returns an array of collections of the specified " + - "community.
  • " + - "
  • GET /communities/{communityId}/communities - Returns an array of subcommunities of the specified " + - "community.
  • " + - "
  • POST /communities - Create a new top-level community. You must post a community.
  • " + - "
  • POST /communities/{communityId}/collections - Create a new collection in the specified community. " + - "You must post a collection.
  • " + - "
  • POST /communities/{communityId}/communities - Create a new subcommunity in the specified community. " + - "You must post a community.
  • " + - "
  • PUT /communities/{communityId} - Update the specified community.
  • " + - "
  • DELETE /communities/{communityId} - Delete the specified community.
  • " + - "
  • DELETE /communities/{communityId}/collections/{collectionId} - Delete the specified collection in " + - "the specified community.
  • " + - "
  • DELETE /communities/{communityId}/communities/{communityId2} - Delete the specified subcommunity " + - "(communityId2) in the specified community (communityId).
  • " + - "
" + - "

Collections

" + - "
    " + - "
  • GET /collections - Return all DSpace collections in array.
  • " + - "
  • GET /collections/{collectionId} - Return a collection with the specified ID.
  • " + - "
  • GET /collections/{collectionId}/items - Return all items of the specified collection.
  • " + - "
  • POST /collections/{collectionId}/items - Create an item in the specified collection. You must post " + - "an item.
  • " + - "
  • POST /collections/find-collection - Find a collection by name.
  • " + - "
  • PUT /collections/{collectionId}
  • - Update the specified collection. You must post a collection." + - "
  • DELETE /collections/{collectionId} - Delete the specified collection from DSpace.
  • " + - "
  • DELETE /collections/{collectionId}/items/{itemId} - Delete the specified item (itemId) in the " + - "specified collection (collectionId).
  • " + - "
" + - "

Items

" + - "
    " + - "
  • GET /items - Return a list of items.
  • " + - "
  • GET /items/{item id} - Return the specified item.
  • " + - "
  • GET /items/{item id}/metadata - Return metadata of the specified item.
  • " + - "
  • GET /items/{item id}/bitstreams - Return bitstreams of the specified item.
  • " + - "
  • POST /items/find-by-metadata-field - Find items by the specified metadata value.
  • " + - "
  • POST /items/{item id}/metadata - Add metadata to the specified item.
  • " + - "
  • POST /items/{item id}/bitstreams - Add a bitstream to the specified item.
  • " + - "
  • PUT /items/{item id}/metadata - Update metadata in the specified item.
  • " + - "
  • DELETE /items/{item id} - Delete the specified item.
  • " + - "
  • DELETE /items/{item id}/metadata - Clear metadata of the specified item.
  • " + - "
  • DELETE /items/{item id}/bitstreams/{bitstream id} - Delete the specified bitstream of the specified " + - "item.
  • " + - "
" + - "

Bitstreams

" + - "
    " + - "
  • GET /bitstreams - Return all bitstreams in DSpace.
  • " + - "
  • GET /bitstreams/{bitstream id} - Return the specified bitstream.
  • " + - "
  • GET /bitstreams/{bitstream id}/policy - Return policies of the specified bitstream.
  • " + - "
  • GET /bitstreams/{bitstream id}/retrieve - Return the contents of the specified bitstream.
  • " + - "
  • POST /bitstreams/{bitstream id}/policy - Add a policy to the specified bitstream.
  • " + - "
  • PUT /bitstreams/{bitstream id}/data - Update the contents of the specified bitstream.
  • " + - "
  • PUT /bitstreams/{bitstream id} - Update metadata of the specified bitstream.
  • " + - "
  • DELETE /bitstreams/{bitstream id} - Delete the specified bitstream from DSpace.
  • " + - "
  • DELETE /bitstreams/{bitstream id}/policy/{policy_id} - Delete the specified bitstream policy.
  • " + - "
" + - "

Hierarchy

" + - "
    " + - "
  • GET /hierarchy - Return hierarchy of communities and collections in tree form. Each object is " + - "minimally populated (name, handle, id) for efficient retrieval.
  • " + - "
" + - "

Metadata and Schema Registry

" + - "
    " + - "
  • GET /registries/schema - Return the list of metadata schemas in the registry
  • " + - "
  • GET /registries/schema/{schema_prefix} - Returns the specified metadata schema
  • " + - "
  • GET /registries/schema/{schema_prefix}/metadata-fields/{element} - Returns the metadata field within" + - " a schema with an unqualified element name
  • " + - "
  • GET /registries/schema/{schema_prefix}/metadata-fields/{element}/{qualifier} - Returns the metadata " + - "field within a schema with a qualified element name
  • " + - "
  • POST /registries/schema/ - Add a schema to the schema registry
  • " + - "
  • POST /registries/schema/{schema_prefix}/metadata-fields - Add a metadata field to the specified " + - "schema
  • " + - "
  • GET /registries/metadata-fields/{field_id} - Return the specified metadata field
  • " + - "
  • PUT /registries/metadata-fields/{field_id} - Update the specified metadata field
  • " + - "
  • DELETE /registries/metadata-fields/{field_id} - Delete the specified metadata field from the " + - "metadata field registry
  • " + - "
  • DELETE /registries/schema/{schema_id} - Delete the specified schema from the schema registry
  • " + - "
" + - "

Query/Reporting Tools

" + - "
    " + - "
  • GET /reports - Return a list of report tools built on the rest api
  • " + - "
  • GET /reports/{nickname} - Return a redirect to a specific report
  • " + - "
  • GET /filters - Return a list of use case filters available for quality control reporting
  • " + - "
  • GET /filtered-collections - Return collections and item counts based on pre-defined filters
  • " + - "
  • GET /filtered-collections/{collection_id} - Return items and item counts for a collection based on " + - "pre-defined filters
  • " + - "
  • GET /filtered-items - Retrieve a set of items based on a metadata query and a set of filters
  • " + - "
" + - " "; - } - - /** - * Method only for testing whether the REST API is running. - * - * @return String "REST api is running." - */ - @GET - @Path("/test") - public String test() { - return "REST api is running."; - } - - /** - * Method to login a user into REST API. - * - * @return Returns response code OK and a token. Otherwise returns response - * code FORBIDDEN(403). - */ - @POST - @Path("/login") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response login() { - //If you can get here, you are authenticated, the actual login is handled by spring security - return Response.ok().build(); - } - - @GET - @Path("/shibboleth-login") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response shibbolethLogin() { - //If you can get here, you are authenticated, the actual login is handled by spring security - return Response.ok().build(); - } - - @GET - @Path("/login-shibboleth") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response shibbolethLoginEndPoint() { - org.dspace.core.Context context = null; - try { - context = Resource.createContext(); - AuthenticationService authenticationService = AuthenticateServiceFactory.getInstance() - .getAuthenticationService(); - Iterator authenticationMethodIterator = authenticationService - .authenticationMethodIterator(); - while (authenticationMethodIterator.hasNext()) { - AuthenticationMethod authenticationMethod = authenticationMethodIterator.next(); - if (authenticationMethod instanceof ShibAuthentication) { - //TODO: Perhaps look for a better way of handling this ? - org.dspace.services.model.Request currentRequest = new DSpace().getRequestService() - .getCurrentRequest(); - String loginPageURL = authenticationMethod - .loginPageURL(context, currentRequest.getHttpServletRequest(), - currentRequest.getHttpServletResponse()); - if (StringUtils.isNotBlank(loginPageURL)) { - currentRequest.getHttpServletResponse().sendRedirect(loginPageURL); - } - } - } - context.abort(); - } catch (ContextException | SQLException | IOException e) { - Resource.processException("Shibboleth endpoint error: " + e.getMessage(), context); - } finally { - if (context != null && context.isValid()) { - context.abort(); - } - - } - return Response.ok().build(); - } - - /** - * Method to logout a user from DSpace REST API. Removes the token and user from - * TokenHolder. - * - * @param headers Request header which contains the header named - * "rest-dspace-token" containing the token as value. - * @return Return response OK, otherwise BAD_REQUEST, if there was a problem with - * logout or the token is incorrect. - */ - @POST - @Path("/logout") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response logout(@Context HttpHeaders headers) { - //If you can get here, you are logged out, this actual logout is handled by spring security - return Response.ok().build(); - } - - /** - * Method to check current status of the service and logged in user. - * - * okay: true | false - * authenticated: true | false - * epersonEMAIL: user@example.com - * epersonNAME: John Doe - * - * @param headers Request header which contains the header named - * "rest-dspace-token" containing the token as value. - * @return status the Status object with information about REST API - * @throws UnsupportedEncodingException The Character Encoding is not supported. - */ - @GET - @Path("/status") - @Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Status status(@Context HttpHeaders headers) - throws UnsupportedEncodingException { - org.dspace.core.Context context = null; - - try { - context = Resource.createContext(); - EPerson ePerson = context.getCurrentUser(); - - if (ePerson != null) { - //DB EPerson needed since token won't have full info, need context - EPerson dbEPerson = epersonService.findByEmail(context, ePerson.getEmail()); - - Status status = new Status(dbEPerson.getEmail(), dbEPerson.getFullName()); - return status; - } - } catch (ContextException e) { - Resource.processException("Status context error: " + e.getMessage(), context); - } catch (SQLException e) { - Resource.processException("Status eperson db lookup error: " + e.getMessage(), context); - } finally { - context.abort(); - } - - //fallback status, unauth - return new Status(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/RestReports.java b/dspace-rest/src/main/java/org/dspace/rest/RestReports.java deleted file mode 100644 index 4af556b6f8..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/RestReports.java +++ /dev/null @@ -1,86 +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.rest; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -import org.apache.logging.log4j.Logger; -import org.dspace.rest.common.Report; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; - - -/** - * Root of RESTful api. It provides login and logout. Also have method for - * printing every method which is provides by RESTful api. - * - * @author Terry Brady, Georgetown University - */ -@Path("/reports") -public class RestReports { - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RestReports.class); - - protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - public static final String REST_RPT_URL = "rest.report-url."; - - /** - * Return html page with information about REST api. It contains methods all - * methods provide by REST api. - * - * @return HTML page which has information about all methods of REST api. - */ - @GET - @Produces(MediaType.APPLICATION_XML) - public Report[] reportIndex() - throws WebApplicationException { - ArrayList reports = new ArrayList(); - List propNames = configurationService.getPropertyKeys("rest"); - for (String propName : propNames) { - if (propName.startsWith(REST_RPT_URL)) { - String nickname = propName.substring(REST_RPT_URL.length()); - String url = configurationService.getProperty(propName); - reports.add(new Report(nickname, url)); - } - } - return reports.toArray(new Report[0]); - } - - @Path("/{report_nickname}") - @GET - public Response customReport(@PathParam("report_nickname") String report_nickname, @Context UriInfo uriInfo) - throws WebApplicationException { - URI uri = null; - if (!report_nickname.isEmpty()) { - log.info(String.format("Seeking report %s", report_nickname)); - String url = configurationService.getProperty(REST_RPT_URL + report_nickname); - - log.info(String.format("URL for report %s found: [%s]", report_nickname, url)); - if (!url.isEmpty()) { - uri = uriInfo.getBaseUriBuilder().path(url).build(""); - log.info(String.format("URI for report %s", uri)); - } - } - - if (uri != null) { - return Response.temporaryRedirect(uri).build(); - } - - return Response.noContent().build(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/authentication/DSpaceAuthenticationProvider.java b/dspace-rest/src/main/java/org/dspace/rest/authentication/DSpaceAuthenticationProvider.java deleted file mode 100644 index eac4c40111..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/authentication/DSpaceAuthenticationProvider.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.dspace.rest.authentication; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.dspace.authenticate.AuthenticationMethod; -import org.dspace.authenticate.factory.AuthenticateServiceFactory; -import org.dspace.authenticate.service.AuthenticationService; -import org.dspace.core.Context; -import org.dspace.core.LogHelper; -import org.dspace.eperson.EPerson; -import org.dspace.eperson.Group; -import org.dspace.utils.DSpace; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.authority.SimpleGrantedAuthority; - -/** - * The core authentication & authorization provider, this provider is called when logging in & will process - * - * @author Roeland Dillen (roeland at atmire dot com) - * @author kevinvandevelde at atmire.com - * - * FIXME This provider handles both the authorization as well as the authentication, - * due to the way that the DSpace authentication is implemented there is currently no other way to do this. - */ -public class DSpaceAuthenticationProvider implements AuthenticationProvider { - - private static final Logger log = LogManager.getLogger(); - - protected AuthenticationService authenticationService = AuthenticateServiceFactory.getInstance() - .getAuthenticationService(); - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - Context context = null; - - try { - context = new Context(); - String name = authentication.getName(); - String password = authentication.getCredentials().toString(); - HttpServletRequest httpServletRequest = new DSpace().getRequestService().getCurrentRequest() - .getHttpServletRequest(); - List grantedAuthorities = new ArrayList<>(); - - - int implicitStatus = authenticationService - .authenticateImplicit(context, null, null, null, httpServletRequest); - - if (implicitStatus == AuthenticationMethod.SUCCESS) { - log.info(LogHelper.getHeader(context, "login", "type=implicit")); - addSpecialGroupsToGrantedAuthorityList(context, httpServletRequest, grantedAuthorities); - return createAuthenticationToken(password, context, grantedAuthorities); - - } else { - int authenticateResult = authenticationService - .authenticate(context, name, password, null, httpServletRequest); - if (AuthenticationMethod.SUCCESS == authenticateResult) { - addSpecialGroupsToGrantedAuthorityList(context, httpServletRequest, grantedAuthorities); - - log.info(LogHelper.getHeader(context, "login", "type=explicit")); - - return createAuthenticationToken(password, context, grantedAuthorities); - - } else { - log.info(LogHelper.getHeader(context, "failed_login", - "email=" + name + ", result=" + authenticateResult)); - throw new BadCredentialsException("Login failed"); - } - } - } catch (BadCredentialsException e) { - throw e; - } catch (Exception e) { - log.error("Error while authenticating in the rest api", e); - } finally { - if (context != null && context.isValid()) { - try { - context.complete(); - } catch (SQLException e) { - log.error(e.getMessage() + " occurred while trying to close", e); - } - } - } - - return null; - } - - protected void addSpecialGroupsToGrantedAuthorityList(Context context, HttpServletRequest httpServletRequest, - List grantedAuthorities) - throws SQLException { - List groups = authenticationService.getSpecialGroups(context, httpServletRequest); - for (Group group : groups) { - grantedAuthorities.add(new SimpleGrantedAuthority(group.getName())); - } - } - - private Authentication createAuthenticationToken(final String password, final Context context, - final List grantedAuthorities) { - EPerson ePerson = context.getCurrentUser(); - if (ePerson != null && StringUtils.isNotBlank(ePerson.getEmail())) { - return new UsernamePasswordAuthenticationToken(ePerson.getEmail(), password, grantedAuthorities); - - } else { - log.info(LogHelper.getHeader(context, "failed_login", - "No eperson with an non-blank e-mail address found")); - throw new BadCredentialsException("Login failed"); - } - } - - @Override - public boolean supports(Class authentication) { - return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); - } -} \ No newline at end of file diff --git a/dspace-rest/src/main/java/org/dspace/rest/authentication/NoRedirectAuthenticationLoginSuccessHandler.java b/dspace-rest/src/main/java/org/dspace/rest/authentication/NoRedirectAuthenticationLoginSuccessHandler.java deleted file mode 100644 index af146f27b7..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/authentication/NoRedirectAuthenticationLoginSuccessHandler.java +++ /dev/null @@ -1,41 +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.rest.authentication; - -import java.io.IOException; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.web.RedirectStrategy; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; - -/** - * @author kevinvandevelde at atmire.com - * - * Spring redirects to the home page after a successfull login. This success handles ensures that this is NOT the case. - */ -public class NoRedirectAuthenticationLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - - @PostConstruct - public void afterPropertiesSet() { - setRedirectStrategy(new NoRedirectStrategy()); - } - - protected class NoRedirectStrategy implements RedirectStrategy { - - @Override - public void sendRedirect(HttpServletRequest request, - HttpServletResponse response, String url) throws IOException { - // no redirect - - } - - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/authentication/NoRedirectAuthenticationLogoutSuccessHandler.java b/dspace-rest/src/main/java/org/dspace/rest/authentication/NoRedirectAuthenticationLogoutSuccessHandler.java deleted file mode 100644 index db28f2e388..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/authentication/NoRedirectAuthenticationLogoutSuccessHandler.java +++ /dev/null @@ -1,39 +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.rest.authentication; - -import java.io.IOException; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.web.RedirectStrategy; -import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; - -/** - * @author kevinvandevelde at atmire.com - * - * Spring redirects to the home page after a successfull logout. This success handles ensures that this is NOT the case. - */ -public class NoRedirectAuthenticationLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { - @PostConstruct - public void afterPropertiesSet() { - setRedirectStrategy(new NoRedirectStrategy()); - } - - protected class NoRedirectStrategy implements RedirectStrategy { - - @Override - public void sendRedirect(HttpServletRequest request, - HttpServletResponse response, String url) throws IOException { - // no redirect - - } - - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/Bitstream.java b/dspace-rest/src/main/java/org/dspace/rest/common/Bitstream.java deleted file mode 100644 index 7eb198990e..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/Bitstream.java +++ /dev/null @@ -1,199 +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.rest.common; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.servlet.ServletContext; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; -import org.dspace.content.Bundle; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.BitstreamService; -import org.dspace.content.service.BundleService; -import org.dspace.core.Constants; -import org.dspace.core.Context; -import org.dspace.utils.DSpace; - -/** - * Created with IntelliJ IDEA. - * User: peterdietz - * Date: 9/21/13 - * Time: 12:54 AM - * To change this template use File | Settings | File Templates. - */ -@XmlRootElement(name = "bitstream") -public class Bitstream extends DSpaceObject { - protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService(); - - Logger log = org.apache.logging.log4j.LogManager.getLogger(Bitstream.class); - - private String bundleName; - private String description; - private String format; - private String mimeType; - private Long sizeBytes; - private DSpaceObject parentObject; - private String retrieveLink; - private CheckSum checkSum; - private Integer sequenceId; - - private ResourcePolicy[] policies = null; - - public Bitstream() { - - } - - public Bitstream(org.dspace.content.Bitstream bitstream, ServletContext servletContext, String expand, - Context context) - throws SQLException { - super(bitstream, servletContext); - setup(bitstream, servletContext, expand, context); - } - - public void setup(org.dspace.content.Bitstream bitstream, ServletContext servletContext, String expand, - Context context) - throws SQLException { - List expandFields = new ArrayList(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - - //A logo bitstream might not have a bundle... - if (bitstream.getBundles() != null && !bitstream.getBundles().isEmpty()) { - if (bitstreamService.getParentObject(context, bitstream).getType() == Constants.ITEM) { - bundleName = bitstream.getBundles().get(0).getName(); - } - } - - description = bitstream.getDescription(); - format = bitstreamService.getFormatDescription(context, bitstream); - sizeBytes = bitstream.getSizeBytes(); - String path = new DSpace().getRequestService().getCurrentRequest().getHttpServletRequest().getContextPath(); - retrieveLink = path + "/bitstreams/" + bitstream.getID() + "/retrieve"; - mimeType = bitstreamService.getFormat(context, bitstream).getMIMEType(); - sequenceId = bitstream.getSequenceID(); - CheckSum checkSum = new CheckSum(); - checkSum.setCheckSumAlgorith(bitstream.getChecksumAlgorithm()); - checkSum.setValue(bitstream.getChecksum()); - this.setCheckSum(checkSum); - - if (expandFields.contains("parent") || expandFields.contains("all")) { - parentObject = new DSpaceObject(bitstreamService.getParentObject(context, bitstream), servletContext); - } else { - this.addExpand("parent"); - } - - if (expandFields.contains("policies") || expandFields.contains("all")) { - // Find policies without context. - List tempPolicies = new ArrayList(); - List bundles = bitstream.getBundles(); - for (Bundle bundle : bundles) { - List bitstreamsPolicies = bundleService - .getBitstreamPolicies(context, bundle); - for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies) { - if (policy.getdSpaceObject().equals(bitstream)) { - tempPolicies.add(new ResourcePolicy(policy)); - } - } - } - - policies = tempPolicies.toArray(new ResourcePolicy[0]); - } else { - this.addExpand("policies"); - } - - if (!expandFields.contains("all")) { - this.addExpand("all"); - } - } - - public Integer getSequenceId() { - return sequenceId; - } - - public void setSequenceId(Integer sequenceId) { - this.sequenceId = sequenceId; - } - - public String getBundleName() { - return bundleName; - } - - public void setBundleName(String bundleName) { - this.bundleName = bundleName; - } - - public void setDescription(String description) { - this.description = description; - } - - public void setFormat(String format) { - this.format = format; - } - - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - public void setSizeBytes(Long sizeBytes) { - this.sizeBytes = sizeBytes; - } - - public void setParentObject(DSpaceObject parentObject) { - this.parentObject = parentObject; - } - - public void setRetrieveLink(String retrieveLink) { - this.retrieveLink = retrieveLink; - } - - public String getDescription() { - return description; - } - - public String getFormat() { - return format; - } - - public String getMimeType() { - return mimeType; - } - - public Long getSizeBytes() { - return sizeBytes; - } - - public String getRetrieveLink() { - return retrieveLink; - } - - public DSpaceObject getParentObject() { - return parentObject; - } - - public CheckSum getCheckSum() { - return checkSum; - } - - public void setCheckSum(CheckSum checkSum) { - this.checkSum = checkSum; - } - - public ResourcePolicy[] getPolicies() { - return policies; - } - - public void setPolicies(ResourcePolicy[] policies) { - this.policies = policies; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/CheckSum.java b/dspace-rest/src/main/java/org/dspace/rest/common/CheckSum.java deleted file mode 100644 index 2db36ae9a0..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/CheckSum.java +++ /dev/null @@ -1,40 +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.rest.common; - -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; - -@XmlType -public class CheckSum { - String checkSumAlgorithm; - String value; - - public CheckSum() { - } - - @XmlAttribute(name = "checkSumAlgorithm") - public String getCheckSumAlgorith() { - return checkSumAlgorithm; - } - - public void setCheckSumAlgorith(String checkSumAlgorith) { - this.checkSumAlgorithm = checkSumAlgorith; - } - - @XmlValue - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/Collection.java b/dspace-rest/src/main/java/org/dspace/rest/common/Collection.java deleted file mode 100644 index be6e698b4d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/Collection.java +++ /dev/null @@ -1,225 +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.rest.common; - -import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION; -import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import javax.servlet.ServletContext; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; - -/** - * Created with IntelliJ IDEA. - * User: peterdietz - * Date: 5/22/13 - * Time: 9:41 AM - */ -@XmlRootElement(name = "collection") -public class Collection extends DSpaceObject { - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - - Logger log = org.apache.logging.log4j.LogManager.getLogger(Collection.class); - - //Relationships - private Bitstream logo; - private Community parentCommunity; - private List parentCommunityList = new ArrayList<>(); - - private List items = new ArrayList<>(); - - //Collection-Metadata - private String license; - private String copyrightText; - private String introductoryText; - private String shortDescription; - private String sidebarText; - - //Calculated - private Integer numberItems; - - public Collection() { - } - - public Collection(org.dspace.content.Collection collection, ServletContext servletContext, String expand, - Context context, Integer limit, Integer offset) - throws SQLException, WebApplicationException { - super(collection, servletContext); - setup(collection, servletContext, expand, context, limit, offset); - } - - private void setup(org.dspace.content.Collection collection, ServletContext servletContext, String expand, - Context context, Integer limit, Integer offset) - throws SQLException { - List expandFields = new ArrayList<>(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - - this.setCopyrightText(collectionService.getMetadataFirstValue(collection, - MD_COPYRIGHT_TEXT, org.dspace.content.Item.ANY)); - this.setIntroductoryText(collectionService.getMetadataFirstValue(collection, - MD_INTRODUCTORY_TEXT, org.dspace.content.Item.ANY)); - this.setShortDescription(collectionService.getMetadataFirstValue(collection, - MD_SHORT_DESCRIPTION, org.dspace.content.Item.ANY)); - this.setSidebarText(collectionService.getMetadataFirstValue(collection, - MD_SIDEBAR_TEXT, org.dspace.content.Item.ANY)); - - if (expandFields.contains("parentCommunityList") || expandFields.contains("all")) { - List parentCommunities = communityService.getAllParents(context, collection); - for (org.dspace.content.Community parentCommunity : parentCommunities) { - this.addParentCommunityList(new Community(parentCommunity, servletContext, null, context)); - } - } else { - this.addExpand("parentCommunityList"); - } - - if (expandFields.contains("parentCommunity") | expandFields.contains("all")) { - org.dspace.content.Community parentCommunity = - (org.dspace.content.Community) collectionService - .getParentObject(context, collection); - this.setParentCommunity(new Community( - parentCommunity, servletContext, null, context)); - } else { - this.addExpand("parentCommunity"); - } - - //TODO: Item paging. limit, offset/page - if (expandFields.contains("items") || expandFields.contains("all")) { - Iterator childItems = - itemService.findByCollection(context, collection, limit, offset); - - items = new ArrayList<>(); - while (childItems.hasNext()) { - org.dspace.content.Item item = childItems.next(); - - if (itemService.isItemListedForUser(context, item)) { - items.add(new Item(item, servletContext, null, context)); - } - } - } else { - this.addExpand("items"); - } - - if (expandFields.contains("license") || expandFields.contains("all")) { - setLicense(collectionService.getLicense(collection)); - } else { - this.addExpand("license"); - } - - if (expandFields.contains("logo") || expandFields.contains("all")) { - if (collection.getLogo() != null) { - this.logo = new Bitstream(collection.getLogo(), servletContext, null, context); - } - } else { - this.addExpand("logo"); - } - - if (!expandFields.contains("all")) { - this.addExpand("all"); - } - - this.setNumberItems(itemService.countItems(context, collection)); - } - - public Bitstream getLogo() { - return logo; - } - - public Integer getNumberItems() { - return numberItems; - } - - public void setNumberItems(Integer numberItems) { - this.numberItems = numberItems; - } - - public Community getParentCommunity() { - return parentCommunity; - } - - public void setParentCommunity(Community parentCommunity) { - this.parentCommunity = parentCommunity; - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } - - public void setParentCommunityList(List parentCommunityList) { - this.parentCommunityList = parentCommunityList; - } - - public List getParentCommunityList() { - return parentCommunityList; - } - - public void addParentCommunityList(Community parentCommunity) { - this.parentCommunityList.add(parentCommunity); - } - - public String getLicense() { - return license; - } - - public void setLicense(String license) { - this.license = license; - } - - public String getCopyrightText() { - return copyrightText; - } - - public void setCopyrightText(String copyrightText) { - this.copyrightText = copyrightText; - } - - public String getIntroductoryText() { - return introductoryText; - } - - public void setIntroductoryText(String introductoryText) { - this.introductoryText = introductoryText; - } - - public String getShortDescription() { - return shortDescription; - } - - public void setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - } - - public String getSidebarText() { - return sidebarText; - } - - public void setSidebarText(String sidebarText) { - this.sidebarText = sidebarText; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/Community.java b/dspace-rest/src/main/java/org/dspace/rest/common/Community.java deleted file mode 100644 index e6e4716eab..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/Community.java +++ /dev/null @@ -1,217 +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.rest.common; - -import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT; -import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION; -import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.servlet.ServletContext; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CommunityService; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; - -/** - * Created with IntelliJ IDEA. - * User: peterdietz - * Date: 5/22/13 - * Time: 9:41 AM - * To change this template use File | Settings | File Templates. - */ -@XmlRootElement(name = "community") -public class Community extends DSpaceObject { - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(Community.class); - - //Exandable relationships - private Bitstream logo; - - private Community parentCommunity; - - private String copyrightText; - private String introductoryText; - private String shortDescription; - private String sidebarText; - private Integer countItems; - - private List subcommunities = new ArrayList<>(); - - private List collections = new ArrayList<>(); - - public Community() { - } - - public Community(org.dspace.content.Community community, ServletContext servletContext, String expand, - Context context) - throws SQLException, WebApplicationException { - super(community, servletContext); - setup(community, servletContext, expand, context); - } - - private void setup(org.dspace.content.Community community, ServletContext servletContext, String expand, - Context context) - throws SQLException { - List expandFields = new ArrayList<>(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - - this.setCopyrightText(communityService.getMetadataFirstValue(community, - MD_COPYRIGHT_TEXT, org.dspace.content.Item.ANY)); - this.setIntroductoryText(communityService.getMetadataFirstValue(community, - MD_INTRODUCTORY_TEXT, org.dspace.content.Item.ANY)); - this.setShortDescription(communityService.getMetadataFirstValue(community, - MD_SHORT_DESCRIPTION, org.dspace.content.Item.ANY)); - this.setSidebarText(communityService.getMetadataFirstValue(community, - MD_SIDEBAR_TEXT, org.dspace.content.Item.ANY)); - this.setCountItems(itemService.countItems(context, community)); - - if (expandFields.contains("parentCommunity") || expandFields.contains("all")) { - org.dspace.content.Community parentCommunity = (org.dspace.content.Community) communityService - .getParentObject(context, community); - if (parentCommunity != null) { - setParentCommunity(new Community(parentCommunity, servletContext, null, context)); - } - } else { - this.addExpand("parentCommunity"); - } - - if (expandFields.contains("collections") || expandFields.contains("all")) { - List collections = community.getCollections(); - List restCollections = new ArrayList<>(); - - for (org.dspace.content.Collection collection : collections) { - if (authorizeService.authorizeActionBoolean(context, collection, org.dspace.core.Constants.READ)) { - restCollections.add(new Collection(collection, servletContext, null, context, null, null)); - } else { - log.info("Omitted restricted collection: " + collection.getID() + " _ " + collection.getName()); - } - } - setCollections(restCollections); - } else { - this.addExpand("collections"); - } - - if (expandFields.contains("subCommunities") || expandFields.contains("all")) { - List communities = community.getSubcommunities(); - subcommunities = new ArrayList<>(); - for (org.dspace.content.Community subCommunity : communities) { - if (authorizeService.authorizeActionBoolean(context, subCommunity, org.dspace.core.Constants.READ)) { - subcommunities.add(new Community(subCommunity, servletContext, null, context)); - } else { - log.info( - "Omitted restricted subCommunity: " + subCommunity.getID() + " _ " + subCommunity.getName()); - } - } - } else { - this.addExpand("subCommunities"); - } - - if (expandFields.contains("logo") || expandFields.contains("all")) { - if (community.getLogo() != null) { - logo = new Bitstream(community.getLogo(), servletContext, null, context); - } - } else { - this.addExpand("logo"); - } - - if (!expandFields.contains("all")) { - this.addExpand("all"); - } - } - - public List getCollections() { - return collections; - } - - public void setCollections(List collections) { - this.collections = collections; - } - - public Integer getCountItems() { - return countItems; - } - - public void setCountItems(Integer countItems) { - this.countItems = countItems; - } - - public String getSidebarText() { - return sidebarText; - } - - public void setSidebarText(String sidebarText) { - this.sidebarText = sidebarText; - } - - public String getShortDescription() { - return shortDescription; - } - - public void setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - } - - public String getIntroductoryText() { - return introductoryText; - } - - public void setIntroductoryText(String introductoryText) { - this.introductoryText = introductoryText; - } - - public String getCopyrightText() { - return copyrightText; - } - - public void setCopyrightText(String copyrightText) { - this.copyrightText = copyrightText; - } - - public Community getParentCommunity() { - return parentCommunity; - } - - public void setParentCommunity(Community parentCommunity) { - this.parentCommunity = parentCommunity; - } - - public Bitstream getLogo() { - return logo; - } - - public void setLogo(Bitstream logo) { - this.logo = logo; - } - - // Renamed because of xml annotation exception with this attribute and getSubCommunities. - @XmlElement(name = "subcommunities", required = true) - public List getSubcommunities() { - return subcommunities; - } - - public void setSubcommunities(List subcommunities) { - this.subcommunities = subcommunities; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/DSpaceObject.java b/dspace-rest/src/main/java/org/dspace/rest/common/DSpaceObject.java deleted file mode 100644 index 08df254336..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/DSpaceObject.java +++ /dev/null @@ -1,107 +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.rest.common; - -import java.util.ArrayList; -import java.util.List; -import javax.servlet.ServletContext; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import org.atteo.evo.inflector.English; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.DSpaceObjectService; - -/** - * Created with IntelliJ IDEA. - * User: peterdietz - * Date: 10/7/13 - * Time: 12:11 PM - * To change this template use File | Settings | File Templates. - */ -@XmlRootElement(name = "dspaceobject") -public class DSpaceObject { - - private String uuid; - - private String name; - private String handle; - private String type; - - @XmlElement(name = "link", required = true) - private String link; - - @XmlElement(required = true) - private ArrayList expand = new ArrayList(); - - public DSpaceObject() { - - } - - public DSpaceObject(org.dspace.content.DSpaceObject dso, ServletContext servletContext) { - setUUID(dso.getID().toString()); - setName(dso.getName()); - setHandle(dso.getHandle()); - DSpaceObjectService dspaceObjectService = ContentServiceFactory.getInstance().getDSpaceObjectService(dso); - setType(dspaceObjectService.getTypeText(dso).toLowerCase()); - link = createLink(servletContext); - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getHandle() { - return handle; - } - - public void setHandle(String handle) { - this.handle = handle; - } - - public String getLink() { - return link; - } - - public String getType() { - return this.type; - } - - public void setType(String type) { - this.type = type; - } - - - public List getExpand() { - return expand; - } - - public void setExpand(ArrayList expand) { - this.expand = expand; - } - - public void addExpand(String expandableAttribute) { - this.expand.add(expandableAttribute); - } - - public String getUUID() { - return uuid; - } - - public void setUUID(String uuid) { - this.uuid = uuid; - } - - private String createLink(ServletContext context) { - return context.getContextPath() + "/" + English.plural(getType()) + "/" + getUUID(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/FilteredCollection.java b/dspace-rest/src/main/java/org/dspace/rest/common/FilteredCollection.java deleted file mode 100644 index c7ff0ef9b3..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/FilteredCollection.java +++ /dev/null @@ -1,191 +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.rest.common; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import javax.servlet.ServletContext; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; -import org.dspace.rest.filter.ItemFilterSet; - -/** - * Retrieve items within a collection that match a specific set of Item Filters of interest - * - * @author Terry Brady, Georgetown University - */ -@XmlRootElement(name = "filtered-collection") -public class FilteredCollection extends DSpaceObject { - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); - protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - Logger log = org.apache.logging.log4j.LogManager.getLogger(FilteredCollection.class); - - //Relationships - private Community parentCommunity; - private Community topCommunity; - private List parentCommunityList = new ArrayList(); - - private List items = new ArrayList(); - - private List itemFilters = new ArrayList(); - - //Calculated - private Integer numberItems; - private Integer numberItemsProcessed; - - public FilteredCollection() { - } - - /** - * Evaluate a collection against of set of Item Filters - * - * @param collection DSpace Collection to evaluate - * @param servletContext Context of the servlet container. - * @param filters String representing a list of filters - * @param expand String in which is what you want to add to returned instance - * of collection. Options are: "all", "parentCommunityList", - * "parentCommunity", "items", "license" and "logo". If you want - * to use multiple options, it must be separated by commas. - * @param context The relevant DSpace Context. - * @param limit Limit value for items in list in collection. Default value is 100. - * @param offset Offset of start index in list of items of collection. Default - * value is 0. - * @throws SQLException An exception that provides information on a database access error or other - * errors. - * @throws WebApplicationException Runtime exception for applications. - */ - public FilteredCollection(org.dspace.content.Collection collection, ServletContext servletContext, String filters, - String expand, Context context, Integer limit, Integer offset) - throws SQLException, WebApplicationException { - super(collection, servletContext); - setup(collection, servletContext, expand, context, limit, offset, filters); - } - - private void setup(org.dspace.content.Collection collection, ServletContext servletContext, String expand, - Context context, Integer limit, Integer offset, String filters) throws SQLException { - List expandFields = new ArrayList(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - - if (expandFields.contains("parentCommunityList") || expandFields.contains("all")) { - List parentCommunities = communityService.getAllParents(context, collection); - List parentCommunityList = new ArrayList(); - for (org.dspace.content.Community parentCommunity : parentCommunities) { - parentCommunityList.add(new Community(parentCommunity, servletContext, null, context)); - } - this.setParentCommunityList(parentCommunityList); - } else { - this.addExpand("parentCommunityList"); - } - - if (expandFields.contains("parentCommunity") | expandFields.contains("all")) { - org.dspace.content.Community parentCommunity = collection.getCommunities().get(0); - this.setParentCommunity(new Community(parentCommunity, servletContext, null, context)); - } else { - this.addExpand("parentCommunity"); - } - - if (expandFields.contains("topCommunity") | expandFields.contains("all")) { - List parentCommunities = communityService.getAllParents(context, collection); - if (parentCommunities.size() > 0) { - org.dspace.content.Community topCommunity = parentCommunities.get(parentCommunities.size() - 1); - this.setTopCommunity(new Community(topCommunity, servletContext, null, context)); - } - } else { - this.addExpand("topCommunity"); - } - - - boolean reportItems = expandFields.contains("items") || expandFields.contains("all"); - ItemFilterSet itemFilterSet = new ItemFilterSet(filters, reportItems); - this.setItemFilters(itemFilterSet.getItemFilters()); - - this.setNumberItemsProcessed(0); - if (itemFilters.size() > 0) { - Iterator childItems = itemService - .findAllByCollection(context, collection, limit, offset); - int numProc = itemFilterSet - .processSaveItems(context, servletContext, childItems, items, reportItems, expand); - this.setNumberItemsProcessed(numProc); - } - - if (!expandFields.contains("all")) { - this.addExpand("all"); - } - this.setNumberItems(itemService.countAllItems(context, collection)); - } - - public Integer getNumberItems() { - return numberItems; - } - - public void setNumberItems(Integer numberItems) { - this.numberItems = numberItems; - } - - public Integer getNumberItemsProcessed() { - return numberItemsProcessed; - } - - public void setNumberItemsProcessed(Integer numberItemsProcessed) { - this.numberItemsProcessed = numberItemsProcessed; - } - - public Community getParentCommunity() { - return parentCommunity; - } - - public void setParentCommunity(Community parentCommunity) { - this.parentCommunity = parentCommunity; - } - - public Community getTopCommunity() { - return topCommunity; - } - - public void setTopCommunity(Community topCommunity) { - this.topCommunity = topCommunity; - } - - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } - - public void setParentCommunityList(List parentCommunityList) { - this.parentCommunityList = parentCommunityList; - } - - public List getParentCommunityList() { - return parentCommunityList; - } - - public List getItemFilters() { - return itemFilters; - } - - public void setItemFilters(List itemFilters) { - this.itemFilters = itemFilters; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyCollection.java b/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyCollection.java deleted file mode 100644 index 6c40faf62b..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyCollection.java +++ /dev/null @@ -1,24 +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.rest.common; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Used to handle/determine status of REST API. - * Mainly to know your authentication status - */ -@XmlRootElement(name = "collection") -public class HierarchyCollection extends HierarchyObject { - public HierarchyCollection() { - } - - public HierarchyCollection(String id, String name, String handle) { - super(id, name, handle); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyCommunity.java b/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyCommunity.java deleted file mode 100644 index 3618608e3e..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyCommunity.java +++ /dev/null @@ -1,44 +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.rest.common; - -import java.util.ArrayList; -import java.util.List; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name = "community") -public class HierarchyCommunity extends HierarchyObject { - private List communities = new ArrayList(); - private List collections = new ArrayList(); - - public HierarchyCommunity() { - } - - public HierarchyCommunity(String id, String name, String handle) { - super(id, name, handle); - } - - @XmlElement(name = "community") - public List getCommunities() { - return communities; - } - - public void setCommunities(List communities) { - this.communities = communities; - } - - @XmlElement(name = "collection") - public List getCollections() { - return collections; - } - - public void setCollections(List collections) { - this.collections = collections; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyObject.java b/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyObject.java deleted file mode 100644 index 0074eeea6a..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchyObject.java +++ /dev/null @@ -1,51 +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.rest.common; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name = "object") -public class HierarchyObject { - //id may be a numeric id or a uuid depending on the version of DSpace - private String id; - private String name; - private String handle; - - public HierarchyObject() { - } - - public HierarchyObject(String id, String name, String handle) { - setId(id); - setName(name); - setHandle(handle); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getHandle() { - return handle; - } - - public void setHandle(String handle) { - this.handle = handle; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchySite.java b/dspace-rest/src/main/java/org/dspace/rest/common/HierarchySite.java deleted file mode 100644 index 5eb2cc523c..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/HierarchySite.java +++ /dev/null @@ -1,24 +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.rest.common; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Used to handle/determine status of REST API. - * Mainly to know your authentication status - */ -@XmlRootElement(name = "site") -public class HierarchySite extends HierarchyCommunity { - public HierarchySite() { - } - - public HierarchySite(String id, String name, String handle) { - super(id, name, handle); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/Item.java b/dspace-rest/src/main/java/org/dspace/rest/common/Item.java deleted file mode 100644 index 3794153b7d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/Item.java +++ /dev/null @@ -1,219 +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.rest.common; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.servlet.ServletContext; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; -import org.dspace.app.util.factory.UtilServiceFactory; -import org.dspace.app.util.service.MetadataExposureService; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.Bundle; -import org.dspace.content.MetadataField; -import org.dspace.content.MetadataValue; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; - -/** - * Created with IntelliJ IDEA. - * User: peterdietz - * Date: 9/19/13 - * Time: 4:50 PM - * To change this template use File | Settings | File Templates. - */ -@SuppressWarnings("deprecation") -@XmlRootElement(name = "item") -public class Item extends DSpaceObject { - protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - protected MetadataExposureService metadataExposureService = UtilServiceFactory.getInstance() - .getMetadataExposureService(); - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - - Logger log = org.apache.logging.log4j.LogManager.getLogger(Item.class); - - String isArchived; - String isWithdrawn; - String lastModified; - - Collection parentCollection; - List parentCollectionList; - List parentCommunityList; - List metadata; - List bitstreams; - - public Item() { - } - - public Item(org.dspace.content.Item item, ServletContext servletContext, String expand, Context context) - throws SQLException, WebApplicationException { - super(item, servletContext); - setup(item, servletContext, expand, context); - } - - private void setup(org.dspace.content.Item item, ServletContext servletContext, String expand, Context context) - throws SQLException { - List expandFields = new ArrayList(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - - if (expandFields.contains("metadata") || expandFields.contains("all")) { - metadata = new ArrayList(); - List metadataValues = itemService.getMetadata( - item, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, - org.dspace.content.Item.ANY, org.dspace.content.Item.ANY); - - for (MetadataValue metadataValue : metadataValues) { - MetadataField metadataField = metadataValue.getMetadataField(); - if (!metadataExposureService.isHidden(context, - metadataField.getMetadataSchema().getName(), - metadataField.getElement(), - metadataField.getQualifier())) { - metadata.add(new MetadataEntry(metadataField.toString('.'), - metadataValue.getValue(), metadataValue.getLanguage())); - } - } - } else { - this.addExpand("metadata"); - } - - this.setArchived(Boolean.toString(item.isArchived())); - this.setWithdrawn(Boolean.toString(item.isWithdrawn())); - this.setLastModified(item.getLastModified().toString()); - - if (expandFields.contains("parentCollection") || expandFields.contains("all")) { - if (item.getOwningCollection() != null) { - this.parentCollection = new Collection(item.getOwningCollection(), - servletContext, null, context, null, null); - } else { - this.addExpand("parentCollection"); - } - } else { - this.addExpand("parentCollection"); - } - - if (expandFields.contains("parentCollectionList") || expandFields.contains("all")) { - this.parentCollectionList = new ArrayList(); - List collections = item.getCollections(); - for (org.dspace.content.Collection collection : collections) { - this.parentCollectionList.add(new Collection(collection, - servletContext, null, context, null, null)); - } - } else { - this.addExpand("parentCollectionList"); - } - - if (expandFields.contains("parentCommunityList") || expandFields.contains("all")) { - this.parentCommunityList = new ArrayList(); - List communities = itemService.getCommunities(context, item); - - for (org.dspace.content.Community community : communities) { - this.parentCommunityList.add(new Community(community, servletContext, null, context)); - } - } else { - this.addExpand("parentCommunityList"); - } - - //TODO: paging - offset, limit - if (expandFields.contains("bitstreams") || expandFields.contains("all")) { - bitstreams = new ArrayList(); - - List bundles = item.getBundles(); - for (Bundle bundle : bundles) { - - List itemBitstreams = bundle.getBitstreams(); - for (org.dspace.content.Bitstream itemBitstream : itemBitstreams) { - if (authorizeService - .authorizeActionBoolean(context, itemBitstream, org.dspace.core.Constants.READ)) { - bitstreams.add(new Bitstream(itemBitstream, servletContext, null, context)); - } - } - } - } else { - this.addExpand("bitstreams"); - } - - if (!expandFields.contains("all")) { - this.addExpand("all"); - } - } - - public String getArchived() { - return isArchived; - } - - public void setArchived(String archived) { - isArchived = archived; - } - - public String getWithdrawn() { - return isWithdrawn; - } - - public void setWithdrawn(String withdrawn) { - isWithdrawn = withdrawn; - } - - public String getLastModified() { - return lastModified; - } - - public void setLastModified(String lastModified) { - this.lastModified = lastModified; - } - - public Collection getParentCollection() { - return parentCollection; - } - - public List getParentCollectionList() { - return parentCollectionList; - } - - public List getMetadata() { - return metadata; - } - - public List getBitstreams() { - return bitstreams; - } - - public List getParentCommunityList() { - return parentCommunityList; - } - - public void setParentCollection(Collection parentCollection) { - this.parentCollection = parentCollection; - } - - public void setParentCollectionList(List parentCollectionList) { - this.parentCollectionList = parentCollectionList; - } - - public void setParentCommunityList(List parentCommunityList) { - this.parentCommunityList = parentCommunityList; - } - - @XmlElement(required = true) - public void setMetadata(List metadata) { - this.metadata = metadata; - } - - public void setBitstreams(List bitstreams) { - this.bitstreams = bitstreams; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/ItemFilter.java b/dspace-rest/src/main/java/org/dspace/rest/common/ItemFilter.java deleted file mode 100644 index bc5bd13134..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/ItemFilter.java +++ /dev/null @@ -1,274 +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.rest.common; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; -import org.dspace.core.Context; -import org.dspace.core.factory.CoreServiceFactory; -import org.dspace.rest.filter.ItemFilterDefs; -import org.dspace.rest.filter.ItemFilterList; -import org.dspace.rest.filter.ItemFilterTest; - - -/** - * Use Case Item Filters that match a specific set of criteria. - * - * @author Terry Brady, Georgetown University - */ -@XmlRootElement(name = "item-filter") -public class ItemFilter { - static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemFilter.class); - - private ItemFilterTest itemFilterTest = null; - private String filterName = ""; - private String title; - private String description; - private String category; - private String queryAnnotation; - private List items = new ArrayList(); - private List itemFilterQueries = new ArrayList(); - private List metadata = new ArrayList(); - private Integer itemCount; - private Integer unfilteredItemCount; - private boolean saveItems = false; - - public ItemFilter() { - } - - public static final String ALL_FILTERS = "all_filters"; - public static final String ALL = "all"; - - public static List getItemFilters(String filters, boolean saveItems) { - LinkedHashMap availableTests = new LinkedHashMap(); - for (ItemFilterList plugobj : - (ItemFilterList[]) CoreServiceFactory.getInstance() - .getPluginService().getPluginSequence(ItemFilterList.class)) { - for (ItemFilterTest defFilter : plugobj.getFilters()) { - availableTests.put(defFilter.getName(), defFilter); - } - } - List itemFilters = new ArrayList(); - ItemFilter allFilters = new ItemFilter(ItemFilter.ALL_FILTERS, "Matches all specified filters", - "This filter includes all items that matched ALL specified filters", - ItemFilterDefs.CAT_ITEM, saveItems); - - if (filters.equals(ALL)) { - for (ItemFilterTest itemFilterDef : availableTests.values()) { - itemFilters.add(new ItemFilter(itemFilterDef, saveItems)); - } - itemFilters.add(allFilters); - } else { - for (String filter : Arrays.asList(filters.split(","))) { - if (filter.equals(ItemFilter.ALL_FILTERS)) { - continue; - } - - ItemFilterTest itemFilterDef; - itemFilterDef = availableTests.get(filter); - if (itemFilterDef == null) { - continue; - } - itemFilters.add(new ItemFilter(itemFilterDef, saveItems)); - } - itemFilters.add(allFilters); - } - return itemFilters; - } - - public static ItemFilter getAllFiltersFilter(List itemFilters) { - for (ItemFilter itemFilter : itemFilters) { - if (itemFilter.getFilterName().equals(ALL_FILTERS)) { - itemFilter.initCount(); - return itemFilter; - } - } - return null; - } - - public ItemFilter(ItemFilterTest itemFilterTest, boolean saveItems) - throws WebApplicationException { - this.itemFilterTest = itemFilterTest; - this.saveItems = saveItems; - setup(itemFilterTest.getName(), itemFilterTest.getTitle(), - itemFilterTest.getDescription(), itemFilterTest.getCategory()); - } - - public ItemFilter(String name, String title, String description, String category, boolean saveItems) - throws WebApplicationException { - this.saveItems = saveItems; - setup(name, title, description, category); - } - - private void setup(String name, String title, String description, String category) { - this.setFilterName(name); - this.setTitle(title); - this.setDescription(description); - this.setCategory(category); - } - - private void initCount() { - if (itemCount == null) { - itemCount = 0; - } - if (unfilteredItemCount == null) { - unfilteredItemCount = 0; - } - } - - public boolean hasItemTest() { - return itemFilterTest != null; - } - - public void addItem(org.dspace.rest.common.Item restItem) { - initCount(); - if (saveItems) { - items.add(restItem); - } - itemCount++; - } - - public boolean testItem(Context context, org.dspace.content.Item item, org.dspace.rest.common.Item restItem) { - initCount(); - if (itemFilterTest == null) { - return false; - } - if (itemFilterTest.testItem(context, item)) { - addItem(restItem); - return true; - } - return false; - } - - @XmlAttribute(name = "filter-name") - public String getFilterName() { - return filterName; - } - - public void setFilterName(String name) { - this.filterName = name; - } - - @XmlAttribute(name = "title") - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - @XmlAttribute(name = "category") - public String getCategory() { - return category; - } - - public void setCategory(String category) { - this.category = category; - } - - @XmlAttribute(name = "description") - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - @XmlAttribute(name = "query-annotation") - public String getQueryAnnotation() { - return queryAnnotation; - } - - public void annotateQuery(List query_field, List query_op, List query_val) - throws SQLException { - int index = Math.min(query_field.size(), Math.min(query_op.size(), query_val.size())); - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < index; i++) { - if (!sb.toString().isEmpty()) { - sb.append(" and "); - } - sb.append("("); - sb.append(query_field.get(i)); - sb.append(" "); - sb.append(query_op.get(i)); - sb.append(" "); - sb.append(query_val.get(i)); - sb.append(")"); - } - setQueryAnnotation(sb.toString()); - } - - public void setQueryAnnotation(String queryAnnotation) { - this.queryAnnotation = queryAnnotation; - } - - @XmlAttribute(name = "item-count") - public Integer getItemCount() { - return itemCount; - } - - public void setItemCount(Integer itemCount) { - this.itemCount = itemCount; - } - - @XmlAttribute(name = "unfiltered-item-count") - public Integer getUnfilteredItemCount() { - return unfilteredItemCount; - } - - public void setUnfilteredItemCount(Integer unfilteredItemCount) { - this.unfilteredItemCount = unfilteredItemCount; - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } - - public List getItemFilterQueries() { - return itemFilterQueries; - } - - public void setItemFilterQueries(List itemFilterQueries) { - this.itemFilterQueries = itemFilterQueries; - } - - public void initMetadataList(List show_fields) { - if (show_fields != null) { - List returnFields = new ArrayList(); - for (String field : show_fields) { - returnFields.add(new MetadataEntry(field, null, null)); - } - setMetadata(returnFields); - } - } - - public List getMetadata() { - return metadata; - } - - @XmlElement(required = true) - public void setMetadata(List metadata) { - this.metadata = metadata; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/ItemFilterQuery.java b/dspace-rest/src/main/java/org/dspace/rest/common/ItemFilterQuery.java deleted file mode 100644 index 6f56e2b44c..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/ItemFilterQuery.java +++ /dev/null @@ -1,77 +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.rest.common; - -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; - -import org.apache.logging.log4j.Logger; - -/** - * Metadata Query for DSpace Items using the REST API - * - * @author Terry Brady, Georgetown University - */ -@XmlRootElement(name = "item-filter-query") -public class ItemFilterQuery { - Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemFilterQuery.class); - - private String field = ""; - private String operation = ""; - private String value = ""; - - public ItemFilterQuery() { - } - - /** - * Construct a metadata query for DSpace items - * - * @param field Name of the metadata field to query - * @param operation Operation to perform on a metadata field - * @param value Query value. - * @throws WebApplicationException Runtime exception for applications. - */ - public ItemFilterQuery(String field, String operation, String value) throws WebApplicationException { - setup(field, operation, value); - } - - private void setup(String field, String operation, String value) { - this.setField(field); - this.setOperation(operation); - this.setValue(value); - } - - @XmlAttribute(name = "field") - public String getField() { - return field; - } - - public void setField(String field) { - this.field = field; - } - - @XmlAttribute(name = "operation") - public String getOperation() { - return operation; - } - - public void setOperation(String operation) { - this.operation = operation; - } - - @XmlAttribute(name = "value") - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/MetadataEntry.java b/dspace-rest/src/main/java/org/dspace/rest/common/MetadataEntry.java deleted file mode 100644 index 27f31cec9c..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/MetadataEntry.java +++ /dev/null @@ -1,77 +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.rest.common; - -import java.util.regex.Pattern; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * @author peterdietz, Rostislav Novak (Computing and Information Centre, CTU in - * Prague) - */ -@XmlRootElement(name = "metadataentry") -public class MetadataEntry { - String key; - - String value; - - String language; - - public MetadataEntry() { - } - - public MetadataEntry(String key, String value, String language) { - this.key = key; - this.value = value; - this.language = language; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public String getSchema() { - String[] fieldPieces = key.split(Pattern.quote(".")); - return fieldPieces[0]; - } - - public String getElement() { - String[] fieldPieces = key.split(Pattern.quote(".")); - return fieldPieces[1]; - } - - public String getQualifier() { - String[] fieldPieces = key.split(Pattern.quote(".")); - if (fieldPieces.length == 3) { - return fieldPieces[2]; - } else { - return null; - } - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/MetadataField.java b/dspace-rest/src/main/java/org/dspace/rest/common/MetadataField.java deleted file mode 100644 index 3688b5b8ca..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/MetadataField.java +++ /dev/null @@ -1,132 +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.rest.common; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import org.dspace.core.Context; - -/** - * Metadata field representation - * - * @author Terry Brady, Georgetown University. - */ -@XmlRootElement(name = "field") -public class MetadataField { - private int fieldId; - private String name; - private String element; - private String qualifier; - private String description; - - private MetadataSchema parentSchema; - - @XmlElement(required = true) - private ArrayList expand = new ArrayList(); - - public MetadataField() { - } - - public MetadataField(org.dspace.content.MetadataSchema schema, org.dspace.content.MetadataField field, - String expand, Context context) throws SQLException, WebApplicationException { - setup(schema, field, expand, context); - } - - private void setup(org.dspace.content.MetadataSchema schema, org.dspace.content.MetadataField field, String expand, - Context context) throws SQLException { - List expandFields = new ArrayList(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - StringBuilder sb = new StringBuilder(); - sb.append(schema.getName()); - sb.append("."); - sb.append(field.getElement()); - if (field.getQualifier() != null) { - sb.append("."); - sb.append(field.getQualifier()); - } - - this.setName(sb.toString()); - this.setFieldId(field.getID()); - this.setElement(field.getElement()); - this.setQualifier(field.getQualifier()); - this.setDescription(field.getScopeNote()); - - if (expandFields.contains("parentSchema") || expandFields.contains("all")) { - this.addExpand("parentSchema"); - parentSchema = new MetadataSchema(schema, "", context); - } - } - - public void setParentSchema(MetadataSchema schema) { - this.parentSchema = schema; - } - - public MetadataSchema getParentSchema() { - return this.parentSchema; - } - - public void setFieldId(int fieldId) { - this.fieldId = fieldId; - } - - public void setName(String name) { - this.name = name; - } - - public void setElement(String element) { - this.element = element; - } - - public void setQualifier(String qualifier) { - this.qualifier = qualifier; - } - - public void setDescription(String description) { - this.description = description; - } - - public int getFieldId() { - return fieldId; - } - - public String getName() { - return name; - } - - public String getQualifier() { - return qualifier; - } - - public String getElement() { - return element; - } - - public String getDescription() { - return description; - } - - public List getExpand() { - return expand; - } - - public void setExpand(ArrayList expand) { - this.expand = expand; - } - - public void addExpand(String expandableAttribute) { - this.expand.add(expandableAttribute); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/MetadataSchema.java b/dspace-rest/src/main/java/org/dspace/rest/common/MetadataSchema.java deleted file mode 100644 index 4b1e29fea2..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/MetadataSchema.java +++ /dev/null @@ -1,105 +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.rest.common; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.ws.rs.WebApplicationException; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.MetadataFieldService; -import org.dspace.core.Context; - -/** - * Metadata schema representation - * - * @author Terry Brady, Georgetown University. - */ -@XmlRootElement(name = "schema") -public class MetadataSchema { - protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService(); - - private int schemaID; - private String prefix; - private String namespace; - - @XmlElement(required = true) - private ArrayList expand = new ArrayList(); - - @XmlElement(name = "fields", required = true) - private List fields = new ArrayList(); - - public MetadataSchema() { - } - - public MetadataSchema(org.dspace.content.MetadataSchema schema, String expand, Context context) - throws SQLException, WebApplicationException { - setup(schema, expand, context); - } - - private void setup(org.dspace.content.MetadataSchema schema, String expand, Context context) throws SQLException { - List expandFields = new ArrayList(); - if (expand != null) { - expandFields = Arrays.asList(expand.split(",")); - } - this.setSchemaID(schema.getID()); - this.setPrefix(schema.getName()); - this.setNamespace(schema.getNamespace()); - if (expandFields.contains("fields") || expandFields.contains("all")) { - List fields = metadataFieldService.findAllInSchema(context, schema); - this.addExpand("fields"); - for (org.dspace.content.MetadataField field : fields) { - this.fields.add(new MetadataField(schema, field, "", context)); - } - } - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public void setNamespace(String namespace) { - this.namespace = namespace; - } - - public String getPrefix() { - return prefix; - } - - public String getNamespace() { - return namespace; - } - - public int getSchemaID() { - return this.schemaID; - } - - public void setSchemaID(int schemaID) { - this.schemaID = schemaID; - } - - public List getMetadataFields() { - return fields; - } - - public List getExpand() { - return expand; - } - - public void setExpand(ArrayList expand) { - this.expand = expand; - } - - public void addExpand(String expandableAttribute) { - this.expand.add(expandableAttribute); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/Report.java b/dspace-rest/src/main/java/org/dspace/rest/common/Report.java deleted file mode 100644 index dcaf7d269e..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/Report.java +++ /dev/null @@ -1,47 +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.rest.common; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Used to handle/determine status of REST API. - * Mainly to know your authentication status - */ -@XmlRootElement(name = "report") -public class Report { - private String nickname; - private String url; - - public Report() { - setNickname("na"); - setUrl(""); - } - - - public Report(String nickname, String url) { - setNickname(nickname); - setUrl(url); - } - - public String getUrl() { - return this.url; - } - - public String getNickname() { - return this.nickname; - } - - public void setUrl(String url) { - this.url = url; - } - - public void setNickname(String nickname) { - this.nickname = nickname; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/ResourcePolicy.java b/dspace-rest/src/main/java/org/dspace/rest/common/ResourcePolicy.java deleted file mode 100644 index 366bd5fc3a..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/ResourcePolicy.java +++ /dev/null @@ -1,195 +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.rest.common; - -import java.util.Date; -import javax.xml.bind.annotation.XmlRootElement; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.dspace.eperson.EPerson; -import org.dspace.eperson.Group; - -@XmlRootElement(name = "resourcepolicy") -public class ResourcePolicy { - - public enum Action { - READ, WRITE, DELETE; - } - - private Integer id; - private Action action; - private String epersonId; //UUID - private String groupId; //UUID - private String resourceId; //UUID - private String resourceType; - private String rpDescription; - private String rpName; - private String rpType; - private Date startDate; - private Date endDate; - - public ResourcePolicy() { - } - - public ResourcePolicy(org.dspace.authorize.ResourcePolicy dspacePolicy) { - this.id = dspacePolicy.getID(); - - switch (dspacePolicy.getAction()) { - case org.dspace.core.Constants.READ: - this.action = Action.READ; - break; - case org.dspace.core.Constants.WRITE: - this.action = Action.WRITE; - break; - case org.dspace.core.Constants.DELETE: - this.action = Action.DELETE; - break; - default: - break; - } - - EPerson ePerson = dspacePolicy.getEPerson(); - if (ePerson != null) { - this.epersonId = ePerson.getID().toString(); - } - - Group group = dspacePolicy.getGroup(); - if (group != null) { - this.groupId = group.getID().toString(); - } - - this.resourceId = dspacePolicy.getdSpaceObject().getID().toString(); - this.rpDescription = dspacePolicy.getRpDescription(); - this.rpName = dspacePolicy.getRpName(); - this.rpType = dspacePolicy.getRpType(); - this.startDate = dspacePolicy.getStartDate(); - this.endDate = dspacePolicy.getEndDate(); - switch (dspacePolicy.getdSpaceObject().getType()) { - case org.dspace.core.Constants.BITSTREAM: - this.resourceType = "bitstream"; - break; - case org.dspace.core.Constants.ITEM: - this.resourceType = "item"; - break; - case org.dspace.core.Constants.COLLECTION: - this.resourceType = "collection"; - break; - case org.dspace.core.Constants.COMMUNITY: - this.resourceType = "community"; - break; - case org.dspace.core.Constants.BUNDLE: - this.resourceType = "bundle"; - break; - default: - this.resourceType = ""; - break; - } - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public Action getAction() { - return action; - } - - @JsonIgnore - public int getActionInt() { - switch (action) { - case READ: - return org.dspace.core.Constants.READ; - case WRITE: - return org.dspace.core.Constants.WRITE; - case DELETE: - return org.dspace.core.Constants.DELETE; - default: - return org.dspace.core.Constants.READ; - } - } - - public void setAction(Action action) { - this.action = action; - } - - public String getEpersonId() { - return epersonId; - } - - public void setEpersonId(String epersonId) { - this.epersonId = epersonId; - } - - public String getGroupId() { - return groupId; - } - - public void setGroupId(String groupId) { - this.groupId = groupId; - } - - public String getResourceId() { - return resourceId; - } - - public void setResourceId(String resourceId) { - this.resourceId = resourceId; - } - - public String getResourceType() { - return resourceType; - } - - public void setResourceType(String resourceType) { - this.resourceType = resourceType; - } - - public String getRpDescription() { - return rpDescription; - } - - public void setRpDescription(String rpDescription) { - this.rpDescription = rpDescription; - } - - public String getRpName() { - return rpName; - } - - public void setRpName(String rpName) { - this.rpName = rpName; - } - - public String getRpType() { - return rpType; - } - - public void setRpType(String rpType) { - this.rpType = rpType; - } - - public Date getStartDate() { - return startDate; - } - - public void setStartDate(Date startDate) { - this.startDate = startDate; - } - - public Date getEndDate() { - return endDate; - } - - public void setEndDate(Date endDate) { - this.endDate = endDate; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/common/Status.java b/dspace-rest/src/main/java/org/dspace/rest/common/Status.java deleted file mode 100644 index cdbb8210b9..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/common/Status.java +++ /dev/null @@ -1,111 +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.rest.common; - -import javax.xml.bind.annotation.XmlRootElement; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.dspace.app.util.Util; -import org.dspace.eperson.EPerson; - -/** - * Determine status of REST API - is it running, accessible and without errors?. - * Find out API version (DSpace major version) and DSpace source version. - * Find out your authentication status. - */ -@XmlRootElement(name = "status") -public class Status { - private boolean okay; - private boolean authenticated; - private String email; - private String fullname; - private String sourceVersion; - private String apiVersion; - - public Status() { - setOkay(true); - - setSourceVersion(Util.getSourceVersion()); - String[] version = Util.getSourceVersion().split("\\."); - setApiVersion(version[0]); // major version - - setAuthenticated(false); - } - - public Status(String email, String fullname) { - setOkay(true); - setAuthenticated(true); - setEmail(email); - setFullname(fullname); - } - - public Status(EPerson eperson) { - setOkay(true); - if (eperson != null) { - setAuthenticated(true); - setEmail(eperson.getEmail()); - setFullname(eperson.getFullName()); - } else { - setAuthenticated(false); - } - } - - @JsonProperty("okay") - public boolean isOkay() { - return this.okay; - } - - public void setOkay(boolean okay) { - this.okay = okay; - } - - @JsonProperty("authenticated") - public boolean isAuthenticated() { - return authenticated; - } - - public void setAuthenticated(boolean authenticated) { - this.authenticated = authenticated; - } - - @JsonProperty("email") - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - @JsonProperty("fullname") - public String getFullname() { - return fullname; - } - - public void setFullname(String fullname) { - this.fullname = fullname; - } - - @JsonProperty("sourceVersion") - public String getSourceVersion() { - return this.sourceVersion; - } - - public void setSourceVersion(String sourceVersion) { - this.sourceVersion = sourceVersion; - } - - @JsonProperty("apiVersion") - public String getApiVersion() { - return this.apiVersion; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/exceptions/ContextException.java b/dspace-rest/src/main/java/org/dspace/rest/exceptions/ContextException.java deleted file mode 100644 index 817b662f73..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/exceptions/ContextException.java +++ /dev/null @@ -1,31 +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.rest.exceptions; - -/** - * Simple exception which only encapsulate classic exception. This exception is - * only for exceptions caused by creating context. - * - * @author Rostislav Novak (Computing and Information Centre, CTU in Prague) - */ -public class ContextException extends Exception { - - private static final long serialVersionUID = 1L; - - Exception causedBy; - - public ContextException(String message, Exception causedBy) { - super(message); - this.causedBy = causedBy; - } - - public Exception getCausedBy() { - return causedBy; - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefs.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefs.java deleted file mode 100644 index 0712ec546d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefs.java +++ /dev/null @@ -1,159 +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.rest.filter; - -import org.dspace.content.Item; -import org.dspace.core.Context; - -/** - * Define the set of use cases for filtering items of interest through the REST API. - * - * @author Terry Brady, Georgetown University - */ - -public class ItemFilterDefs implements ItemFilterList { - public static final String CAT_ITEM = "Item Property Filters"; - public static final String CAT_BASIC = "Basic Bitstream Filters"; - public static final String CAT_MIME = "Bitstream Filters by MIME Type"; - - public static final String[] MIMES_PDF = {"application/pdf"}; - public static final String[] MIMES_JPG = {"image/jpeg"}; - - - private enum EnumItemFilterDefs implements ItemFilterTest { - is_item("Is Item - always true", null, CAT_ITEM) { - public boolean testItem(Context context, Item item) { - return true; - } - }, - is_withdrawn("Withdrawn Items", null, CAT_ITEM) { - public boolean testItem(Context context, Item item) { - return item.isWithdrawn(); - } - }, - is_not_withdrawn("Available Items - Not Withdrawn", null, CAT_ITEM) { - public boolean testItem(Context context, Item item) { - return !item.isWithdrawn(); - } - }, - is_discoverable("Discoverable Items - Not Private", null, CAT_ITEM) { - public boolean testItem(Context context, Item item) { - return item.isDiscoverable(); - } - }, - is_not_discoverable("Not Discoverable - Private Item", null, CAT_ITEM) { - public boolean testItem(Context context, Item item) { - return !item.isDiscoverable(); - } - }, - has_multiple_originals("Item has Multiple Original Bitstreams", null, CAT_BASIC) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countOriginalBitstream(item) > 1; - } - }, - has_no_originals("Item has No Original Bitstreams", null, CAT_BASIC) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countOriginalBitstream(item) == 0; - } - }, - has_one_original("Item has One Original Bitstream", null, CAT_BASIC) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countOriginalBitstream(item) == 1; - } - }, - has_doc_original("Item has a Doc Original Bitstream (PDF, Office, Text, HTML, XML, etc)", null, CAT_MIME) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil - .countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes()) > 0; - } - }, - has_image_original("Item has an Image Original Bitstream", null, CAT_MIME) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image") > 0; - } - }, - has_unsupp_type("Has Other Bitstream Types (not Doc or Image)", null, ItemFilterDefs.CAT_MIME) { - public boolean testItem(Context context, Item 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; - } - }, - has_mixed_original("Item has multiple types of Original Bitstreams (Doc, Image, Other)", null, CAT_MIME) { - public boolean testItem(Context context, Item 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; - } - }, - has_pdf_original("Item has a PDF Original Bitstream", null, CAT_MIME) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countOriginalBitstreamMime(context, item, MIMES_PDF) > 0; - } - }, - has_jpg_original("Item has JPG Original Bitstream", null, CAT_MIME) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countOriginalBitstreamMime(context, item, MIMES_JPG) > 0; - } - },; - - private String title = null; - private String description = null; - - private EnumItemFilterDefs(String title, String description, String category) { - this.title = title; - this.description = description; - this.category = category; - } - - private EnumItemFilterDefs() { - this(null, null, null); - } - - public String getName() { - return name(); - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - private String category = null; - - public String getCategory() { - return category; - } - } - - public ItemFilterDefs() { - } - - public ItemFilterTest[] getFilters() { - return EnumItemFilterDefs.values(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsMeta.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsMeta.java deleted file mode 100644 index 96a866357d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsMeta.java +++ /dev/null @@ -1,177 +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.rest.filter; - -import java.util.regex.Pattern; - -import org.apache.logging.log4j.Logger; -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; - -/** - * Define the set of use cases for filtering items of interest through the REST API. - * - * @author Terry Brady, Georgetown University - */ - -public class ItemFilterDefsMeta implements ItemFilterList { - protected static ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemFilterDefsMeta.class); - - public static final String CAT_META_GEN = "General Metadata Filters"; - public static final String CAT_META_SPEC = "Specific Metadata Filters"; - public static final String CAT_MOD = "Recently Modified"; - - private enum EnumItemFilterDefs implements ItemFilterTest { - has_no_title("Has no dc.title", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - return itemService.getMetadataByMetadataString(item, "dc.title").size() == 0; - } - }, - has_no_uri("Has no dc.identifier.uri", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - return itemService.getMetadataByMetadataString(item, "dc.identifier.uri").size() == 0; - } - }, - has_mult_uri("Has multiple dc.identifier.uri", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - return itemService.getMetadataByMetadataString(item, "dc.identifier.uri").size() > 1; - } - }, - has_compound_subject("Has compound subject", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-compound-subject"); - return ItemFilterUtil.hasMetadataMatch(item, "dc.subject.*", Pattern.compile(regex)); - } - }, - has_compound_author("Has compound author", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-compound-author"); - return ItemFilterUtil - .hasMetadataMatch(item, "dc.creator,dc.contributor.author", Pattern.compile(regex)); - } - }, - has_empty_metadata("Has empty metadata", null, CAT_META_GEN) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile("^\\s*$")); - } - }, - has_unbreaking_metadata("Has unbreaking metadata", null, CAT_META_GEN) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-unbreaking"); - return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex)); - } - }, - has_long_metadata("Has long metadata field", null, CAT_META_GEN) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-long"); - return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex)); - } - }, - has_xml_entity("Has XML entity in metadata", null, CAT_META_GEN) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-xml-entity"); - return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex)); - } - }, - has_non_ascii("Has non-ascii in metadata", null, CAT_META_GEN) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-non-ascii"); - return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex)); - } - }, - has_desc_url("Has url in description", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-url"); - return ItemFilterUtil.hasMetadataMatch(item, "dc.description.*", Pattern.compile(regex)); - } - }, - has_fulltext_provenance("Has fulltext in provenance", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-fulltext"); - return ItemFilterUtil.hasMetadataMatch(item, "dc.description.provenance", Pattern.compile(regex)); - } - }, - no_fulltext_provenance("Doesn't have fulltext in provenance", null, CAT_META_SPEC) { - public boolean testItem(Context context, Item item) { - String regex = DSpaceServicesFactory.getInstance().getConfigurationService() - .getProperty("rest.report-regex-fulltext"); - return !ItemFilterUtil.hasMetadataMatch(item, "dc.description.provenance", Pattern.compile(regex)); - } - }, - mod_last_day("Modified in last 1 day", null, CAT_MOD) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.recentlyModified(item, 1); - } - }, - mod_last_7_days("Modified in last 7 days", null, CAT_MOD) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.recentlyModified(item, 7); - } - }, - mod_last_30_days("Modified in last 30 days", null, CAT_MOD) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.recentlyModified(item, 30); - } - }, - mod_last_90_days("Modified in last 60 days", null, CAT_MOD) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.recentlyModified(item, 60); - } - },; - - private String title = null; - private String description = null; - - private EnumItemFilterDefs(String title, String description, String category) { - this.title = title; - this.description = description; - this.category = category; - } - - private EnumItemFilterDefs() { - this(null, null, null); - } - - public String getName() { - return name(); - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - private String category = null; - - public String getCategory() { - return category; - } - } - - public ItemFilterDefsMeta() { - } - - public ItemFilterTest[] getFilters() { - return EnumItemFilterDefs.values(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsMisc.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsMisc.java deleted file mode 100644 index 5b5cc4b12d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsMisc.java +++ /dev/null @@ -1,206 +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.rest.filter; - -import java.util.List; - -import org.dspace.content.Item; -import org.dspace.core.Context; -import org.dspace.rest.filter.ItemFilterUtil.BundleName; -import org.dspace.services.factory.DSpaceServicesFactory; - -/** - * Define the set of use cases for filtering items of interest through the REST API. - * - * @author Terry Brady, Georgetown University - */ - -public class ItemFilterDefsMisc implements ItemFilterList { - public static final String CAT_MISC = "Bitstream Bundle Filters"; - public static final String CAT_MIME_SUPP = "Supported MIME Type Filters"; - - private enum EnumItemFilterDefs implements ItemFilterTest { - has_only_supp_image_type("Item Image Bitstreams are Supported", null, CAT_MIME_SUPP) { - public boolean testItem(Context context, Item item) { - int imageCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image/"); - if (imageCount == 0) { - return false; - } - int suppImageCount = ItemFilterUtil - .countOriginalBitstreamMime(context, item, ItemFilterUtil.getSupportedImageMimeTypes()); - return (imageCount == suppImageCount); - } - }, - has_unsupp_image_type("Item has Image Bitstream that is Unsupported", null, CAT_MIME_SUPP) { - public boolean testItem(Context context, Item 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; - } - }, - has_only_supp_doc_type("Item Document Bitstreams are Supported", null, CAT_MIME_SUPP) { - public boolean testItem(Context context, Item 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; - } - }, - has_unsupp_doc_type("Item has Document Bitstream that is Unsupported", null, CAT_MIME_SUPP) { - public boolean testItem(Context context, Item 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; - } - }, - has_small_pdf("Has unusually small PDF", null, ItemFilterDefs.CAT_MIME) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil - .countBitstreamSmallerThanMinSize(context, BundleName.ORIGINAL, item, ItemFilterDefs.MIMES_PDF, - "rest.report-pdf-min-size") > 0; - } - }, - has_large_pdf("Has unusually large PDF", null, ItemFilterDefs.CAT_MIME) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil - .countBitstreamLargerThanMaxSize(context, BundleName.ORIGINAL, item, ItemFilterDefs.MIMES_PDF, - "rest.report-pdf-max-size") > 0; - } - }, - has_unsupported_bundle("Has bitstream in an unsuppored bundle", null, CAT_MISC) { - public boolean testItem(Context context, Item item) { - String[] bundleList = DSpaceServicesFactory.getInstance().getConfigurationService() - .getArrayProperty("rest.report-supp-bundles"); - return ItemFilterUtil.hasUnsupportedBundle(item, bundleList); - } - }, - has_small_thumbnail("Has unusually small thumbnail", null, CAT_MISC) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil - .countBitstreamSmallerThanMinSize(context, BundleName.THUMBNAIL, item, ItemFilterDefs.MIMES_JPG, - "rest.report-thumbnail-min-size") > 0; - } - }, - has_doc_without_text("Has document bitstream without TEXT item", null, ItemFilterDefs.CAT_MIME) { - public boolean testItem(Context context, Item item) { - int countDoc = ItemFilterUtil - .countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes()); - if (countDoc == 0) { - return false; - } - int countText = ItemFilterUtil.countBitstream(BundleName.TEXT, item); - return countDoc > countText; - } - }, - has_original_without_thumbnail("Has original bitstream without thumbnail", null, CAT_MISC) { - public boolean testItem(Context context, Item item) { - int countBit = ItemFilterUtil.countOriginalBitstream(item); - if (countBit == 0) { - return false; - } - int countThumb = ItemFilterUtil.countBitstream(BundleName.THUMBNAIL, item); - return countBit > countThumb; - } - }, - has_invalid_thumbnail_name("Has invalid thumbnail name (assumes one thumbnail for each original)", null, - CAT_MISC) { - public boolean testItem(Context context, Item item) { - List originalNames = ItemFilterUtil.getBitstreamNames(BundleName.ORIGINAL, item); - List thumbNames = ItemFilterUtil.getBitstreamNames(BundleName.THUMBNAIL, item); - if (thumbNames.size() != originalNames.size()) { - return false; - } - for (String name : originalNames) { - if (!thumbNames.contains(name + ".jpg")) { - return true; - } - } - return false; - } - }, - has_non_generated_thumb("Has non generated thumbnail", null, CAT_MISC) { - public boolean testItem(Context context, Item 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); - } - }, - no_license("Doesn't have a license", null, CAT_MISC) { - public boolean testItem(Context context, Item item) { - return ItemFilterUtil.countBitstream(BundleName.LICENSE, item) == 0; - } - }, - has_license_documentation("Has documentation in the license bundle", null, CAT_MISC) { - public boolean testItem(Context context, Item item) { - List names = ItemFilterUtil.getBitstreamNames(BundleName.LICENSE, item); - for (String name : names) { - if (!name.equals("license.txt")) { - return true; - } - } - return false; - } - },; - - private String title = null; - private String description = null; - - private EnumItemFilterDefs(String title, String description, String category) { - this.title = title; - this.description = description; - this.category = category; - } - - private EnumItemFilterDefs() { - this(null, null, null); - } - - public String getName() { - return name(); - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - private String category = null; - - public String getCategory() { - return category; - } - } - - public ItemFilterDefsMisc() { - } - - public ItemFilterTest[] getFilters() { - return EnumItemFilterDefs.values(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsPerm.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsPerm.java deleted file mode 100644 index 9e80f31196..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterDefsPerm.java +++ /dev/null @@ -1,138 +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.rest.filter; - -import java.sql.SQLException; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.Bitstream; -import org.dspace.content.Bundle; -import org.dspace.content.Item; -import org.dspace.core.Context; -import org.dspace.rest.filter.ItemFilterUtil.BundleName; - -/** - * Define the set of use cases for filtering items of interest through the REST API. - * - * @author Terry Brady, Georgetown University - */ -public class ItemFilterDefsPerm implements ItemFilterList { - protected static AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - public static final String CAT_PERM = "Perimission Filters"; - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemFilterDefsPerm.class); - - public ItemFilterDefsPerm() { - } - - public enum EnumItemFilterPermissionDefs implements ItemFilterTest { - has_restricted_original("Item has Restricted Original Bitstream", - "Item has at least one original bitstream that is not accessible to Anonymous user", - CAT_PERM) { - public boolean testItem(Context context, Item item) { - try { - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(BundleName.ORIGINAL.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - if (!authorizeService - .authorizeActionBoolean(getAnonContext(), bit, org.dspace.core.Constants.READ)) { - return true; - } - } - } - } catch (SQLException e) { - ItemFilterDefsPerm.log.warn("SQL Exception testing original bitstream access " + e.getMessage(), e); - } - return false; - } - }, - has_restricted_thumbnail("Item has Restricted Thumbnail", - "Item has at least one thumbnail that is not accessible to Anonymous user", CAT_PERM) { - public boolean testItem(Context context, Item item) { - try { - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(BundleName.THUMBNAIL.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - if (!authorizeService - .authorizeActionBoolean(getAnonContext(), bit, org.dspace.core.Constants.READ)) { - return true; - } - } - } - } catch (SQLException e) { - ItemFilterDefsPerm.log - .warn("SQL Exception testing thumbnail bitstream access " + e.getMessage(), e); - } - return false; - } - }, - has_restricted_metadata("Item has Restricted Metadata", - "Item has metadata that is not accessible to Anonymous user", CAT_PERM) { - public boolean testItem(Context context, Item item) { - try { - return !authorizeService - .authorizeActionBoolean(getAnonContext(), item, org.dspace.core.Constants.READ); - } catch (SQLException e) { - ItemFilterDefsPerm.log.warn("SQL Exception testing item metadata access " + e.getMessage(), e); - return false; - } - } - },; - - private static Context anonContext; - - private static Context getAnonContext() { - if (anonContext == null) { - anonContext = new Context(); - } - return anonContext; - } - - - private String title = null; - private String description = null; - - private EnumItemFilterPermissionDefs(String title, String description, String category) { - this.title = title; - this.description = description; - this.category = category; - } - - private EnumItemFilterPermissionDefs() { - this(null, null, null); - } - - public String getName() { - return name(); - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - private String category = null; - - public String getCategory() { - return category; - } - } - - @Override - public ItemFilterTest[] getFilters() { - return EnumItemFilterPermissionDefs.values(); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterList.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterList.java deleted file mode 100644 index f6590e36f8..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterList.java +++ /dev/null @@ -1,12 +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.rest.filter; - -public interface ItemFilterList { - public ItemFilterTest[] getFilters(); -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterSet.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterSet.java deleted file mode 100644 index f70bc9664d..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterSet.java +++ /dev/null @@ -1,143 +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.rest.filter; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import javax.servlet.ServletContext; -import javax.ws.rs.WebApplicationException; - -import org.apache.logging.log4j.Logger; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.core.Context; -import org.dspace.rest.common.Item; -import org.dspace.rest.common.ItemFilter; - -/** - * The set of Item Filter Use Cases to apply to a collection of items. - * - * @author Terry Brady, Georgetown University - */ -public class ItemFilterSet { - protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); - static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemFilterSet.class); - - private List itemFilters; - private ItemFilter allFiltersFilter; - - /** - * Construct a set of Item Filters identified by a list string. - * - * @param filterList Comma separated list of filter names to include. - * Use {@link org.dspace.rest.common.ItemFilter#ALL} to retrieve all filters. - * @param reportItems If true, return item details. If false, return only counts of items. - */ - public ItemFilterSet(String filterList, boolean reportItems) { - log.debug(String.format("Create ItemFilterSet: %s", filterList)); - itemFilters = ItemFilter.getItemFilters(filterList, reportItems); - allFiltersFilter = ItemFilter.getAllFiltersFilter(itemFilters); - } - - /** - * Get the special filter that represents the intersection of all items in the Item Filter Set. - * - * @return the special Item Filter that contains items that satisfied every other Item Filter in the Item Filter Set - */ - public ItemFilter getAllFiltersFilter() { - return allFiltersFilter; - } - - /** - * Evaluate an item against the use cases in the Item Filter Set. - * - * If an item satisfies all items in the Item Filter Set, it should also ve added to the special all items filter. - * - * @param context Active DSpace Context - * @param item DSpace Object to evaluate - * @param restItem REST representation of the DSpace Object being evaluated - */ - public void testItem(Context context, org.dspace.content.Item item, Item restItem) { - boolean bAllTrue = true; - for (ItemFilter itemFilter : itemFilters) { - if (itemFilter.hasItemTest()) { - bAllTrue &= itemFilter.testItem(context, item, restItem); - } - } - if (bAllTrue && allFiltersFilter != null) { - allFiltersFilter.addItem(restItem); - } - } - - /** - * Get all of the Item Filters initialized into the Item Filter Set - * - * @return a list of Item Filters initialized into the Item Filter Set - */ - public List getItemFilters() { - return itemFilters; - } - - /** - * Evaluate a set of Items against the Item Filters in the Item Filter Set - * Current DSpace Context - * - * @param context Current DSpace Context - * @param servletContext Context of the servlet container. - * @param childItems Collection of Items to Evaluate - * @param save If true, save the details of each item that is evaluated - * @param expand List of item details to include in the results - * @return The number of items evaluated - * @throws WebApplicationException Runtime exception for applications. - * @throws SQLException An exception that provides information on a database access error or other - * errors. - */ - public int processSaveItems(Context context, ServletContext servletContext, - Iterator childItems, boolean save, String expand) - throws WebApplicationException, SQLException { - return processSaveItems(context, servletContext, childItems, new ArrayList(), save, expand); - } - - /** - * Evaluate a set of Items against the Item Filters in the Item Filter Set - * - * @param context Current DSpace Context - * @param servletContext Context of the servlet container. - * @param childItems Collection of Items to Evaluate - * @param items List of items to contain saved results - * @param save If true, save the details of each item that is evaluated - * @param expand List of item details to include in the results - * @return The number of items evaluated - * @throws WebApplicationException Runtime exception for applications. - * @throws SQLException An exception that provides information on a database access error or other - * errors. - */ - public int processSaveItems(Context context, ServletContext servletContext, - Iterator childItems, List items, boolean save, - String expand) throws WebApplicationException, SQLException { - int count = 0; - while (childItems.hasNext()) { - count++; - org.dspace.content.Item item = childItems.next(); - log.debug(item.getHandle() + " evaluate."); - if (authorizeService.authorizeActionBoolean(context, item, org.dspace.core.Constants.READ)) { - Item restItem = new Item(item, servletContext, expand, context); - if (save) { - items.add(restItem); - } - testItem(context, item, restItem); - } else { - log.debug(item.getHandle() + " not authorized - not included in result set."); - } - } - return count; - } - -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterTest.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterTest.java deleted file mode 100644 index 4ef2998e16..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterTest.java +++ /dev/null @@ -1,29 +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.rest.filter; - -import org.dspace.content.Item; -import org.dspace.core.Context; - -/** - * Item Filter Use Case Interface. - * Items will be evaluated against a set of filters. - * - * @author Terry Brady, Georgetown University - */ -public interface ItemFilterTest { - public String getName(); - - public String getTitle(); - - public String getDescription(); - - public String getCategory(); - - public boolean testItem(Context context, Item i); -} diff --git a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterUtil.java b/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterUtil.java deleted file mode 100644 index ddb75f0db8..0000000000 --- a/dspace-rest/src/main/java/org/dspace/rest/filter/ItemFilterUtil.java +++ /dev/null @@ -1,278 +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.rest.filter; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import com.ibm.icu.util.Calendar; -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.MetadataValue; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; -import org.dspace.services.factory.DSpaceServicesFactory; - -public class ItemFilterUtil { - protected static ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemFilterUtil.class); - - public enum BundleName { ORIGINAL, TEXT, LICENSE, THUMBNAIL } - - /** - * Default constructor - */ - 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"); - } - - static int countOriginalBitstream(Item item) { - return countBitstream(BundleName.ORIGINAL, item); - } - - static int countBitstream(BundleName bundleName, Item item) { - int count = 0; - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - count += bundle.getBitstreams().size(); - } - - return count; - } - - static List getBitstreamNames(BundleName bundleName, Item item) { - ArrayList names = new ArrayList(); - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - names.add(bit.getName()); - } - } - return names; - } - - - static int countOriginalBitstreamMime(Context context, Item item, String[] mimeList) { - return countBitstreamMime(context, BundleName.ORIGINAL, item, mimeList); - } - - static int countBitstreamMime(Context context, BundleName bundleName, Item item, String[] mimeList) { - int count = 0; - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - 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; - } - - static int countBitstreamByDesc(BundleName bundleName, Item item, String[] descList) { - int count = 0; - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - for (String desc : descList) { - String bitDesc = bit.getDescription(); - if (bitDesc == null) { - continue; - } - if (bitDesc.equals(desc.trim())) { - count++; - } - } - } - } - return count; - } - - static int countBitstreamSmallerThanMinSize(Context context, BundleName bundleName, Item item, String[] mimeList, - String prop) { - long size = DSpaceServicesFactory.getInstance().getConfigurationService().getLongProperty(prop); - int count = 0; - try { - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - for (String mime : mimeList) { - if (bit.getFormat(context).getMIMEType().equals(mime.trim())) { - if (bit.getSizeBytes() < size) { - count++; - } - } - } - } - } - } catch (SQLException e) { - // ignore - } - return count; - } - - static int countBitstreamLargerThanMaxSize(Context context, BundleName bundleName, Item item, String[] mimeList, - String prop) { - long size = DSpaceServicesFactory.getInstance().getConfigurationService().getLongProperty(prop); - int count = 0; - try { - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - for (String mime : mimeList) { - if (bit.getFormat(context).getMIMEType().equals(mime.trim())) { - if (bit.getSizeBytes() > size) { - count++; - } - } - } - } - } - } catch (SQLException e) { - // ignore - } - return count; - } - - static int countOriginalBitstreamMimeStartsWith(Context context, Item item, String prefix) { - return countBitstreamMimeStartsWith(context, BundleName.ORIGINAL, item, prefix); - } - - static int countBitstreamMimeStartsWith(Context context, BundleName bundleName, Item item, String prefix) { - int count = 0; - try { - for (Bundle bundle : item.getBundles()) { - if (!bundle.getName().equals(bundleName.name())) { - continue; - } - for (Bitstream bit : bundle.getBitstreams()) { - if (bit.getFormat(context).getMIMEType().startsWith(prefix)) { - count++; - } - } - } - } catch (SQLException e) { - // ignore - } - return count; - } - - static boolean hasUnsupportedBundle(Item item, String[] bundleList) { - if (bundleList == null) { - return false; - } - ArrayList bundles = new ArrayList(); - for (String bundleName : bundleList) { - bundles.add(bundleName.trim()); - } - for (Bundle bundle : item.getBundles()) { - if (!bundles.contains(bundle.getName())) { - return true; - } - } - return false; - } - - 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; - } - - static boolean hasMetadataMatch(Item item, String fieldList, Pattern regex) { - if (fieldList.equals("*")) { - for (MetadataValue md : itemService - .getMetadata(item, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, - org.dspace.content.Item.ANY, org.dspace.content.Item.ANY)) { - if (regex.matcher(md.getValue()).matches()) { - return true; - } - } - } else { - for (String field : fieldList.split(",")) { - for (MetadataValue md : itemService.getMetadataByMetadataString(item, field.trim())) { - if (regex.matcher(md.getValue()).matches()) { - return true; - } - } - } - } - - return false; - } - - static boolean hasOnlyMetadataMatch(Item item, String fieldList, Pattern regex) { - boolean matches = false; - if (fieldList.equals("*")) { - for (MetadataValue md : itemService - .getMetadata(item, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, - org.dspace.content.Item.ANY, org.dspace.content.Item.ANY)) { - if (regex.matcher(md.getValue()).matches()) { - matches = true; - } else { - return false; - } - } - } else { - for (String field : fieldList.split(",")) { - for (MetadataValue md : itemService.getMetadataByMetadataString(item, field.trim())) { - if (regex.matcher(md.getValue()).matches()) { - matches = true; - } else { - return false; - } - } - } - } - return matches; - } - - static boolean recentlyModified(Item item, int days) { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DATE, -days); - return cal.getTime().before(item.getLastModified()); - } -} diff --git a/dspace-rest/src/main/java/org/dspace/utils/DSpaceWebapp.java b/dspace-rest/src/main/java/org/dspace/utils/DSpaceWebapp.java deleted file mode 100644 index 5d3ce8bfa8..0000000000 --- a/dspace-rest/src/main/java/org/dspace/utils/DSpaceWebapp.java +++ /dev/null @@ -1,28 +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.utils; - -import org.dspace.app.util.AbstractDSpaceWebapp; - -/** - * An MBean to identify this web application. - * - * @author Bram Luyten (bram at atmire dot com) - */ -public class DSpaceWebapp - extends AbstractDSpaceWebapp { - public DSpaceWebapp() { - super("REST"); - } - - @Override - public boolean isUI() { - return false; - } -} diff --git a/dspace-rest/src/main/webapp/WEB-INF/applicationContext.xml b/dspace-rest/src/main/webapp/WEB-INF/applicationContext.xml deleted file mode 100644 index ec892fbaa4..0000000000 --- a/dspace-rest/src/main/webapp/WEB-INF/applicationContext.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dspace-rest/src/main/webapp/WEB-INF/security-applicationContext.xml b/dspace-rest/src/main/webapp/WEB-INF/security-applicationContext.xml deleted file mode 100644 index 677753d7f0..0000000000 --- a/dspace-rest/src/main/webapp/WEB-INF/security-applicationContext.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dspace-rest/src/main/webapp/WEB-INF/web.xml b/dspace-rest/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 34d74d9630..0000000000 --- a/dspace-rest/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - dspace.request - org.dspace.utils.servlet.DSpaceWebappServletFilter - - - - dspace.request - /* - - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - - springSecurityFilterChain - /* - - - - - DSpace REST API (Deprecated) - - org.glassfish.jersey.servlet.ServletContainer - - - javax.ws.rs.Application - org.dspace.rest.DSpaceRestApplication - - 1 - - - - DSpace REST API (Deprecated) - /* - - - - default - /static/* - - - - - - DSpace REST API (Deprecated) - /* - - - CONFIDENTIAL - - - - - - - The location of the DSpace home directory - - dspace.dir - ${dspace.dir} - - - - - The location of the Log4J configuration - - log4jConfiguration - ${dspace.dir}/config/log4j2.xml - - - - contextConfigLocation - - /WEB-INF/applicationContext.xml, - /WEB-INF/security-applicationContext.xml - - - - - org.dspace.app.util.DSpaceContextListener - - - - - org.dspace.servicemanager.servlet.DSpaceKernelServletContextListener - - - - - org.springframework.web.context.ContextLoaderListener - - - - - org.dspace.app.util.DSpaceWebappListener - - - - diff --git a/dspace-rest/src/main/webapp/static/reports/authenticate.html b/dspace-rest/src/main/webapp/static/reports/authenticate.html deleted file mode 100644 index 046ced425c..0000000000 --- a/dspace-rest/src/main/webapp/static/reports/authenticate.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - Authenticate for the REST Report Tools - - -

Login for an Authenticated Report View

-
This is intended for sites with Password Authentication Enabled
- -
-
- - -
-
- - -
-
- - -
-
- - \ No newline at end of file diff --git a/dspace-rest/src/main/webapp/static/reports/index.html b/dspace-rest/src/main/webapp/static/reports/index.html deleted file mode 100644 index bc71b0417c..0000000000 --- a/dspace-rest/src/main/webapp/static/reports/index.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - DSpace REST QC Client - - -Query Tool - -
-

DSpace REST QC Client

-
-

Filters

-
-
- -
-
-
-
- -
-
-

Collection Report

- -

Item Results

-
-

Additional data to return

-
- -
-
-

Bitstream data to return

-
-
- -
-

Results

-
-

- -
- - - - Export will export one page of results -
- - -
-
-
-
- - \ No newline at end of file diff --git a/dspace-rest/src/main/webapp/static/reports/query.html b/dspace-rest/src/main/webapp/static/reports/query.html deleted file mode 100644 index 5a7a79cb20..0000000000 --- a/dspace-rest/src/main/webapp/static/reports/query.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - DSpace REST Query Client - - -Collection Filter - -
-

DSpace REST Query Client

-
-
-

Collection Selector

-
-
-

Metadata Field Queries

-
-
- - -
-
-
-
- -
-
-

Limit/Paginate Queries

-
-
- - - -
-
- -
-
-

Filters

-
-
- -
-
-
-
- -
-
-

Additional data to return

-
-
-
- -
-
-

Bitstream data to return

-
-
- -
-

Item Results

-
-

- -
- - - - Export will export one page of results, increase result limits as needed -
- -
-
-
-
- - \ No newline at end of file diff --git a/dspace-rest/src/main/webapp/static/reports/restClient.css b/dspace-rest/src/main/webapp/static/reports/restClient.css deleted file mode 100644 index d81724ae67..0000000000 --- a/dspace-rest/src/main/webapp/static/reports/restClient.css +++ /dev/null @@ -1,98 +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/ - */ -table {border-collapse: collapse;border-right:solid thin black;} -table td, table th {border: thin solid black; padding: 4px;} -tr.header {background-color: #EEEEEE;} -tr:hover td, tr:hover th {background-color: #DDDDDD;} -tr.even td {border-bottom: thin dotted black;} -tr.odd td {border-top: thin dotted black;} -td.even {background-color: #EEFFEE;} -td.head {background-color: #EEEEFF;} -td.num {text-align: right;} -td.link {text-decoration: underline; color: blue;} -td, th {width: 100px;} -td.error {color: red;background-color: yellow;} -td.title, th.title {width: 400px;} -td.mod, th.mod {width: 200px;} -#itemtable {width: 100%;} -#itemdiv {display: none;} -td.ititle, th.ititle {width: 600px;} -td.partial {color:red; font-style: italic;} - -button:disabled { - background-color:gray; -} -input:read-only { - background-color: gray; -} -div.metadata { - padding: 2px; - width: 880px; -} -#metadatadiv select, #metadatadiv input { - padding: 2px; - margin: 4px; -} -#metadatadiv fieldset { - margin: 6px 15px; - width: 850px; -} - -#metadatadiv label { - font-weight: bold; -} - -#itemtable td div:not(:first-child) { - border-top: thin solid gray; -} - -body { - min-height: 700px; - min-width: 700px; -} - -tr.header th { - vertical-align: bottom; -} - -a.partial::after { - content:" ?"; -} - -fieldset.catdiv { - border: thin solid black; - margin-bottom: 8px; -} - -fieldset.catdiv div { - width: 380px; - float: left; -} - -#collSel { - width: 90%; -} - -#filterdiv label { - font-weight: normal; -} - -.button { - background-color: #EEEEEE; -} - -.toobig::before { - content: "*"; -} -#exlimit { - font-style: italic; -} - -.red { - color: red; -} \ No newline at end of file diff --git a/dspace-rest/src/main/webapp/static/reports/restCollReport.js b/dspace-rest/src/main/webapp/static/reports/restCollReport.js deleted file mode 100644 index 8d800a8edc..0000000000 --- a/dspace-rest/src/main/webapp/static/reports/restCollReport.js +++ /dev/null @@ -1,510 +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/ - */ -var CollReport = function() { - Report.call(this); - //If sortable.js is included, uncomment the following - //this.hasSorttable = function(){return true;} - this.getLangSuffix = function(){ - return "[en]"; - }; - - //Indicate if Password Authentication is supported - //this.makeAuthLink = function(){return true;}; - //Indicate if Shibboleth Authentication is supported - //this.makeShibLink = function(){return true;}; - - this.COLL_LIMIT = 20; - this.TOOBIG = 10000; - this.loadId = 0; - this.THREADS =11; - this.THREADSP = 11; - this.ACCIDX_COLL = 1; - this.ACCIDX_ITEM = 2; - this.IACCIDX_META = 0; - this.IACCIDX_BIT = 1; - this.IACCIDX_ITEM = 2; - this.getDefaultParameters = function(){ - return { - "show_fields[]" : [], - "show_fields_bits[]" : [], - filters : "", - limit : this.COUNT_LIMIT, - offset : 0, - icollection : "", - ifilter : "", - }; - }; - this.getCurrentParameters = function(){ - return { - "show_fields[]" : this.myMetadataFields.getShowFields(), - "show_fields_bits[]" : this.myBitstreamFields.getShowFieldsBits(), - filters : this.myFilters.getFilterList(), - limit : this.myReportParameters.getLimit(), - offset : this.myReportParameters.getOffset(), - icollection : $("#icollection").val(), - ifilter : $("#ifilter").val(), - }; - }; - var self = this; - - this.init = function() { - this.baseInit(); - $("#icollection").val(self.myReportParameters.params.icollection); - $("#ifilter").val(self.myReportParameters.params.ifilter); - $("#itemResults").accordion({ - heightStyle: "content", - collapsible: true, - active: 2 - }); - }; - - this.myAuth.callback = function(data) { - self.createCollectionTable(); - $(".showCollections").bind("click", function(){ - self.loadData(); - }); - $("#refresh-fields,#refresh-fields-bits").bind("click", function(){ - self.drawItemTable($("#icollection").val(), $("#ifilter").val(), 0); - }); - }; - - this.createCollectionTable = function() { - var self = this; - var tbl = $(""); - tbl.attr("id","table"); - $("#report").replaceWith(tbl); - - var thead = $(""); - tbl.append(thead); - var tbody = $(""); - tbl.append(tbody); - var tr = self.myHtmlUtil.addTr(thead).addClass("header"); - self.myHtmlUtil.addTh(tr, "Num").addClass("num").addClass("sorttable_numeric"); - self.myHtmlUtil.addTh(tr, "Community").addClass("title"); - self.myHtmlUtil.addTh(tr, "Collection").addClass("title"); - var thn = self.myHtmlUtil.addTh(tr, "Num Items").addClass("sorttable_numeric"); - self.myHtmlUtil.makeTotalCol(thn); - thn = self.myHtmlUtil.addTh(tr, "Num Filtered").addClass("sorttable_numeric"); - self.myHtmlUtil.makeTotalCol(thn); - - self.addCollections(); - }; - - this.addCollections = function() { - var self = this; - - $.ajax({ - url: "/rest/hierarchy", - dataType: "json", - headers: self.myAuth.getHeaders(), - success: function(data){ - if (data.community != null) { - $.each(data.community, function(index, comm){ - self.addCommunity(comm, comm); - }); - } - self.setCollectionCounts(0); - }, - error: function(xhr, status, errorThrown) { - alert("Error in /rest/hierarchy "+ status+ " " + errorThrown); - } - }); - }; - - this.addCommunity = function(top, comm) { - var self = this; - - if (comm.collection != null) { - $.each(comm.collection, function(index, coll){ - self.addCollection(top, coll); - }); - } - if (comm.community != null) { - $.each(comm.community, function(index, scomm){ - self.addCommunity(top, scomm); - }); - } - }; - - this.addCollection = function(top, coll) { - var self = this; - - var tbody = $("#table tbody"); - var index = tbody.find("tr").length; - - var tr = self.myHtmlUtil.addTr(tbody); - tr.attr("cid", coll.id).attr("index",index).addClass(index % 2 == 0 ? "odd data" : "even data"); - self.myHtmlUtil.addTd(tr, index + 1).addClass("num"); - var parval = self.myHtmlUtil.getAnchor(top.name, self.ROOTPATH + top.handle); - - self.myHtmlUtil.addTd(tr, parval).addClass("title comm"); - self.myHtmlUtil.addTdAnchor(tr, coll.name, self.ROOTPATH + coll.handle).addClass("title"); - }; - - - this.setCollectionCounts = function(offset) { - var self = this; - - $.ajax({ - url: "/rest/filtered-collections", - data: { - limit : self.COLL_LIMIT, - offset : offset - }, - dataType: "json", - headers: self.myAuth.getHeaders(), - success: function(data){ - $.each(data, function(index, coll){ - var id = self.getId(coll); - var tr = $("#table tbody").find("tr[cid="+id+"]"); - var td = tr.find("td.numCount"); - td.text(coll.numberItems); - td.on("click", function(){ - self.drawItemTable(self.getId(coll),'',0); - $("#icollection").val(self.getId(coll)); - $("#ifilter").val(""); - }); - }); - - //cannot assume data returned is full amount in case some items are restricted - //if (data.length == self.COLL_LIMIT) { - if (data.length > 0) { - self.setCollectionCounts(offset + self.COLL_LIMIT); - return; - } - self.myHtmlUtil.totalCol(3); - $("#table").addClass("sortable"); - - if (self.myFilters.getFilterList() != "") { - self.loadData(); - if ($("#icollection").val() != "") { - self.drawItemTable($("#icollection").val(), $("#ifilter").val(), 0); - } - } - }, - error: function(xhr, status, errorThrown) { - alert("Error in /rest/collections "+ status+ " " + errorThrown); - }, - complete: function(xhr, status) { - self.spinner.stop(); - $(".showCollections").attr("disabled", false); - } - }); - }; - - this.loadData = function() { - self.spinner.spin($("h1")[0]); - $(".showCollections").attr("disabled", true); - $("#metadatadiv").accordion("option", "active", self.ACCIDX_COLL); - self.loadId++; - $("td.datacol,th.datacol").remove(); - $("#table tr.data").addClass("processing"); - self.myFilters.filterString = self.myFilters.getFilterList(); - self.doRow(0, self.THREADS, self.loadId); - }; - - this.doRow = function(row, threads, curLoadId) { - if (self.loadId != curLoadId) return; - var tr = $("tr[index="+row+"]"); - if (!tr.is("*")){ - return; - } - - var cid = tr.attr("cid"); - $.ajax({ - url: "/rest/filtered-collections/"+cid, - data: { - limit : self.COUNT_LIMIT, - filters : self.myFilters.filterString, - }, - dataType: "json", - headers: self.myAuth.getHeaders(), - success: function(data) { - var numItems = data.numberItems; - var numItemsProcessed = data.numberItemsProcessed; - $.each(data.itemFilters, function(index, itemFilter){ - if (self.loadId != curLoadId) { - return; - } - var trh = $("#table tr.header"); - var filterName = itemFilter["filter-name"]; - var filterTitle = itemFilter.title == null ? filterName : itemFilter.title; - if (!trh.find("th."+filterName).is("*")) { - var th = self.myHtmlUtil.addTh(trh, filterTitle); - th.addClass(filterName).addClass("datacol").addClass("sorttable_numeric"); - self.myHtmlUtil.makeTotalCol(th); - - if (itemFilter.description != null) { - th.attr("title", itemFilter.description); - } - - $("tr.data").each(function(){ - var td = self.myHtmlUtil.addTd($(this), ""); - td.addClass(filterName).addClass("num").addClass("datacol"); - }); - } - - self.setCellCount(tr, cid, 0, (numItems != numItemsProcessed), itemFilter); - self.setFilteredCount(tr, cid, 0, numItems, numItemsProcessed); - }); - - tr.removeClass("processing"); - if (!$("#table tr.processing").is("*")) { - self.updateSortable(); - self.totalFilters(); - self.spinner.stop(); - $(".showCollections").attr("disabled", false); - return; - } - if (row % threads == 0 || threads == 1) { - for(var i=1; i<=threads; i++) { - self.doRow(row+i, threads, curLoadId); - } - } - }, - error: function(xhr, status, errorThrown) { - alert("Error in /rest/filtered-collections "+ status+ " " + errorThrown); - }, - complete: function(xhr, status) { - self.spinner.stop(); - $(".showCollections").attr("disabled", false); - } - }); - }; - - this.updateSortable = function() { - if (self.hasSorttable()) { - $("#table").removeClass("sortable"); - $("#table").addClass("sortable"); - sorttable.makeSortable($("#table")[0]); - } - }; - - this.totalFilters = function() { - var colcount = $("#table tr th").length; - for(var i=4; i= self.TOOBIG) { - td.addClass("toobig"); - title+= "\nIt will take significant time to apply this filter to the entire collection."; - } - td.attr("title", title); - return false; - } else { - self.totalFilters(); - } - return true; - }; - - this.setCellCount = function(tr, cid, offset, isPartial, itemFilter) { - var filterName = itemFilter["filter-name"]; - var icount = itemFilter["item-count"]; - - var td = tr.find("td."+filterName); - if (icount == null) { - icount = 0; - } - var cur = parseInt(td.text()); - if (!isNaN(cur)) { - icount += cur; - } - - td.removeClass("partial"); - td.removeClass("link"); - td.removeAttr("title"); - td.off(); - td.text(icount); - if (icount != 0) { - td.addClass("link"); - if (isPartial) { - td.addClass("partial"); - td.attr("title", "Collection partially processed, item counts are incomplete"); - } - td.on("click", function(){ - self.drawItemTable(cid,filterName,0); - $("#icollection").val(cid); - $("#ifilter").val(filterName); - }); - } - }; - - - this.drawItemTable = function(cid, filter, offset) { - self = this; - self.spinner.spin($("h1")[0]); - $("#itemtable").replaceWith($('
')); - var itbl = $("#itemtable"); - //itbl.find("tr").remove("*"); - var tr = self.myHtmlUtil.addTr(itbl).addClass("header"); - self.myHtmlUtil.addTh(tr, "Num").addClass("num").addClass("sorttable_numeric"); - self.myHtmlUtil.addTh(tr, "id"); - self.myHtmlUtil.addTh(tr, "Handle"); - self.myHtmlUtil.addTh(tr, "dc.title" + self.getLangSuffix()).addClass("title"); - var fields = $("#show-fields select").val(); - if (fields != null) { - $.each(fields, function(index, field){ - self.myHtmlUtil.addTh(tr, field + self.getLangSuffix()); - }); - } - var bitfields = $("#show-fields-bits select").val(); - if (bitfields != null) { - $.each(bitfields, function(index, bitf){ - self.myHtmlUtil.addTh(tr, bitf); - }); - } - - var expand = "items"; - if (fields != null) { - expand += ",metadata"; - } - if (bitfields != null) { - expand += ",bitstreams"; - } - - var params = { - expand: expand, - limit: self.ITEM_LIMIT, - filters: filter, - offset: offset, - "show_fields[]" : fields, - "show_fields_bits[]" : bitfields, - }; - - $.ajax({ - url: "/rest/filtered-collections/"+cid, - data: params, - dataType: "json", - headers: self.myAuth.getHeaders(), - success: function(data){ - var source = filter == "" ? data.items : data.itemFilters[0].items; - - $.each(source, function(index, item){ - var tr = self.myHtmlUtil.addTr(itbl); - tr.addClass(index % 2 == 0 ? "odd data" : "even data"); - self.myHtmlUtil.addTd(tr, offset+index+1).addClass("num"); - self.myHtmlUtil.addTd(tr, self.getId(item)); - self.myHtmlUtil.addTdAnchor(tr, item.handle, self.ROOTPATH + item.handle); - self.myHtmlUtil.addTd(tr, item.name).addClass("ititle"); - if (fields != null) { - $.each(fields, function(index, field){ - var td = self.myHtmlUtil.addTd(tr, ""); - $.each(item.metadata, function(mindex,mv){ - if (mv.key == field) { - td.append($("
"+mv.value+"
")); - } - }); - }); - } - if (bitfields != null) { - $.each(bitfields, function(index, bitfield){ - var td = self.myHtmlUtil.addTd(tr, ""); - var fieldtext = self.myBitstreamFields.getKeyText(bitfield, item, bitfields); - for(var j=0; j"+fieldtext[j]+"")); - } - }); - } - }); - self.displayItems(filter + " Items in " + data.name, - offset, - self.ITEM_LIMIT, - data.numberItems, - function(){self.drawItemTable(cid, filter, (offset - self.ITEM_LIMIT < 0) ? 0 : offset - self.ITEM_LIMIT);}, - function(){self.drawItemTable(cid, filter, offset + self.ITEM_LIMIT);} - ); - - if (self.hasSorttable()){ - sorttable.makeSortable(itbl[0]); - } - $("#metadatadiv").accordion("option", "active", self.ACCIDX_ITEM); - }, - error: function(xhr, status, errorThrown) { - alert("Error in /rest/filtered-collections "+ status+ " " + errorThrown); - }, - complete: function(xhr, status) { - self.spinner.stop(); - $(".showCollections").attr("disabled", false); - $("#itemResults").accordion("option", "active", self.IACCIDX_ITEM); - } - }); - }; - - //Ignore the first column containing a row number and the item handle - this.exportCol = function(colnum, col) { - var data = ""; - if (colnum == 0) return ""; - if (colnum == 2) return ""; - data += (colnum == 1) ? "" : ","; - data += self.exportCell(col); - return data; - }; -}; -CollReport.prototype = Object.create(Report.prototype); - -$(document).ready(function(){ - var myReport=new CollReport(); - myReport.init(); -}); \ No newline at end of file diff --git a/dspace-rest/src/main/webapp/static/reports/restQueryReport.js b/dspace-rest/src/main/webapp/static/reports/restQueryReport.js deleted file mode 100644 index 18e9a61d08..0000000000 --- a/dspace-rest/src/main/webapp/static/reports/restQueryReport.js +++ /dev/null @@ -1,350 +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/ - */ -var QueryReport = function() { - Report.call(this); - - //If sortable.js is included, uncomment the following - //this.hasSorttable = function(){return true;} - this.getLangSuffix = function(){ - return "[en]"; - }; - - //Indicate if Password Authentication is supported - //this.makeAuthLink = function(){return true;}; - //Indicate if Shibboleth Authentication is supported - //this.makeShibLink = function(){return true;}; - - this.getDefaultParameters = function(){ - return { - "collSel[]" : [], - "query_field[]" : [], - "query_op[]" : [], - "query_val[]" : [], - "show_fields[]" : [], - "show_fields_bits[]" : [], - "filters" : "", - "limit" : this.ITEM_LIMIT, - "offset" : 0, - }; - }; - this.getCurrentParameters = function(){ - var expand = "parentCollection,metadata"; - if (this.myBitstreamFields.hasBitstreamFields()) { - expand += ",bitstreams"; - } - var params = { - "query_field[]" : [], - "query_op[]" : [], - "query_val[]" : [], - "collSel[]" : ($("#collSel").val() == null) ? [""] : $("#collSel").val(), - limit : this.myReportParameters.getLimit(), - offset : this.myReportParameters.getOffset(), - "expand" : expand, - filters : this.myFilters.getFilterList(), - "show_fields[]" : this.myMetadataFields.getShowFields(), - "show_fields_bits[]" : this.myBitstreamFields.getShowFieldsBits(), - }; - $("select.query-tool,input.query-tool").each(function() { - var paramArr = params[$(this).attr("name")]; - paramArr[paramArr.length] = $(this).val(); - }); - return params; - }; - var self = this; - - this.init = function() { - this.baseInit(); - }; - - this.initMetadataFields = function() { - this.myMetadataFields = new QueryableMetadataFields(self); - this.myMetadataFields.load(); - }; - this.myAuth.callback = function(data) { - $(".query-button").click(function(){self.runQuery();}); - }; - - this.runQuery = function() { - this.spinner.spin($("body")[0]); - $("button").attr("disabled", true); - $.ajax({ - url: "/rest/filtered-items", - data: this.getCurrentParameters(), - dataType: "json", - headers: self.myAuth.getHeaders(), - success: function(data){ - data.metadata = $("#show-fields select").val(); - data.bitfields = $("#show-fields-bits select").val(); - self.drawItemFilterTable(data); - self.spinner.stop(); - $("button").not("#next,#prev").attr("disabled", false); - }, - error: function(xhr, status, errorThrown) { - alert("Error in /rest/filtered-items "+ status+ " " + errorThrown); - }, - complete: function(xhr, status, errorThrown) { - self.spinner.stop(); - $("button").not("#next,#prev").attr("disabled", false); - } - }); - }; - - this.drawItemFilterTable = function(data) { - $("#itemtable").replaceWith($('
')); - var itbl = $("#itemtable"); - var tr = self.myHtmlUtil.addTr(itbl).addClass("header"); - self.myHtmlUtil.addTh(tr, "Num").addClass("num").addClass("sorttable_numeric"); - self.myHtmlUtil.addTh(tr, "id"); - self.myHtmlUtil.addTh(tr, "collection"); - self.myHtmlUtil.addTh(tr, "Item Handle"); - self.myHtmlUtil.addTh(tr, "dc.title" + self.getLangSuffix()); - - var mdCols = []; - if (data.metadata) { - $.each(data.metadata, function(index, field) { - if (field != "") { - self.myHtmlUtil.addTh(tr,field + self.getLangSuffix()).addClass("returnFields"); - mdCols[mdCols.length] = field; - } - }); - } - - if (data.bitfields) { - $.each(data.bitfields, function(index, bitfield) { - if (bitfield != "") { - self.myHtmlUtil.addTh(tr,bitfield).addClass("returnFields"); - mdCols[mdCols.length] = bitfield; - } - }); - } - - $.each(data.items, function(index, item){ - var tr = self.myHtmlUtil.addTr(itbl); - tr.addClass(index % 2 == 0 ? "odd data" : "even data"); - self.myHtmlUtil.addTd(tr, self.myReportParameters.getOffset()+index+1).addClass("num"); - self.myHtmlUtil.addTd(tr, self.getId(item)); - if (item.parentCollection == null) { - self.myHtmlUtil.addTd(tr, "--"); - } else { - self.myHtmlUtil.addTdAnchor(tr, item.parentCollection.name, self.ROOTPATH + item.parentCollection.handle); - } - self.myHtmlUtil.addTdAnchor(tr, item.handle, self.ROOTPATH + item.handle); - self.myHtmlUtil.addTd(tr, item.name); - - for(var i=0; i"+metadata.value+""); - td.append(div); - } - } - }); - var fieldtext = self.myBitstreamFields.getKeyText(key, item, data.bitfields); - for(var j=0; j"+fieldtext[j]+"")); - } - } - }); - - this.displayItems(data["query-annotation"], - this.myReportParameters.getOffset(), - this.myReportParameters.getLimit(), - data["unfiltered-item-count"], - function(){ - self.myReportParameters.updateOffset(false); - self.runQuery(); - }, - function(){ - self.myReportParameters.updateOffset(true); - self.runQuery(); - } - ); - - if (this.hasSorttable()) { - sorttable.makeSortable(itbl[0]); - } - $("#metadatadiv").accordion("option", "active", $("#metadatadiv > h3").length - 1); - }; - - //Ignore the first column containing a row number and the item handle, get handle for the collection - this.exportCol = function(colnum, col) { - var data = ""; - if (colnum == 0) return ""; - if (colnum == 3) return ""; - data += (colnum == 1) ? "" : ","; - - if (colnum == 2) { - var anchor = $(col).find("a"); - var href = anchor.is("a") ? anchor.attr("href").replace(self.ROOTPATH,"") : $(col).text(); - data += "\"" + href + "\""; - } else { - data += self.exportCell(col); } - return data; - }; -}; -QueryReport.prototype = Object.create(Report.prototype); - -$(document).ready(function(){ - var myReport=new QueryReport(); - myReport.init(); -}); - -var QueryableMetadataFields = function(report) { - MetadataFields.call(this, report); - var self = this; - - this.initFields = function(data, report) { - self.metadataSchemas = data; - var params = report.myReportParameters.params; - var fields = params["query_field[]"]; - var ops = params["query_op[]"]; - var vals = params["query_val[]"]; - if (fields && ops && vals) { - if (fields.length == 0) { - self.drawFilterQuery("*","exists",""); - } else { - for(var i=0; i i ? ops[i] : ""; - var val = vals.length > i ? vals[i] : ""; - self.drawFilterQuery(fields[i],op,val); - } - } - } - self.drawShowFields(params["show_fields[]"]); - self.initQueries(); - report.spinner.stop(); - $(".query-button").attr("disabled", false); - }; - - this.initQueries = function() { - $("#predefselect") - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .append($("")) - .on("change",function(){ - $("div.metadata").remove(); - var val = $("#predefselect").val(); - if (val == 'new') { - self.drawFilterQuery("","",""); - } else if (val == 'q1') { - self.drawFilterQuery("dc.title","doesnt_exist",""); - } else if (val == 'q2') { - self.drawFilterQuery("dc.identifier.uri","doesnt_exist",""); - } else if (val == 'q3') { - self.drawFilterQuery("dc.subject.*","like","%;%"); - } else if (val == 'q4') { - self.drawFilterQuery("dc.contributor.author","like","% and %"); - } else if (val == 'q5') { - self.drawFilterQuery("dc.creator","like","% and %"); - } else if (val == 'q6') { - self.drawFilterQuery("dc.description","matches","^.*(http://|https://|mailto:).*$"); - } else if (val == 'q7') { - self.drawFilterQuery("dc.description.provenance","matches","^.*No\\. of bitstreams(.|\\r|\\n|\\r\\n)*\\.(PDF|pdf|DOC|doc|PPT|ppt|DOCX|docx|PPTX|pptx).*$"); - } else if (val == 'q8') { - self.drawFilterQuery("dc.description.provenance","doesnt_match","^.*No\\. of bitstreams(.|\\r|\\n|\\r\\n)*\\.(PDF|pdf|DOC|doc|PPT|ppt|DOCX|docx|PPTX|pptx).*$"); - } else if (val == 'q9') { - self.drawFilterQuery("*","matches","^\\s*$"); - } else if (val == 'q10') { - self.drawFilterQuery("dc.description.*","matches","^.*[^\\s]{50,}.*$"); - } else if (val == 'q12') { - self.drawFilterQuery("*","matches","^.*&#.*$"); - } else if (val == 'q13') { - self.drawFilterQuery("*","matches","^.*[^[:ascii:]].*$"); - } - }); - }; - - this.drawFilterQuery = function(pField, pOp, pVal) { - var div = $("