Merge branch 'master' into Authentication_X-Forwarded-For

This commit is contained in:
Tim Donohue
2018-10-31 14:55:53 -05:00
committed by GitHub
139 changed files with 7277 additions and 1180 deletions

View File

@@ -1,5 +1,12 @@
# This image will be published as dspace/dspace
# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details
#
# This version is JDK8 compatible
# - tomcat:8-jre8
# - ANT 1.10.5
# - maven:latest
# - note:
# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8
# Step 1 - Run Maven Build
FROM maven as build
@@ -12,7 +19,7 @@ COPY dspace/src/main/docker/local.cfg /app/local.cfg
RUN mvn package
# Step 2 - Run Ant Deploy
FROM tomcat:8 as ant_build
FROM tomcat:8-jre8 as ant_build
ARG TARGET_DIR=dspace-installer
COPY --from=build /app /dspace-src
WORKDIR /dspace-src/dspace/target/${TARGET_DIR}
@@ -29,7 +36,7 @@ RUN ant update_configs update_code update_webapps update_solr_indexes
# Step 3 - Run tomcat
# Create a new tomcat image that does not retain the the build directory contents
FROM tomcat:8
FROM tomcat:8-jre8
COPY --from=ant_build /dspace /dspace
EXPOSE 8080 8009

64
Dockerfile.jdk8-test Normal file
View File

@@ -0,0 +1,64 @@
# This image will be published as dspace/dspace
# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details
#
# This version is JDK8 compatible
# - tomcat:8-jre8
# - ANT 1.10.5
# - maven:latest
# - note: expose /solr to any host; provide /rest over http
# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8-test
# Step 1 - Run Maven Build
FROM maven as build
WORKDIR /app
# Copy the DSpace source code into the workdir (excluding .dockerignore contents)
ADD . /app/
COPY dspace/src/main/docker/local.cfg /app/local.cfg
# Provide web.xml overrides to make webapps easier to test
COPY dspace/src/main/docker/test/solr_web.xml /app/dspace-solr/src/main/webapp/WEB-INF/web.xml
COPY dspace/src/main/docker/test/rest_web.xml /app/dspace-rest/src/main/webapp/WEB-INF/web.xml
RUN mvn package
# Step 2 - Run Ant Deploy
FROM tomcat:8-jre8 as ant_build
ARG TARGET_DIR=dspace-installer
COPY --from=build /app /dspace-src
WORKDIR /dspace-src/dspace/target/${TARGET_DIR}
# Create the initial install deployment using ANT
ENV ANT_VERSION 1.10.5
ENV ANT_HOME /tmp/ant-$ANT_VERSION
ENV PATH $ANT_HOME/bin:$PATH
RUN mkdir $ANT_HOME && \
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
RUN ant update_configs update_code update_webapps update_solr_indexes
# Step 3 - Run tomcat
# Create a new tomcat image that does not retain the the build directory contents
FROM tomcat:8-jre8
COPY --from=ant_build /dspace /dspace
EXPOSE 8080 8009
# Ant will be embedded in the final container to allow additional deployments
ENV ANT_VERSION 1.10.5
ENV ANT_HOME /tmp/ant-$ANT_VERSION
ENV PATH $ANT_HOME/bin:$PATH
RUN mkdir $ANT_HOME && \
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
ENV DSPACE_INSTALL=/dspace
ENV JAVA_OPTS=-Xmx2000m
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
ln -s $DSPACE_INSTALL/webapps/spring-rest /usr/local/tomcat/webapps/spring-rest && \
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest && \
ln -s $DSPACE_INSTALL/webapps/oai /usr/local/tomcat/webapps/oai && \
ln -s $DSPACE_INSTALL/webapps/rdf /usr/local/tomcat/webapps/rdf && \
ln -s $DSPACE_INSTALL/webapps/sword /usr/local/tomcat/webapps/sword && \
ln -s $DSPACE_INSTALL/webapps/swordv2 /usr/local/tomcat/webapps/swordv2

View File

@@ -40,6 +40,9 @@ Please be aware that, as a Java web application, DSpace requires a database (Pos
and a servlet container (usually Tomcat) in order to function.
More information about these and all other prerequisites can be found in the Installation instructions above.
## Dockerfile Usage
See the [DSpace Docker Tutorial](https://dspace-labs.github.io/DSpace-Docker-Images/).
## Contributing
DSpace is a community built and supported project. We do not have a centralized development or support team,

View File

@@ -364,10 +364,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
@@ -399,7 +395,7 @@
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@@ -63,6 +63,12 @@ public class DCInput {
*/
private String label = null;
/**
* a style instruction to apply to the input. The exact way to use the style value is UI depending that receive the
* value from the REST API as is
*/
private String style = null;
/**
* the input type
*/
@@ -199,7 +205,7 @@ public class DCInput {
typeBind.add(type.trim());
}
}
style = fieldMap.get("style");
}
/**
@@ -262,7 +268,7 @@ public class DCInput {
}
/**
* Get the DC element for this form row.
* Get the DC element for this form field.
*
* @return the DC element
*/
@@ -271,7 +277,7 @@ public class DCInput {
}
/**
* Get the DC namespace prefix for this form row.
* Get the DC namespace prefix for this form field.
*
* @return the DC namespace prefix
*/
@@ -290,7 +296,7 @@ public class DCInput {
}
/**
* Is there a required string for this form row?
* Is there a required string for this form field?
*
* @return true if a required string is set
*/
@@ -299,7 +305,7 @@ public class DCInput {
}
/**
* Get the DC qualifier for this form row.
* Get the DC qualifier for this form field.
*
* @return the DC qualifier
*/
@@ -308,7 +314,7 @@ public class DCInput {
}
/**
* Get the language for this form row.
* Get the language for this form field.
*
* @return the language state
*/
@@ -317,7 +323,7 @@ public class DCInput {
}
/**
* Get the hint for this form row, formatted for an HTML table
* Get the hint for this form field
*
* @return the hints
*/
@@ -326,7 +332,7 @@ public class DCInput {
}
/**
* Get the label for this form row.
* Get the label for this form field.
*
* @return the label
*/
@@ -334,6 +340,15 @@ public class DCInput {
return label;
}
/**
* Get the style for this form field
*
* @return the style
*/
public String getStyle() {
return style;
}
/**
* Get the name of the pairs type
*

View File

@@ -25,25 +25,26 @@ public class DCInputSet {
/**
* the inputs ordered by row position
*/
private DCInput[] inputs = null;
private DCInput[][] inputs = null;
/**
* constructor
*
* @param formName form name
* @param headings
* @param mandatoryFlags
* @param fields fields
* @param rows the rows
* @param listMap map
*/
public DCInputSet(String formName,
List<Map<String, String>> fields, Map<String, List<String>> listMap) {
public DCInputSet(String formName, List<List<Map<String, String>>> rows, Map<String, List<String>> listMap) {
this.formName = formName;
this.inputs = new DCInput[fields.size()];
this.inputs = new DCInput[rows.size()][];
for (int i = 0; i < inputs.length; i++) {
Map<String, String> field = fields.get(i);
inputs[i] = new DCInput(field, listMap);
List<Map<String, String>> fields = rows.get(i);
inputs[i] = new DCInput[fields.size()];
for (int j = 0; j < inputs[i].length; j++) {
Map<String, String> field = rows.get(i).get(j);
inputs[i][j] = new DCInput(field, listMap);
}
}
}
@@ -71,7 +72,7 @@ public class DCInputSet {
* @return an array containing the fields
*/
public DCInput[] getFields() {
public DCInput[][] getFields() {
return inputs;
}
@@ -104,10 +105,12 @@ public class DCInputSet {
*/
public boolean isFieldPresent(String fieldName) {
for (int i = 0; i < inputs.length; i++) {
DCInput field = inputs[i];
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
return true;
for (int j = 0; j < inputs[i].length; j++) {
DCInput field = inputs[i][j];
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
return true;
}
}
}
return false;
@@ -127,11 +130,13 @@ public class DCInputSet {
documentType = "";
}
for (int i = 0; i < inputs.length; i++) {
DCInput field = inputs[i];
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
if (field.isAllowedFor(documentType)) {
return true;
for (int j = 0; j < inputs[i].length; j++) {
DCInput field = inputs[i][j];
String fullName = field.getFieldName();
if (fullName.equals(fieldName)) {
if (field.isAllowedFor(documentType)) {
return true;
}
}
}
}

View File

@@ -74,7 +74,7 @@ public class DCInputsReader {
* Reference to the forms definitions map, computed from the forms
* definition file
*/
private Map<String, List<Map<String, String>>> formDefns = null;
private Map<String, List<List<Map<String, String>>>> formDefns = null;
/**
* Reference to the value-pairs map, computed from the forms definition file
@@ -115,7 +115,7 @@ public class DCInputsReader {
private void buildInputs(String fileName)
throws DCInputsReaderException {
formDefns = new HashMap<String, List<Map<String, String>>>();
formDefns = new HashMap<String, List<List<Map<String, String>>>>();
valuePairs = new HashMap<String, List<String>>();
String uri = "file:" + new File(fileName).getAbsolutePath();
@@ -212,7 +212,7 @@ public class DCInputsReader {
return lastInputSet;
}
// cache miss - construct new DCInputSet
List<Map<String, String>> pages = formDefns.get(formName);
List<List<Map<String, String>>> pages = formDefns.get(formName);
if (pages == null) {
throw new DCInputsReaderException("Missing the " + formName + " form");
}
@@ -292,8 +292,8 @@ public class DCInputsReader {
/**
* Process the form-definitions section of the XML file. Each element is
* formed thusly: <form name="formname">...pages...</form> Each pages
* subsection is formed: <page number="#"> ...fields... </page> Each field
* formed thusly: <form name="formname">...row...</form> Each rows
* subsection is formed: <row> ...fields... </row> Each field
* is formed from: dc-element, dc-qualifier, label, hint, input-type name,
* required text, and repeatable flag.
*/
@@ -311,26 +311,24 @@ public class DCInputsReader {
if (formName == null) {
throw new SAXException("form element has no name attribute");
}
List<Map<String, String>> fields = new ArrayList<Map<String, String>>(); // the form contains fields
formDefns.put(formName, fields);
List<List<Map<String, String>>> rows = new ArrayList<List<Map<String, String>>>(); // the form
// contains rows of fields
formDefns.put(formName, rows);
NodeList pl = nd.getChildNodes();
int lenpg = pl.getLength();
for (int j = 0; j < lenpg; j++) {
Node npg = pl.item(j);
if (npg.getNodeName().equals("field")) {
// process each field definition
Map<String, String> field = new HashMap<String, String>();
processField(formName, npg, field);
fields.add(field);
// we omit the duplicate validation, allowing multiple
// fields definition for
// the same metadata and different visibility/type-bind
if (npg.getNodeName().equals("row")) {
List<Map<String, String>> fields = new ArrayList<Map<String, String>>(); // the fields in the
// row
// process each row definition
processRow(formName, j, npg, fields);
rows.add(fields);
}
}
// sanity check number of fields
if (fields.size() < 1) {
throw new DCInputsReaderException("Form " + formName + " has no fields");
if (rows.size() < 1) {
throw new DCInputsReaderException("Form " + formName + " has no rows");
}
}
}
@@ -339,6 +337,48 @@ public class DCInputsReader {
}
}
/**
* Process parts of a row
*/
private void processRow(String formName, int rowIdx, Node n, List<Map<String, String>> fields)
throws SAXException, DCInputsReaderException {
NodeList pl = n.getChildNodes();
int lenpg = pl.getLength();
for (int j = 0; j < lenpg; j++) {
Node npg = pl.item(j);
if (npg.getNodeName().equals("field")) {
// process each field definition
Map<String, String> field = new HashMap<String, String>();
processField(formName, npg, field);
fields.add(field);
String key = field.get(PAIR_TYPE_NAME);
if (StringUtils
.isNotBlank(key)) {
String schema = field.get("dc-schema");
String element = field.get("dc-element");
String qualifier = field
.get("dc-qualifier");
String metadataField = schema + "."
+ element;
if (StringUtils.isNotBlank(qualifier)) {
metadataField += "." + qualifier;
}
}
// we omit the duplicate validation, allowing multiple
// fields definition for
// the same metadata and different visibility/type-bind
}
}
// sanity check number of fields
if (fields.size() < 1) {
throw new DCInputsReaderException("Form " + formName + "row " + rowIdx + " has no fields");
}
}
/**
* Process parts of a field
* At the end, make sure that input-types 'qualdrop_value' and
@@ -537,26 +577,29 @@ public class DCInputsReader {
Iterator<String> ki = formDefns.keySet().iterator();
while (ki.hasNext()) {
String idName = ki.next();
List<Map<String, String>> fields = formDefns.get(idName);
for (int i = 0; i < fields.size(); i++) {
Map<String, String> fld = fields.get(i);
// verify reference in certain input types
String type = fld.get("input-type");
if (type.equals("dropdown")
|| type.equals("qualdrop_value")
|| type.equals("list")) {
String pairsName = fld.get(PAIR_TYPE_NAME);
List<String> v = valuePairs.get(pairsName);
if (v == null) {
String errString = "Cannot find value pairs for " + pairsName;
throw new DCInputsReaderException(errString);
List<List<Map<String, String>>> rows = formDefns.get(idName);
for (int j = 0; j < rows.size(); j++) {
List<Map<String, String>> fields = rows.get(j);
for (int i = 0; i < fields.size(); i++) {
Map<String, String> fld = fields.get(i);
// verify reference in certain input types
String type = fld.get("input-type");
if (type.equals("dropdown")
|| type.equals("qualdrop_value")
|| type.equals("list")) {
String pairsName = fld.get(PAIR_TYPE_NAME);
List<String> v = valuePairs.get(pairsName);
if (v == null) {
String errString = "Cannot find value pairs for " + pairsName;
throw new DCInputsReaderException(errString);
}
}
}
// we omit the "required" and "visibility" validation, provided this must be checked in the
// processing class
// only when it makes sense (if the field isn't visible means that it is not applicable, therefore it
// can't be required)
// we omit the "required" and "visibility" validation, provided this must be checked in the
// processing class
// only when it makes sense (if the field isn't visible means that it is not applicable,
// therefore it can't be required)
}
}
}
}
@@ -639,4 +682,5 @@ public class DCInputsReader {
}
throw new DCInputsReaderException("No field configuration found!");
}
}

View File

@@ -418,11 +418,12 @@ public class Util {
List<DCInputSet> inputSets = inputsReader.getInputsByCollectionHandle(col_handle);
// Replace the values of Metadatum[] with the correct ones in case
// of
// controlled vocabularies
String currentField = Utils.standardize(schema, element, qualifier, ".");
for (DCInputSet inputSet : inputSets) {
// Replace the values of Metadatum[] with the correct ones in case
// of
// controlled vocabularies
String currentField = Utils.standardize(schema, element, qualifier, ".");
if (inputSet != null) {
@@ -430,19 +431,20 @@ public class Util {
for (int p = 0; p < fieldsNums; p++) {
DCInput[] inputs = inputSet.getFields();
DCInput[][] inputs = inputSet.getFields();
if (inputs != null) {
for (int i = 0; i < inputs.length; i++) {
String inputField = Utils.standardize(inputs[i].getSchema(), inputs[i].getElement(),
inputs[i].getQualifier(), ".");
if (currentField.equals(inputField)) {
myInputs = inputs[i];
myInputsFound = true;
break;
for (int j = 0; j < inputs[i].length; j++) {
String inputField = Utils
.standardize(inputs[i][j].getSchema(), inputs[i][j].getElement(),
inputs[i][j].getQualifier(), ".");
if (currentField.equals(inputField)) {
myInputs = inputs[i][j];
myInputsFound = true;
break;
}
}
}
}
@@ -480,13 +482,17 @@ public class Util {
Set<String> fromFieldName = new HashSet<>();
Set<String> toFieldName = new HashSet<>();
for (DCInputSet ff : from) {
for (DCInput fdc : ff.getFields()) {
fromFieldName.add(fdc.getFieldName());
for (DCInput[] fdcrow : ff.getFields()) {
for (DCInput fdc : fdcrow) {
fromFieldName.add(fdc.getFieldName());
}
}
}
for (DCInputSet tt : to) {
for (DCInput tdc : tt.getFields()) {
toFieldName.add(tdc.getFieldName());
for (DCInput[] tdcrow : tt.getFields()) {
for (DCInput tdc : tdcrow) {
toFieldName.add(tdc.getFieldName());
}
}
}

View File

@@ -147,6 +147,12 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
return workspaceItemDAO.findByEPerson(context, ep);
}
@Override
public List<WorkspaceItem> findByEPerson(Context context, EPerson ep, Integer limit, Integer offset)
throws SQLException {
return workspaceItemDAO.findByEPerson(context, ep, limit, offset);
}
@Override
public List<WorkspaceItem> findByCollection(Context context, Collection collection) throws SQLException {
return workspaceItemDAO.findByCollection(context, collection);
@@ -231,6 +237,11 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
return workspaceItemDAO.countRows(context);
}
@Override
public int countByEPerson(Context context, EPerson ep) throws SQLException {
return workspaceItemDAO.countRows(context, ep);
}
@Override
public List<Map.Entry<Integer, Long>> getStageReachedCounts(Context context) throws SQLException {
return workspaceItemDAO.getStageReachedCounts(context);

View File

@@ -264,39 +264,42 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
try {
DCInputsReader dcInputsReader = new DCInputsReader();
for (DCInputSet dcinputSet : dcInputsReader.getAllInputs(Integer.MAX_VALUE, 0)) {
DCInput[] dcinputs = dcinputSet.getFields();
for (DCInput dcinput : dcinputs) {
if (StringUtils.isNotBlank(dcinput.getPairsType())
|| StringUtils.isNotBlank(dcinput.getVocabulary())) {
String authorityName = dcinput.getPairsType();
if (StringUtils.isBlank(authorityName)) {
authorityName = dcinput.getVocabulary();
}
if (!StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
dcinput.getQualifier());
ChoiceAuthority ca = controller.get(authorityName);
if (ca == null) {
InputFormSelfRegisterWrapperAuthority ifa = new InputFormSelfRegisterWrapperAuthority();
if (controller.containsKey(fieldKey)) {
ifa = (InputFormSelfRegisterWrapperAuthority) controller.get(fieldKey);
DCInput[][] dcinputs = dcinputSet.getFields();
for (DCInput[] dcrows : dcinputs) {
for (DCInput dcinput : dcrows) {
if (StringUtils.isNotBlank(dcinput.getPairsType())
|| StringUtils.isNotBlank(dcinput.getVocabulary())) {
String authorityName = dcinput.getPairsType();
if (StringUtils.isBlank(authorityName)) {
authorityName = dcinput.getVocabulary();
}
if (!StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
dcinput.getQualifier());
ChoiceAuthority ca = controller.get(authorityName);
if (ca == null) {
InputFormSelfRegisterWrapperAuthority ifa = new
InputFormSelfRegisterWrapperAuthority();
if (controller.containsKey(fieldKey)) {
ifa = (InputFormSelfRegisterWrapperAuthority) controller.get(fieldKey);
}
ChoiceAuthority ma = (ChoiceAuthority) pluginService
.getNamedPlugin(ChoiceAuthority.class, authorityName);
if (ma == null) {
log.warn("Skipping invalid configuration for " + fieldKey
+ " because named plugin not found: " + authorityName);
continue;
}
ifa.getDelegates().put(dcinputSet.getFormName(), ma);
controller.put(fieldKey, ifa);
}
ChoiceAuthority ma = (ChoiceAuthority) pluginService
.getNamedPlugin(ChoiceAuthority.class, authorityName);
if (ma == null) {
log.warn("Skipping invalid configuration for " + fieldKey
+ " because named plugin not found: " + authorityName);
continue;
if (!authorities.containsKey(authorityName)) {
authorities.put(authorityName, fieldKey);
}
ifa.getDelegates().put(dcinputSet.getFormName(), ma);
controller.put(fieldKey, ifa);
}
if (!authorities.containsKey(authorityName)) {
authorities.put(authorityName, fieldKey);
}
}
}
}

View File

@@ -235,21 +235,23 @@ public class MetadataAuthorityServiceImpl implements MetadataAuthorityService {
try {
DCInputsReader dcInputsReader = new DCInputsReader();
for (DCInputSet dcinputSet : dcInputsReader.getAllInputs(Integer.MAX_VALUE, 0)) {
DCInput[] dcinputs = dcinputSet.getFields();
for (DCInput dcinput : dcinputs) {
if (StringUtils.isNotBlank(dcinput.getPairsType())
|| StringUtils.isNotBlank(dcinput.getVocabulary())) {
String authorityName = dcinput.getPairsType();
if (StringUtils.isBlank(authorityName)) {
authorityName = dcinput.getVocabulary();
}
if (!StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
dcinput.getQualifier());
boolean req = ConfigurationManager
.getBooleanProperty("authority.required." + fieldKey, false);
controlled.put(fieldKey, true);
isAuthorityRequired.put(fieldKey, req);
DCInput[][] dcinputs = dcinputSet.getFields();
for (DCInput[] dcrows : dcinputs) {
for (DCInput dcinput : dcrows) {
if (StringUtils.isNotBlank(dcinput.getPairsType())
|| StringUtils.isNotBlank(dcinput.getVocabulary())) {
String authorityName = dcinput.getPairsType();
if (StringUtils.isBlank(authorityName)) {
authorityName = dcinput.getVocabulary();
}
if (!StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
dcinput.getQualifier());
boolean req = ConfigurationManager
.getBooleanProperty("authority.required." + fieldKey, false);
controlled.put(fieldKey, true);
isAuthorityRequired.put(fieldKey, req);
}
}
}
}

View File

@@ -30,6 +30,9 @@ public interface WorkspaceItemDAO extends GenericDAO<WorkspaceItem> {
public List<WorkspaceItem> findByEPerson(Context context, EPerson ep) throws SQLException;
public List<WorkspaceItem> findByEPerson(Context context, EPerson ep, Integer limit, Integer offset)
throws SQLException;
public List<WorkspaceItem> findByCollection(Context context, Collection c) throws SQLException;
public WorkspaceItem findByItem(Context context, Item i) throws SQLException;
@@ -45,4 +48,7 @@ public interface WorkspaceItemDAO extends GenericDAO<WorkspaceItem> {
int countRows(Context context) throws SQLException;
List<Map.Entry<Integer, Long>> getStageReachedCounts(Context context) throws SQLException;
public int countRows(Context context, EPerson ep) throws SQLException;
}

View File

@@ -51,6 +51,18 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
return list(query);
}
@Override
public List<WorkspaceItem> findByEPerson(Context context, EPerson ep, Integer limit, Integer offset)
throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, WorkspaceItem.class);
Root<WorkspaceItem> workspaceItemRoot = criteriaQuery.from(WorkspaceItem.class);
criteriaQuery.select(workspaceItemRoot);
criteriaQuery.where(criteriaBuilder.equal(workspaceItemRoot.get(WorkspaceItem_.item).get("submitter"), ep));
criteriaQuery.orderBy(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.workspaceItemId)));
return list(context, criteriaQuery, false, WorkspaceItem.class, limit, offset);
}
@Override
public List<WorkspaceItem> findByCollection(Context context, Collection c) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
@@ -58,8 +70,8 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
Root<WorkspaceItem> workspaceItemRoot = criteriaQuery.from(WorkspaceItem.class);
criteriaQuery.select(workspaceItemRoot);
criteriaQuery.where(criteriaBuilder.equal(workspaceItemRoot.get(WorkspaceItem_.collection), c));
criteriaQuery.orderBy(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.workspaceItemId)));
return list(context, criteriaQuery, false, WorkspaceItem.class, -1, -1);
}
@Override
@@ -74,14 +86,13 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
@Override
public List<WorkspaceItem> findAll(Context context) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, WorkspaceItem.class);
Root<WorkspaceItem> workspaceItemRoot = criteriaQuery.from(WorkspaceItem.class);
criteriaQuery.select(workspaceItemRoot);
List<javax.persistence.criteria.Order> orderList = new LinkedList<>();
orderList.add(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.item)));
orderList.add(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.workspaceItemId)));
criteriaQuery.orderBy(orderList);
@@ -96,7 +107,7 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
criteriaQuery.select(workspaceItemRoot);
List<javax.persistence.criteria.Order> orderList = new LinkedList<>();
orderList.add(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.item)));
orderList.add(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.workspaceItemId)));
criteriaQuery.orderBy(orderList);
@@ -105,8 +116,6 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
@Override
public List<WorkspaceItem> findWithSupervisedGroup(Context context) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, WorkspaceItem.class);
Root<WorkspaceItem> workspaceItemRoot = criteriaQuery.from(WorkspaceItem.class);
@@ -116,16 +125,11 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
List<javax.persistence.criteria.Order> orderList = new LinkedList<>();
orderList.add(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.workspaceItemId)));
criteriaQuery.orderBy(orderList);
return list(context, criteriaQuery, false, WorkspaceItem.class, -1, -1);
}
@Override
public List<WorkspaceItem> findBySupervisedGroupMember(Context context, EPerson ePerson) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, WorkspaceItem.class);
Root<WorkspaceItem> workspaceItemRoot = criteriaQuery.from(WorkspaceItem.class);
@@ -133,6 +137,7 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
Join<Group, EPerson> secondJoin = join.join("epeople");
criteriaQuery.select(workspaceItemRoot);
criteriaQuery.where(criteriaBuilder.equal(secondJoin.get(EPerson_.id), ePerson.getID()));
criteriaQuery.orderBy(criteriaBuilder.asc(workspaceItemRoot.get(WorkspaceItem_.workspaceItemId)));
return list(context, criteriaQuery, false, WorkspaceItem.class, -1, -1);
}
@@ -141,6 +146,14 @@ public class WorkspaceItemDAOImpl extends AbstractHibernateDAO<WorkspaceItem> im
return count(createQuery(context, "SELECT count(*) from WorkspaceItem"));
}
@Override
public int countRows(Context context, EPerson ep) throws SQLException {
Query query = createQuery(context,
"SELECT count(*) from WorkspaceItem ws where ws.item.submitter = :submitter");
query.setParameter("submitter", ep);
return count(query);
}
@Override
@SuppressWarnings("unchecked")
public List<Map.Entry<Integer, Long>> getStageReachedCounts(Context context) throws SQLException {

View File

@@ -72,6 +72,21 @@ public interface WorkspaceItemService extends InProgressSubmissionService<Worksp
public List<WorkspaceItem> findByEPerson(Context context, EPerson ep)
throws SQLException;
/**
* Get a page of workspace items for a particular e-person. These are ordered by
* workspace item ID, since this should likely keep them in the order in
* which they were created.
*
* @param context the context object
* @param ep the eperson
* @param limit the max number of workspaceitems to return
* @param offset the offset
* @return the corresponding workspace items
* @throws SQLException if database error
*/
public List<WorkspaceItem> findByEPerson(Context context, EPerson ep, Integer limit, Integer offset)
throws SQLException;
/**
* Get all workspace items for a particular collection.
*
@@ -146,4 +161,8 @@ public interface WorkspaceItemService extends InProgressSubmissionService<Worksp
* @throws SQLException if database error
*/
List<Map.Entry<Integer, Long>> getStageReachedCounts(Context context) throws SQLException;
public int countByEPerson(Context context, EPerson ep) throws SQLException;
}

View File

@@ -100,17 +100,19 @@ public class RequiredMetadata extends AbstractCurationTask {
reqList = new ArrayList<String>();
List<DCInputSet> inputSet = reader.getInputsByCollectionHandle(handle);
for (DCInputSet inputs : inputSet) {
for (DCInput input : inputs.getFields()) {
if (input.isRequired()) {
StringBuilder sb = new StringBuilder();
sb.append(input.getSchema()).append(".");
sb.append(input.getElement()).append(".");
String qual = input.getQualifier();
if (qual == null) {
qual = "";
for (DCInput[] row : inputs.getFields()) {
for (DCInput input : row) {
if (input.isRequired()) {
StringBuilder sb = new StringBuilder();
sb.append(input.getSchema()).append(".");
sb.append(input.getElement()).append(".");
String qual = input.getQualifier();
if (qual == null) {
qual = "";
}
sb.append(qual);
reqList.add(sb.toString());
}
sb.append(qual);
reqList.add(sb.toString());
}
}
reqMap.put(inputs.getFormName(), reqList);

View File

@@ -152,7 +152,6 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
@Override
public void afterPropertiesSet() throws Exception {
log.info("solr-statistics.spidersfile:" + configurationService.getProperty("solr-statistics.spidersfile"));
log.info("solr-statistics.server:" + configurationService.getProperty("solr-statistics.server"));
log.info("usage-statistics.dbfile:" + configurationService.getProperty("usage-statistics.dbfile"));

View File

@@ -7,10 +7,12 @@
*/
package org.dspace.submit;
import org.dspace.app.itemimport.BTEBatchImportService;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.InProgressSubmission;
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;
@@ -20,24 +22,27 @@ import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.services.model.Request;
/**
* Abstract processing class for DSpace Submission Steps. This defines the base
* methods which are required for any Step processing class.
* Abstract processing class for DSpace Submission Steps. This defines the base methods which are required for any Step
* processing class.
*/
public abstract class AbstractProcessingStep {
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 CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
protected BTEBatchImportService bteBatchImportService = DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("org.dspace.app.itemimport" + ".BTEBatchImportService", BTEBatchImportService.class);
public abstract void doProcessing(Context context, Request req, InProgressSubmission wsi);
public abstract void doPreProcessing(Context context, InProgressSubmission wsi);
public abstract void doPostProcessing(Context context, Request obj, InProgressSubmission wsi);
public abstract void doPostProcessing(Context context, InProgressSubmission wsi);
}

View File

@@ -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.submit.extraction;
import java.util.List;
import gr.ekt.bte.dataloader.FileDataLoader;
import org.dspace.services.ConfigurationService;
/**
* Configuration bean to associate a BTE FileDataLoader with a specific list of format identified by the file
* extensions. See config/spring/api/metadata-extractor.xml
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public class MetadataExtractor {
private List<String> extensions;
private FileDataLoader dataLoader;
private ConfigurationService configurationService;
public List<String> getExtensions() {
return extensions;
}
public void setExtensions(List<String> mime) {
this.extensions = mime;
}
public FileDataLoader getDataLoader() {
return dataLoader;
}
public void setDataLoader(FileDataLoader dataLoader) {
this.dataLoader = dataLoader;
}
public ConfigurationService getConfigurationService() {
return configurationService;
}
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
}

View File

@@ -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.submit.listener;
import java.util.Map;
import gr.ekt.bte.core.DataLoader;
import org.dspace.services.ConfigurationService;
/**
* Configuration bean to map metadata to identifiers (i.e dc.identifier.doi -> doi, dc.identifier.isbn -> isbn) and
* alias to BTE Data Loader. See config/spring/api/step-processing.xml
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class MetadataListener {
/**
* Metadata to identifier map
*/
private Map<String, String> metadata;
private ConfigurationService configurationService;
/**
* Alias to data loader map
*/
private Map<String, DataLoader> dataloadersMap;
public ConfigurationService getConfigurationService() {
return configurationService;
}
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
public Map<String, String> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
public Map<String, DataLoader> getDataloadersMap() {
return dataloadersMap;
}
public void setDataloadersMap(Map<String, DataLoader> dataloadersMap) {
this.dataloadersMap = dataloadersMap;
}
}

View File

@@ -275,13 +275,15 @@ public class DSpaceWorkspaceItemOutputGenerator implements OutputGenerator {
String qualifier) throws DCInputsReaderException {
List<DCInputSet> dcinputsets = new DCInputsReader().getInputsBySubmissionName(formName);
for (DCInputSet dcinputset : dcinputsets) {
for (DCInput dcinput : dcinputset.getFields()) {
if (dcinput.getSchema().equals(schema)
&& dcinput.getElement().equals(element)
&& ((dcinput.getQualifier() != null && dcinput
.getQualifier().equals(qualifier))
|| (dcinput.getQualifier() == null && qualifier == null))) {
return dcinput;
for (DCInput[] dcrow : dcinputset.getFields()) {
for (DCInput dcinput : dcrow) {
if (dcinput.getSchema().equals(schema)
&& dcinput.getElement().equals(element)
&& ((dcinput.getQualifier() != null && dcinput
.getQualifier().equals(qualifier))
|| (dcinput.getQualifier() == null && qualifier == null))) {
return dcinput;
}
}
}
}

View File

@@ -7,10 +7,22 @@
*/
package org.dspace.submit.model;
/**
* A simple POJO to store information about the available languages for a field
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*
*/
public class LanguageFormField {
/**
* The value to present to the user
*/
private String display;
/**
* The internal iso code to store in the database
*/
private String code;
public LanguageFormField(String code, String display) {

View File

@@ -10,7 +10,6 @@ package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
@@ -21,13 +20,13 @@ public class AccessStep extends AbstractProcessingStep {
private static Logger log = Logger.getLogger(AccessStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -12,7 +12,6 @@ import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.license.factory.LicenseServiceFactory;
import org.dspace.license.service.CreativeCommonsService;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class CCLicenseStep extends AbstractProcessingStep {
@@ -25,15 +24,15 @@ public class CCLicenseStep extends AbstractProcessingStep {
.getCreativeCommonsService();
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
}
}

View File

@@ -10,7 +10,6 @@ package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class CompleteStep extends AbstractProcessingStep {
@@ -20,13 +19,13 @@ public class CompleteStep extends AbstractProcessingStep {
private static Logger log = Logger.getLogger(CompleteStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -8,30 +8,14 @@
package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class DescribeStep extends AbstractProcessingStep {
/**
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class DescribeStep extends MetadataStep {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(DescribeStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,22 @@
/**
* 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.submit.step;
import org.apache.log4j.Logger;
/**
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class ExtractionStep extends MetadataStep {
/**
* log4j logger
*/
private static Logger log = Logger
.getLogger(ExtractionStep.class);
}

View File

@@ -9,19 +9,18 @@ package org.dspace.submit.step;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class InitialQuestionsStep extends AbstractProcessingStep {
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -10,7 +10,6 @@ package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class LicenseStep extends AbstractProcessingStep {
@@ -21,13 +20,13 @@ public class LicenseStep extends AbstractProcessingStep {
private static Logger log = Logger.getLogger(LicenseStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -0,0 +1,197 @@
/**
* 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.submit.step;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import gr.ekt.bte.core.DataLoader;
import gr.ekt.bte.core.Record;
import gr.ekt.bte.core.Value;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpException;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.core.Context;
import org.dspace.core.Utils;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.submit.AbstractProcessingStep;
import org.dspace.submit.listener.MetadataListener;
import org.dspace.submit.lookup.SubmissionLookupDataLoader;
//FIXME move to the ExtractionStep
/**
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class MetadataStep extends AbstractProcessingStep {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(MetadataStep.class);
protected List<MetadataListener> listeners = DSpaceServicesFactory.getInstance().getServiceManager()
.getServicesByType(MetadataListener.class);
protected Map<String, List<MetadataValue>> metadataMap = new HashMap<String, List<MetadataValue>>();
private Map<String, Set<String>> results = new HashMap<String, Set<String>>();
private Map<String, String> mappingIdentifier = new HashMap<String, String>();
@Override
public void doPreProcessing(Context context, InProgressSubmission wsi) {
for (MetadataListener listener : listeners) {
for (String metadata : listener.getMetadata().keySet()) {
String[] tokenized = Utils.tokenize(metadata);
List<MetadataValue> mm = itemService.getMetadata(wsi.getItem(), tokenized[0], tokenized[1],
tokenized[2], Item.ANY);
if (mm != null && !mm.isEmpty()) {
metadataMap.put(metadata, mm);
} else {
metadataMap.put(metadata, new ArrayList<MetadataValue>());
}
mappingIdentifier.put(metadata, listener.getMetadata().get(metadata));
}
}
}
@Override
public void doPostProcessing(Context context, InProgressSubmission wsi) {
external:
for (String metadata : metadataMap.keySet()) {
String[] tokenized = Utils.tokenize(metadata);
List<MetadataValue> currents = itemService.getMetadata(wsi.getItem(), tokenized[0], tokenized[1],
tokenized[2], Item.ANY);
if (currents != null && !currents.isEmpty()) {
List<MetadataValue> olds = metadataMap.get(metadata);
if (olds.isEmpty()) {
process(context, metadata, currents);
continue external;
}
internal:
for (MetadataValue current : currents) {
boolean found = false;
for (MetadataValue old : olds) {
if (old.getValue().equals(current.getValue())) {
found = true;
}
}
if (!found) {
process(context, metadata, current);
}
}
}
}
if (!results.isEmpty()) {
for (MetadataListener listener : listeners) {
for (DataLoader dataLoader : listener.getDataloadersMap().values()) {
SubmissionLookupDataLoader submissionLookupDataLoader = (SubmissionLookupDataLoader) dataLoader;
try {
List<Record> recordSet = submissionLookupDataLoader.getByIdentifier(context, results);
List<Record> resultSet = convertFields(recordSet, bteBatchImportService.getOutputMap());
enrichItem(context, resultSet, wsi.getItem());
} catch (HttpException | IOException | SQLException | AuthorizeException e) {
log.error(e.getMessage(), e);
}
}
}
}
}
protected void enrichItem(Context context, List<Record> rset, Item item) throws SQLException, AuthorizeException {
for (Record record : rset) {
for (String field : record.getFields()) {
try {
String[] tfield = Utils.tokenize(field);
List<MetadataValue> mdvs = itemService
.getMetadata(item, tfield[0], tfield[1], tfield[2], Item.ANY);
if (mdvs == null || mdvs.isEmpty()) {
for (Value value : record.getValues(field)) {
itemService.addMetadata(context, item, tfield[0], tfield[1], tfield[2], null,
value.getAsString());
}
} else {
external:
for (Value value : record.getValues(field)) {
boolean found = false;
for (MetadataValue mdv : mdvs) {
if (mdv.getValue().equals(value.getAsString())) {
found = true;
continue external;
}
}
if (!found) {
itemService.addMetadata(context, item, tfield[0], tfield[1], tfield[2], null,
value.getAsString());
}
}
}
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
}
}
itemService.update(context, item);
}
private void process(Context context, String metadata, List<MetadataValue> currents) {
for (MetadataValue current : currents) {
process(context, metadata, current);
}
}
private void process(Context context, String metadata, MetadataValue current) {
String key = mappingIdentifier.get(metadata);
Set<String> identifiers = null;
if (!results.containsKey(key)) {
identifiers = new HashSet<String>();
} else {
identifiers = results.get(key);
}
identifiers.add(current.getValue());
results.put(key, identifiers);
}
public List<Record> convertFields(List<Record> recordSet, Map<String, String> fieldMap) {
List<Record> result = new ArrayList<Record>();
for (Record publication : recordSet) {
for (String fieldName : fieldMap.keySet()) {
String md = null;
if (fieldMap != null) {
md = fieldMap.get(fieldName);
}
if (StringUtils.isBlank(md)) {
continue;
} else {
md = md.trim();
}
if (publication.isMutable()) {
List<Value> values = publication.getValues(md);
publication.makeMutable().removeField(md);
publication.makeMutable().addField(fieldName, values);
}
}
result.add(publication);
}
return result;
}
}

View File

@@ -9,19 +9,18 @@ package org.dspace.submit.step;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class SampleStep extends AbstractProcessingStep {
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -10,7 +10,6 @@ package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class SelectCollectionStep extends AbstractProcessingStep {
@@ -18,13 +17,13 @@ public class SelectCollectionStep extends AbstractProcessingStep {
private static final Logger log = Logger.getLogger(SelectCollectionStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -10,7 +10,6 @@ package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class StartSubmissionLookupStep extends AbstractProcessingStep {
@@ -21,14 +20,14 @@ public class StartSubmissionLookupStep extends AbstractProcessingStep {
.getLogger(StartSubmissionLookupStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
}
}

View File

@@ -10,7 +10,6 @@ package org.dspace.submit.step;
import org.apache.log4j.Logger;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class UploadStep extends AbstractProcessingStep {
@@ -20,13 +19,13 @@ public class UploadStep extends AbstractProcessingStep {
private static final Logger log = Logger.getLogger(UploadStep.class);
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -9,19 +9,18 @@ package org.dspace.submit.step;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.dspace.submit.AbstractProcessingStep;
public class VerifyStep extends AbstractProcessingStep {
@Override
public void doProcessing(Context context, Request req, InProgressSubmission wsi) {
public void doPreProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}
@Override
public void doPostProcessing(Context context, Request obj, InProgressSubmission wsi) {
public void doPostProcessing(Context context, InProgressSubmission wsi) {
// TODO Auto-generated method stub
}

View File

@@ -4,402 +4,416 @@
<input-forms>
<!-- The form-definitions map lays out the detailed definition of all the -->
<!-- submission forms. Each separate form set has a unique name as an -->
<!-- attribute. This name matches one of the names in the form-map. One -->
<!-- named form set has the name "traditional"; as this name suggests, -->
<!-- it is the old style and is also the default, which gets used when -->
<!-- the specified collection has no correspondingly-named form set. -->
<!-- -->
<!-- Each form set contains an ordered set of pages; each page defines -->
<!-- one submission metadata entry screen. Each page has an ordered list -->
<!-- of field definitions, Each field definition corresponds to one -->
<!-- metadata entry (a so-called row), which has a DC element name, a -->
<!-- displayed label, a text string prompt which is called a hint, and -->
<!-- an input-type. Each field also may hold optional elements: DC -->
<!-- qualifier name, a repeatable flag, and a text string whose presence -->
<!-- serves as a 'this field is required' flag. -->
<!-- The form-definitions map lays out the detailed definition of all the -->
<!-- submission forms. Each separate form set has a unique name as an -->
<!-- attribute. This name matches one of the names in the form-map. One -->
<!-- named form set has the name "traditional"; as this name suggests, -->
<!-- it is the old style and is also the default, which gets used when -->
<!-- the specified collection has no correspondingly-named form set. -->
<!-- -->
<!-- Each form set contains an ordered set of pages; each page defines -->
<!-- one submission metadata entry screen. Each page has an ordered list -->
<!-- of field definitions, Each field definition corresponds to one -->
<!-- metadata entry (a so-called row), which has a DC element name, a -->
<!-- displayed label, a text string prompt which is called a hint, and -->
<!-- an input-type. Each field also may hold optional elements: DC -->
<!-- qualifier name, a repeatable flag, and a text string whose presence -->
<!-- serves as a 'this field is required' flag. -->
<form-definitions>
<form name="bitstream-metadata">
<field>
<dc-schema>dc</dc-schema>
<dc-element>title</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Title</label>
<input-type>onebox</input-type>
<hint>Enter the name of the file.</hint>
<required>You must enter a main title for this item.</required>
</field>
<form-definitions>
<form name="bitstream-metadata">
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>title</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Title</label>
<input-type>onebox</input-type>
<hint>Enter the name of the file.</hint>
<required>You must enter a main title for this item.</required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<repeatable>true</repeatable>
<label>Description</label>
<input-type>textarea</input-type>
<hint>Enter a description for the file</hint>
<required></required>
</field>
</row>
</form>
<form name="traditionalpageone">
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>contributor</dc-element>
<dc-qualifier>author</dc-qualifier>
<repeatable>true</repeatable>
<label>Authors</label>
<input-type>name</input-type>
<hint>Enter the names of the authors of this item.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>title</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Title</label>
<input-type>onebox</input-type>
<hint>Enter the main title of the item.</hint>
<required>You must enter a main title for this item.</required>
<!-- <language value-pairs-name="common_iso_languages">true</language> -->
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>title</dc-element>
<dc-qualifier>alternative</dc-qualifier>
<repeatable>true</repeatable>
<label>Other Titles</label>
<input-type>onebox</input-type>
<hint>If the item has any alternative titles, please enter them here.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>date</dc-element>
<dc-qualifier>issued</dc-qualifier>
<repeatable>false</repeatable>
<label>Date of Issue</label>
<style>col-sm-4</style>
<input-type>date</input-type>
<hint>Please give the date of previous publication or public distribution.
You can leave out the day and/or month if they aren't
applicable.</hint>
<required>You must enter at least the year.</required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<repeatable>true</repeatable>
<label>Description</label>
<input-type>textarea</input-type>
<hint>Enter a description for the file</hint>
<required></required>
</field>
</form>
<field>
<dc-schema>dc</dc-schema>
<dc-element>publisher</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Publisher</label>
<style>col-sm-8</style>
<input-type>onebox</input-type>
<hint>Enter the name of the publisher of the previously issued instance of this item.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>identifier</dc-element>
<dc-qualifier>citation</dc-qualifier>
<repeatable>false</repeatable>
<label>Citation</label>
<input-type>onebox</input-type>
<hint>Enter the standard citation for the previously issued instance of this item.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>relation</dc-element>
<dc-qualifier>ispartofseries</dc-qualifier>
<repeatable>true</repeatable>
<label>Series/Report No.</label>
<input-type>series</input-type>
<hint>Enter the series and number assigned to this item by your community.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>identifier</dc-element>
<dc-qualifier></dc-qualifier>
<!-- An input-type of qualdrop_value MUST be marked as repeatable -->
<repeatable>true</repeatable>
<label>Identifiers</label>
<input-type value-pairs-name="common_identifiers">qualdrop_value</input-type>
<hint>If the item has any identification numbers or codes associated with
it, please enter the types and the actual numbers or codes.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>type</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>true</repeatable>
<label>Type</label>
<input-type value-pairs-name="common_types">dropdown</input-type>
<hint>Select the type(s) of content of the item. To select more than one value in the list, you may have to hold down the "CTRL" or "Shift" key.</hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>language</dc-element>
<dc-qualifier>iso</dc-qualifier>
<repeatable>false</repeatable>
<label>Language</label>
<input-type value-pairs-name="common_iso_languages">dropdown</input-type>
<hint>Select the language of the main content of the item. If the language does not appear in the list, please select 'Other'. If the content does not really have a language (for example, if it is a dataset or an image) please select 'N/A'.</hint>
<required></required>
</field>
</row>
</form>
<form name="traditionalpagetwo">
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>subject</dc-element>
<dc-qualifier></dc-qualifier>
<!-- An input-type of twobox MUST be marked as repeatable -->
<repeatable>true</repeatable>
<label>Subject Keywords</label>
<input-type>twobox</input-type>
<hint>Enter appropriate subject keywords or phrases. </hint>
<required></required>
<vocabulary>srsc</vocabulary>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<dc-qualifier>abstract</dc-qualifier>
<repeatable>false</repeatable>
<label>Abstract</label>
<input-type>textarea</input-type>
<hint>Enter the abstract of the item. </hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<dc-qualifier>sponsorship</dc-qualifier>
<repeatable>false</repeatable>
<label>Sponsors</label>
<input-type>textarea</input-type>
<hint>Enter the names of any sponsors and/or funding codes in the box. </hint>
<required></required>
</field>
</row>
<row>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Description</label>
<input-type>textarea</input-type>
<hint>Enter any other description or comments in this box. </hint>
<required></required>
</field>
</row>
</form>
<form name="traditionalpageone">
<field>
<dc-schema>dc</dc-schema>
<dc-element>contributor</dc-element>
<dc-qualifier>author</dc-qualifier>
<repeatable>true</repeatable>
<label>Authors</label>
<input-type>name</input-type>
<hint>Enter the names of the authors of this item.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>title</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Title</label>
<input-type>onebox</input-type>
<hint>Enter the main title of the item.</hint>
<required>You must enter a main title for this item.</required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>title</dc-element>
<dc-qualifier>alternative</dc-qualifier>
<repeatable>true</repeatable>
<label>Other Titles</label>
<input-type>onebox</input-type>
<hint>If the item has any alternative titles, please enter them here.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>date</dc-element>
<dc-qualifier>issued</dc-qualifier>
<repeatable>false</repeatable>
<label>Date of Issue</label>
<input-type>date</input-type>
<hint>Please give the date of previous publication or public distribution.
You can leave out the day and/or month if they aren't
applicable.
</hint>
<required>You must enter at least the year.</required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>publisher</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Publisher</label>
<input-type>onebox</input-type>
<hint>Enter the name of the publisher of the previously issued instance of this item.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>identifier</dc-element>
<dc-qualifier>citation</dc-qualifier>
<repeatable>false</repeatable>
<label>Citation</label>
<input-type>onebox</input-type>
<hint>Enter the standard citation for the previously issued instance of this item.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>relation</dc-element>
<dc-qualifier>ispartofseries</dc-qualifier>
<repeatable>true</repeatable>
<label>Series/Report No.</label>
<input-type>series</input-type>
<hint>Enter the series and number assigned to this item by your community.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>identifier</dc-element>
<dc-qualifier></dc-qualifier>
<!-- An input-type of qualdrop_value MUST be marked as repeatable -->
<repeatable>true</repeatable>
<label>Identifiers</label>
<input-type value-pairs-name="common_identifiers">qualdrop_value</input-type>
<hint>If the item has any identification numbers or codes associated with
it, please enter the types and the actual numbers or codes.
</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>type</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>true</repeatable>
<label>Type</label>
<input-type value-pairs-name="common_types">dropdown</input-type>
<hint>Select the type(s) of content of the item. To select more than one value in the list, you may have
to hold down the "CTRL" or "Shift" key.
</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>language</dc-element>
<dc-qualifier>iso</dc-qualifier>
<repeatable>false</repeatable>
<label>Language</label>
<input-type value-pairs-name="common_iso_languages">dropdown</input-type>
<hint>Select the language of the main content of the item. If the language does not appear in the list,
please select 'Other'. If the content does not really have a language (for example, if it is a
dataset or an image) please select 'N/A'.
</hint>
<required></required>
</field>
</form>
<form name="traditionalpagetwo">
<field>
<dc-schema>dc</dc-schema>
<dc-element>subject</dc-element>
<dc-qualifier></dc-qualifier>
<!-- An input-type of twobox MUST be marked as repeatable -->
<repeatable>true</repeatable>
<label>Subject Keywords</label>
<input-type>twobox</input-type>
<hint>Enter appropriate subject keywords or phrases.</hint>
<required></required>
<vocabulary>srsc</vocabulary>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<dc-qualifier>abstract</dc-qualifier>
<repeatable>false</repeatable>
<label>Abstract</label>
<input-type>textarea</input-type>
<hint>Enter the abstract of the item.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<dc-qualifier>sponsorship</dc-qualifier>
<repeatable>false</repeatable>
<label>Sponsors</label>
<input-type>textarea</input-type>
<hint>Enter the names of any sponsors and/or funding codes in the box.</hint>
<required></required>
</field>
<field>
<dc-schema>dc</dc-schema>
<dc-element>description</dc-element>
<dc-qualifier></dc-qualifier>
<repeatable>false</repeatable>
<label>Description</label>
<input-type>textarea</input-type>
<hint>Enter any other description or comments in this box.</hint>
<required></required>
</field>
</form>
</form-definitions>
</form-definitions>
<!-- form-value-pairs populate dropdown and qualdrop-value lists. -->
<!-- The form-value-pairs element holds child elements named 'value-pairs' -->
<!-- A 'value-pairs' element has a value-pairs-name and a dc-term -->
<!-- attribute. The dc-term attribute specifies which to which Dublin Core -->
<!-- Term this set of value-pairs applies. -->
<!-- Current dc-terms are: identifier-pairs, type-pairs, and -->
<!-- language_iso-pairs. The name attribute matches a name -->
<!-- in the form-map, above. -->
<!-- A value-pair contains one 'pair' for each value displayed in the list -->
<!-- Each pair contains a 'displayed-value' element and a 'stored-value' -->
<!-- element. A UI list displays the displayed-values, but the program -->
<!-- stores the associated stored-values in the database. -->
<!-- form-value-pairs populate dropdown and qualdrop-value lists. -->
<!-- The form-value-pairs element holds child elements named 'value-pairs' -->
<!-- A 'value-pairs' element has a value-pairs-name and a dc-term -->
<!-- attribute. The dc-term attribute specifies which to which Dublin Core -->
<!-- Term this set of value-pairs applies. -->
<!-- Current dc-terms are: identifier-pairs, type-pairs, and -->
<!-- language_iso-pairs. The name attribute matches a name -->
<!-- in the form-map, above. -->
<!-- A value-pair contains one 'pair' for each value displayed in the list -->
<!-- Each pair contains a 'displayed-value' element and a 'stored-value' -->
<!-- element. A UI list displays the displayed-values, but the program -->
<!-- stores the associated stored-values in the database. -->
<form-value-pairs>
<value-pairs value-pairs-name="common_identifiers" dc-term="identifier">
<pair>
<displayed-value>ISSN</displayed-value>
<stored-value>issn</stored-value>
</pair>
<pair>
<displayed-value>Other</displayed-value>
<stored-value>other</stored-value>
</pair>
<pair>
<displayed-value>ISMN</displayed-value>
<stored-value>ismn</stored-value>
</pair>
<pair>
<displayed-value>Gov't Doc #</displayed-value>
<stored-value>govdoc</stored-value>
</pair>
<pair>
<displayed-value>URI</displayed-value>
<stored-value>uri</stored-value>
</pair>
<pair>
<displayed-value>ISBN</displayed-value>
<stored-value>isbn</stored-value>
</pair>
</value-pairs>
<form-value-pairs>
<value-pairs value-pairs-name="common_identifiers" dc-term="identifier">
<pair>
<displayed-value>ISSN</displayed-value>
<stored-value>issn</stored-value>
</pair>
<pair>
<displayed-value>Other</displayed-value>
<stored-value>other</stored-value>
</pair>
<pair>
<displayed-value>ISMN</displayed-value>
<stored-value>ismn</stored-value>
</pair>
<pair>
<displayed-value>Gov't Doc #</displayed-value>
<stored-value>govdoc</stored-value>
</pair>
<pair>
<displayed-value>URI</displayed-value>
<stored-value>uri</stored-value>
</pair>
<pair>
<displayed-value>ISBN</displayed-value>
<stored-value>isbn</stored-value>
</pair>
</value-pairs>
<value-pairs value-pairs-name="common_types" dc-term="type">
<pair>
<displayed-value>Animation</displayed-value>
<stored-value>Animation</stored-value>
</pair>
<pair>
<displayed-value>Article</displayed-value>
<stored-value>Article</stored-value>
</pair>
<pair>
<displayed-value>Book</displayed-value>
<stored-value>Book</stored-value>
</pair>
<pair>
<displayed-value>Book chapter</displayed-value>
<stored-value>Book chapter</stored-value>
</pair>
<pair>
<displayed-value>Dataset</displayed-value>
<stored-value>Dataset</stored-value>
</pair>
<pair>
<displayed-value>Learning Object</displayed-value>
<stored-value>Learning Object</stored-value>
</pair>
<pair>
<displayed-value>Image</displayed-value>
<stored-value>Image</stored-value>
</pair>
<pair>
<displayed-value>Image, 3-D</displayed-value>
<stored-value>Image, 3-D</stored-value>
</pair>
<pair>
<displayed-value>Map</displayed-value>
<stored-value>Map</stored-value>
</pair>
<pair>
<displayed-value>Musical Score</displayed-value>
<stored-value>Musical Score</stored-value>
</pair>
<pair>
<displayed-value>Plan or blueprint</displayed-value>
<stored-value>Plan or blueprint</stored-value>
</pair>
<pair>
<displayed-value>Preprint</displayed-value>
<stored-value>Preprint</stored-value>
</pair>
<pair>
<displayed-value>Presentation</displayed-value>
<stored-value>Presentation</stored-value>
</pair>
<pair>
<displayed-value>Recording, acoustical</displayed-value>
<stored-value>Recording, acoustical</stored-value>
</pair>
<pair>
<displayed-value>Recording, musical</displayed-value>
<stored-value>Recording, musical</stored-value>
</pair>
<pair>
<displayed-value>Recording, oral</displayed-value>
<stored-value>Recording, oral</stored-value>
</pair>
<pair>
<displayed-value>Software</displayed-value>
<stored-value>Software</stored-value>
</pair>
<pair>
<displayed-value>Technical Report</displayed-value>
<stored-value>Technical Report</stored-value>
</pair>
<pair>
<displayed-value>Thesis</displayed-value>
<stored-value>Thesis</stored-value>
</pair>
<pair>
<displayed-value>Video</displayed-value>
<stored-value>Video</stored-value>
</pair>
<pair>
<displayed-value>Working Paper</displayed-value>
<stored-value>Working Paper</stored-value>
</pair>
<pair>
<displayed-value>Other</displayed-value>
<stored-value>Other</stored-value>
</pair>
</value-pairs>
<value-pairs value-pairs-name="common_types" dc-term="type">
<pair>
<displayed-value>Animation</displayed-value>
<stored-value>Animation</stored-value>
</pair>
<pair>
<displayed-value>Article</displayed-value>
<stored-value>Article</stored-value>
</pair>
<pair>
<displayed-value>Book</displayed-value>
<stored-value>Book</stored-value>
</pair>
<pair>
<displayed-value>Book chapter</displayed-value>
<stored-value>Book chapter</stored-value>
</pair>
<pair>
<displayed-value>Dataset</displayed-value>
<stored-value>Dataset</stored-value>
</pair>
<pair>
<displayed-value>Learning Object</displayed-value>
<stored-value>Learning Object</stored-value>
</pair>
<pair>
<displayed-value>Image</displayed-value>
<stored-value>Image</stored-value>
</pair>
<pair>
<displayed-value>Image, 3-D</displayed-value>
<stored-value>Image, 3-D</stored-value>
</pair>
<pair>
<displayed-value>Map</displayed-value>
<stored-value>Map</stored-value>
</pair>
<pair>
<displayed-value>Musical Score</displayed-value>
<stored-value>Musical Score</stored-value>
</pair>
<pair>
<displayed-value>Plan or blueprint</displayed-value>
<stored-value>Plan or blueprint</stored-value>
</pair>
<pair>
<displayed-value>Preprint</displayed-value>
<stored-value>Preprint</stored-value>
</pair>
<pair>
<displayed-value>Presentation</displayed-value>
<stored-value>Presentation</stored-value>
</pair>
<pair>
<displayed-value>Recording, acoustical</displayed-value>
<stored-value>Recording, acoustical</stored-value>
</pair>
<pair>
<displayed-value>Recording, musical</displayed-value>
<stored-value>Recording, musical</stored-value>
</pair>
<pair>
<displayed-value>Recording, oral</displayed-value>
<stored-value>Recording, oral</stored-value>
</pair>
<pair>
<displayed-value>Software</displayed-value>
<stored-value>Software</stored-value>
</pair>
<pair>
<displayed-value>Technical Report</displayed-value>
<stored-value>Technical Report</stored-value>
</pair>
<pair>
<displayed-value>Thesis</displayed-value>
<stored-value>Thesis</stored-value>
</pair>
<pair>
<displayed-value>Video</displayed-value>
<stored-value>Video</stored-value>
</pair>
<pair>
<displayed-value>Working Paper</displayed-value>
<stored-value>Working Paper</stored-value>
</pair>
<pair>
<displayed-value>Other</displayed-value>
<stored-value>Other</stored-value>
</pair>
</value-pairs>
<!-- default language order: (from dspace 1.2.1)
"en_US", "en", "es", "de", "fr", "it", "ja", "zh", "other", ""
-->
<value-pairs value-pairs-name="common_iso_languages" dc-term="language_iso">
<pair>
<displayed-value>N/A</displayed-value>
<stored-value></stored-value>
</pair>
<pair>
<displayed-value>English (United States)</displayed-value>
<stored-value>en_US</stored-value>
</pair>
<pair>
<displayed-value>English</displayed-value>
<stored-value>en</stored-value>
</pair>
<pair>
<displayed-value>Spanish</displayed-value>
<stored-value>es</stored-value>
</pair>
<pair>
<displayed-value>German</displayed-value>
<stored-value>de</stored-value>
</pair>
<pair>
<displayed-value>French</displayed-value>
<stored-value>fr</stored-value>
</pair>
<pair>
<displayed-value>Italian</displayed-value>
<stored-value>it</stored-value>
</pair>
<pair>
<displayed-value>Japanese</displayed-value>
<stored-value>ja</stored-value>
</pair>
<pair>
<displayed-value>Chinese</displayed-value>
<stored-value>zh</stored-value>
</pair>
<pair>
<displayed-value>Turkish</displayed-value>
<stored-value>tr</stored-value>
</pair>
<pair>
<displayed-value>(Other)</displayed-value>
<stored-value>other</stored-value>
</pair>
</value-pairs>
<!-- default language order: (from dspace 1.2.1)
"en_US", "en", "es", "de", "fr", "it", "ja", "zh", "other", ""
-->
<value-pairs value-pairs-name="common_iso_languages" dc-term="language_iso">
<pair>
<displayed-value>N/A</displayed-value>
<stored-value></stored-value>
</pair>
<pair>
<displayed-value>English (United States)</displayed-value>
<stored-value>en_US</stored-value>
</pair>
<pair>
<displayed-value>English</displayed-value>
<stored-value>en</stored-value>
</pair>
<pair>
<displayed-value>Spanish</displayed-value>
<stored-value>es</stored-value>
</pair>
<pair>
<displayed-value>German</displayed-value>
<stored-value>de</stored-value>
</pair>
<pair>
<displayed-value>French</displayed-value>
<stored-value>fr</stored-value>
</pair>
<pair>
<displayed-value>Italian</displayed-value>
<stored-value>it</stored-value>
</pair>
<pair>
<displayed-value>Japanese</displayed-value>
<stored-value>ja</stored-value>
</pair>
<pair>
<displayed-value>Chinese</displayed-value>
<stored-value>zh</stored-value>
</pair>
<pair>
<displayed-value>Turkish</displayed-value>
<stored-value>tr</stored-value>
</pair>
<pair>
<displayed-value>(Other)</displayed-value>
<stored-value>other</stored-value>
</pair>
</value-pairs>
</form-value-pairs>
</form-value-pairs>
</input-forms>

View File

@@ -10,20 +10,29 @@ package org.dspace.statistics.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Collections;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;
import org.apache.commons.collections.CollectionUtils;
import org.dspace.core.Utils;
@@ -54,6 +63,15 @@ public class DummyHttpServletRequest implements HttpServletRequest {
this.remoteHost = host;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#changeSessionId
*/
@Override
public String changeSessionId() {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#getAuthType()
*/
@@ -99,6 +117,15 @@ public class DummyHttpServletRequest implements HttpServletRequest {
List<String> values = headers.computeIfAbsent(headerName, k -> new LinkedList<>());
values.add(headerValue);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#getDispatcherType()
*/
@Override
public DispatcherType getDispatcherType() {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)
*/
@@ -270,6 +297,55 @@ public class DummyHttpServletRequest implements HttpServletRequest {
return false;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#authenticate(javax.servlet.http.HttpServletResponse)
*/
@Override
public boolean authenticate(HttpServletResponse httpServletResponse) {
return false;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#login(java.lang.String,java.lang.String)
*/
@Override
public void login(String s, String s1) {
return;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#logout()
*/
@Override
public void logout() {
return;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#getPart(java.lang.String)
*/
@Override
public Part getPart(String arg0) {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#getParts()
*/
@Override
public Collection<Part> getParts() {
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#upgrade(java.lang.Class<T>)
*/
@Override
public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
*/
@@ -324,6 +400,14 @@ public class DummyHttpServletRequest implements HttpServletRequest {
return 0;
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#getContentLengthLong()
*/
@Override
public long getContentLengthLong() {
return 0;
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#getContentType()
*/
@@ -505,10 +589,51 @@ public class DummyHttpServletRequest implements HttpServletRequest {
*/
@Override
public void setCharacterEncoding(String arg0)
throws UnsupportedEncodingException {
throws UnsupportedOperationException {
throw new UnsupportedOperationException("Not supported yet.");
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#startAsync
*/
@Override
public AsyncContext startAsync() throws IllegalStateException {
throw new IllegalStateException("Not supported yet.");
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#startAsync(javax.servlet.ServletRequest,javax.servlet.ServletResponse)
*/
@Override
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
throws IllegalStateException {
throw new IllegalStateException("Not supported yet.");
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#isAsyncStarted
*/
@Override
public boolean isAsyncStarted() {
return false;
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#isAsyncSupported
*/
@Override
public boolean isAsyncSupported() {
return false;
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#getAsyncContext
*/
@Override
public AsyncContext getAsyncContext() {
return null;
}
@Override
public int getRemotePort() {
throw new UnsupportedOperationException("Not supported yet.");
@@ -529,4 +654,12 @@ public class DummyHttpServletRequest implements HttpServletRequest {
throw new UnsupportedOperationException("Not supported yet.");
}
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#getServletContext
*/
@Override
public ServletContext getServletContext() {
return null;
}
}

View File

@@ -16,7 +16,6 @@
<properties>
<!-- This is the path to the root [dspace-src] directory. -->
<root.basedir>${basedir}/..</root.basedir>
<spring.version>3.2.5.RELEASE</spring.version>
<xoai.version>3.2.10</xoai.version>
<jtwig.version>2.0.1</jtwig.version>
</properties>
@@ -144,10 +143,6 @@
<artifactId>jtwig-spring</artifactId>
<version>${jtwig.version}</version>
<exclusions>
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -160,6 +155,10 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
@@ -185,7 +184,7 @@
<!-- Web API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
@@ -224,10 +223,12 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- Note: XOAI requires hamcrest both for testing and runtime -->
<!-- As our Parent POM sets this to 'test' scope, we must override it -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>

View File

@@ -8,9 +8,9 @@
http://www.dspace.org/license/
-->
<web-app id="XOAILynCode" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
<web-app id="DSpace-OAI" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>XOAI Data Provider</display-name>

View File

@@ -64,7 +64,7 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@@ -8,10 +8,9 @@
http://www.dspace.org/license/
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<web-app id="DSpace-RDF" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>RDF Data Provider</display-name>

View File

@@ -220,7 +220,6 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@@ -8,10 +8,9 @@
http://www.dspace.org/license/
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<web-app id="DSpace-RESTv6" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<filter>
<filter-name>dspace.request</filter-name>
@@ -109,4 +108,4 @@
</listener>
</web-app>
</web-app>

View File

@@ -108,7 +108,7 @@
<!-- for filters -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@@ -12,7 +12,9 @@ import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.services.ConfigurationService;
@@ -33,7 +35,7 @@ import org.springframework.beans.factory.annotation.Required;
public class EmailServiceImpl
extends Authenticator
implements EmailService, InitializedService {
private static final Logger logger = (Logger) LoggerFactory.getLogger(EmailServiceImpl.class);
private static final Logger logger = LoggerFactory.getLogger(EmailServiceImpl.class);
private Session session = null;
@@ -53,7 +55,7 @@ public class EmailServiceImpl
/**
* Provide a reference to the JavaMail session.
*
* @return the managed Session, or null if none could be created.
* @return the managed Session, or {@code null} if none could be created.
*/
@Override
public Session getSession() {
@@ -67,12 +69,16 @@ public class EmailServiceImpl
if (null == sessionName) {
sessionName = "Session";
}
String sessionUri = "java:comp/env/mail/" + sessionName;
logger.debug("Looking up Session as {}", sessionUri);
try {
InitialContext ctx = new InitialContext(null);
session = (Session) ctx.lookup("java:comp/env/mail/" + sessionName);
session = (Session) ctx.lookup(sessionUri);
} catch (NameNotFoundException | NoInitialContextException ex) {
// Not a problem -- build a new Session from configuration.
} catch (NamingException ex) {
logger.warn("Couldn't get an email session from environment: {}",
ex.getMessage());
logger.warn("Couldn't get an email session from environment: {}: {}",
ex.getClass().getName(), ex.getMessage());
}
if (null != session) {
@@ -106,8 +112,6 @@ public class EmailServiceImpl
props.put("mail.smtp.auth", "true");
session = Session.getInstance(props, this);
}
}
}

View File

@@ -249,7 +249,7 @@
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@@ -27,6 +27,7 @@ import org.dspace.content.BitstreamFormat;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Context;
import org.dspace.disseminate.service.CitationDocumentService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.EventService;
import org.dspace.usage.UsageEvent;
import org.springframework.beans.factory.annotation.Autowired;
@@ -70,6 +71,9 @@ public class BitstreamContentRestController {
@Autowired
private CitationDocumentService citationDocumentService;
@Autowired
private ConfigurationService configurationService;
@PreAuthorize("hasPermission(#uuid, 'BITSTREAM', 'READ')")
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD})
public void retrieve(@PathVariable UUID uuid, HttpServletResponse response,
@@ -104,6 +108,12 @@ public class BitstreamContentRestController {
.with(request)
.with(response);
//Determine if we need to send the file as a download or if the browser can open it inline
long dispositionThreshold = configurationService.getLongProperty("webui.content_disposition_threshold");
if (dispositionThreshold >= 0 && bitstreamTuple.getRight() > dispositionThreshold) {
sender.withDisposition(MultipartFileSender.CONTENT_DISPOSITION_ATTACHMENT);
}
if (sender.isNoRangeRequest() && isNotAnErrorResponse(response)) {
//We only log a download request when serving a request without Range header. This is because
//a browser always sends a regular request first to check for Range support.

View File

@@ -142,7 +142,8 @@ public class DiscoveryRestController implements InitializingBean {
}
//Get the Search results in JSON format
SearchResultsRest searchResultsRest = discoveryRestRepository
SearchResultsRest searchResultsRest = null;
searchResultsRest = discoveryRestRepository
.getSearchObjects(query, dsoType, dsoScope, configurationName, searchFilters, page);
//Convert the Search JSON results to paginated HAL resources

View File

@@ -10,18 +10,22 @@ package org.dspace.app.rest;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
@@ -46,6 +50,7 @@ import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.repository.LinkRestRepository;
import org.dspace.app.rest.utils.RestRepositoryUtils;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -60,6 +65,7 @@ import org.springframework.hateoas.Link;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.UriTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -426,17 +432,21 @@ public class RestResourceController implements InitializingBean {
}
/**
* Called in POST, multipart, upload a resource passed into "file" request parameter
* Called in POST, multipart, upload to a specific rest resource the file passed as "file" request parameter
*
* Note that the regular expression in the request mapping accept a number as identifier;
*
* @param request
* the http request
* @param apiCategory
* the api category
* @param model
* the rest model that identify the REST resource collection
* @param id
* @param extraField
* the id of the specific rest resource
* @param uploadfile
* @return
* the file to upload
* @return the created resource
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(method = RequestMethod.POST, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT, headers =
@@ -445,26 +455,28 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory,
@PathVariable String model,
@PathVariable Integer id,
@RequestParam(required = false, value =
"extraField") String extraField,
@RequestParam("file") MultipartFile
uploadfile)
throws HttpRequestMethodNotSupportedException {
return uploadInternal(request, apiCategory, model, id, extraField, uploadfile);
return uploadInternal(request, apiCategory, model, id, uploadfile);
}
/**
* Called in POST, multipart, upload a resource passed into "file" request parameter
* Called in POST, multipart, upload to a specific rest resource the file passed as "file" request parameter
*
* Note that the regular expression in the request mapping accept a UUID as identifier;
*
* @param request
* the http request
* @param apiCategory
* the api category
* @param model
* the rest model that identify the REST resource collection
* @param id
* @param extraField
* the id of the specific rest resource
* @param uploadfile
* @return
* the file to upload
* @return the created resource
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(method = RequestMethod.POST, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID, headers =
@@ -473,12 +485,10 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory,
@PathVariable String model,
@PathVariable UUID id,
@RequestParam(required = false, value =
"extraField") String extraField,
@RequestParam("file") MultipartFile
uploadfile)
throws HttpRequestMethodNotSupportedException {
return uploadInternal(request, apiCategory, model, id, extraField, uploadfile);
return uploadInternal(request, apiCategory, model, id, uploadfile);
}
/**
@@ -488,21 +498,19 @@ public class RestResourceController implements InitializingBean {
* @param apiCategory
* @param model
* @param id
* @param extraField
* @param uploadfile
* @return
*/
private <ID extends Serializable> ResponseEntity<ResourceSupport> uploadInternal(HttpServletRequest request,
String apiCategory, String model,
ID id,
String extraField,
MultipartFile uploadfile) {
checkModelPluralForm(apiCategory, model);
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
RestAddressableModel modelObject = null;
try {
modelObject = repository.upload(request, apiCategory, model, id, extraField, uploadfile);
modelObject = repository.upload(request, apiCategory, model, id, uploadfile);
} catch (Exception e) {
log.error(e.getMessage(), e);
return ControllerUtils.toEmptyResponse(HttpStatus.INTERNAL_SERVER_ERROR);
@@ -512,6 +520,46 @@ public class RestResourceController implements InitializingBean {
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
}
/**
* Upload a file against the collection resource endpoint. This is typically used for bulk creation of resources
* such for instance multiple workspaceitems from a CSV or bibliographic file
*
* @param request
* the http request
* @param apiCategory
* the api category
* @param model
* the rest model that identify the REST resource collection
* @param uploadfile
* the bulk file
* @return the list of generated resources
* @throws SQLException
* @throws FileNotFoundException
* @throws IOException
* @throws AuthorizeException
*/
@RequestMapping(method = { RequestMethod.POST }, headers = "content-type=multipart/form-data")
public <T extends RestAddressableModel> ResponseEntity<ResourceSupport> upload(HttpServletRequest request,
@PathVariable String apiCategory,
@PathVariable String model,
@RequestParam("file") MultipartFile
uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException {
checkModelPluralForm(apiCategory, model);
DSpaceRestRepository repository = utils.getResourceRepository(apiCategory, model);
Iterable<T> content = repository.upload(request, uploadfile);
List<DSpaceResource> resources = new ArrayList<>();
for (T modelObject : content) {
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
resources.add(result);
}
return ControllerUtils.toResponseEntity(HttpStatus.OK, null, Resources.wrap(resources));
}
/**
* PATCH method, using operation on the resources following (JSON) Patch notation (https://tools.ietf
* .org/html/rfc6902)
@@ -573,7 +621,6 @@ public class RestResourceController implements InitializingBean {
String model, ID id,
JsonNode jsonNode)
throws HttpRequestMethodNotSupportedException {
checkModelPluralForm(apiCategory, model);
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
RestAddressableModel modelObject = null;
@@ -736,8 +783,9 @@ public class RestResourceController implements InitializingBean {
EmbeddedPage ep = (EmbeddedPage) resource.getEmbeddedResources().get(rel);
List<? extends RestAddressableModel> fullList = ep.getFullList();
if (fullList == null || fullList.size() == 0) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
PageImpl<RestAddressableModel> pageResult = new PageImpl(fullList, page, 0);
result = assembler.toResource(pageResult);
return result;
}
int start = page.getOffset();
int end = (start + page.getPageSize()) > fullList.size() ? fullList.size() : (start + page.getPageSize());
@@ -759,7 +807,6 @@ public class RestResourceController implements InitializingBean {
if (resource.getEmbeddedResources().get(rel) == null) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
return (ResourceSupport) resource.getEmbeddedResources().get(rel);
}

View File

@@ -17,8 +17,10 @@ import org.dspace.app.rest.model.ScopeEnum;
import org.dspace.app.rest.model.SubmissionFormFieldRest;
import org.dspace.app.rest.model.SubmissionFormInputTypeRest;
import org.dspace.app.rest.model.SubmissionFormRest;
import org.dspace.app.rest.model.SubmissionFormRowRest;
import org.dspace.app.rest.model.SubmissionVisibilityRest;
import org.dspace.app.rest.model.VisibilityEnum;
import org.dspace.app.rest.repository.SubmissionFormRestRepository;
import org.dspace.app.rest.utils.AuthorityUtils;
import org.dspace.app.util.DCInput;
import org.dspace.app.util.DCInputSet;
@@ -44,30 +46,41 @@ public class SubmissionFormConverter extends DSpaceConverter<DCInputSet, Submiss
@Autowired
private AuthorityUtils authorityUtils;
@Autowired
private SubmissionFormRestRepository submissionFormRestRepository;
@Override
public SubmissionFormRest fromModel(DCInputSet obj) {
SubmissionFormRest sd = new SubmissionFormRest();
sd.setName(obj.getFormName());
DCInput[] step = obj.getFields();
List<SubmissionFormFieldRest> fields = getPage(step);
sd.setFields(fields);
DCInput[][] step = obj.getFields();
List<SubmissionFormRowRest> rows = getPage(step, obj.getFormName());
sd.setRows(rows);
return sd;
}
private List<SubmissionFormFieldRest> getPage(DCInput[] page) {
List<SubmissionFormFieldRest> fields = new LinkedList<SubmissionFormFieldRest>();
for (DCInput dcinput : page) {
fields.add(getField(dcinput));
private List<SubmissionFormRowRest> getPage(DCInput[][] page, String formName) {
List<SubmissionFormRowRest> rows = new LinkedList<SubmissionFormRowRest>();
for (DCInput[] row : page) {
List<SubmissionFormFieldRest> fields = new LinkedList<SubmissionFormFieldRest>();
SubmissionFormRowRest rowRest = new SubmissionFormRowRest();
rowRest.setFields(fields);
rows.add(rowRest);
for (DCInput dcinput : row) {
fields.add(getField(dcinput, formName));
}
}
return fields;
return rows;
}
private SubmissionFormFieldRest getField(DCInput dcinput) {
private SubmissionFormFieldRest getField(DCInput dcinput, String formName) {
SubmissionFormFieldRest inputField = new SubmissionFormFieldRest();
List<SelectableMetadata> selectableMetadata = new ArrayList<SelectableMetadata>();
inputField.setLabel(dcinput.getLabel());
inputField.setHints(dcinput.getHints());
inputField.setStyle(dcinput.getStyle());
inputField.setMandatoryMessage(dcinput.getWarning());
inputField.setMandatory(dcinput.isRequired());
inputField.setScope(ScopeEnum.fromString(dcinput.getScope()));
@@ -99,17 +112,16 @@ public class SubmissionFormConverter extends DSpaceConverter<DCInputSet, Submiss
SelectableMetadata selMd = new SelectableMetadata();
if (authorityUtils.isChoice(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier())) {
inputRest.setType(
getPresentation(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier(), inputType));
selMd.setAuthority(getAuthorityName(dcinput.getSchema(), dcinput.getElement(),
dcinput.getQualifier(), dcinput.getPairsType(),
dcinput.getVocabulary()));
getPresentation(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier(), inputType));
selMd.setAuthority(getAuthorityName(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier(),
dcinput.getPairsType(), dcinput.getVocabulary()));
selMd.setClosed(
authorityUtils.isClosed(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier()));
authorityUtils.isClosed(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier()));
} else {
inputRest.setType(inputType);
}
selMd.setMetadata(org.dspace.core.Utils
.standardize(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier(), "."));
.standardize(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier(), "."));
selectableMetadata.add(selMd);
} else {
@@ -119,11 +131,10 @@ public class SubmissionFormConverter extends DSpaceConverter<DCInputSet, Submiss
SelectableMetadata selMd = new SelectableMetadata();
selMd.setLabel((String) pairs.get(idx));
selMd.setMetadata(org.dspace.core.Utils
.standardize(dcinput.getSchema(), dcinput.getElement(), pairs.get(idx + 1), "."));
.standardize(dcinput.getSchema(), dcinput.getElement(), pairs.get(idx + 1), "."));
if (authorityUtils.isChoice(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier())) {
selMd.setAuthority(getAuthorityName(dcinput.getSchema(), dcinput.getElement(),
pairs.get(idx + 1), dcinput.getPairsType(),
dcinput.getVocabulary()));
selMd.setAuthority(getAuthorityName(dcinput.getSchema(), dcinput.getElement(), pairs.get(idx + 1),
dcinput.getPairsType(), dcinput.getVocabulary()));
selMd.setClosed(
authorityUtils.isClosed(dcinput.getSchema(), dcinput.getElement(), dcinput.getQualifier()));
}
@@ -160,12 +171,11 @@ public class SubmissionFormConverter extends DSpaceConverter<DCInputSet, Submiss
} else if (StringUtils.isNotBlank(vocabularyName)) {
return vocabularyName;
}
return authorityUtils.getAuthorityName(schema, element,
qualifier);
return authorityUtils.getAuthorityName(schema, element, qualifier);
}
@Override
public DCInputSet toModel(SubmissionFormRest obj) {
throw new NotImplementedException();
}
}
}

View File

@@ -24,7 +24,6 @@ import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.eperson.EPerson;
import org.dspace.submit.AbstractProcessingStep;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -86,6 +85,10 @@ public class WorkspaceItemConverter
// info
if (collection != null) {
// we set the status to true as we will discover validation error later in this block
// we could eventually leave the status to empty if we don't have collection information, this could be
// eventually the case when projection support will be included
witem.setStatus(true);
SubmissionDefinitionRest def = submissionDefinitionConverter
.convert(submissionConfigReader.getSubmissionConfigByCollection(collection.getHandle()));
witem.setSubmissionDefinition(def);
@@ -103,22 +106,21 @@ public class WorkspaceItemConverter
Object stepInstance = stepClass.newInstance();
if (stepInstance instanceof AbstractProcessingStep) {
if (stepInstance instanceof AbstractRestProcessingStep) {
// load the interface for this step
AbstractRestProcessingStep stepProcessing = (AbstractRestProcessingStep) stepClass
.newInstance();
AbstractRestProcessingStep stepProcessing =
(AbstractRestProcessingStep) stepClass.newInstance();
for (ErrorRest error : stepProcessing.validate(submissionService, obj, stepConfig)) {
addError(witem.getErrors(), error);
witem.setStatus(false);
}
witem.getSections()
.put(sections.getId(), stepProcessing.getData(submissionService, obj, stepConfig));
.put(sections.getId(), stepProcessing.getData(submissionService, obj, stepConfig));
} else {
throw new Exception("The submission step class specified by '"
+ stepConfig.getProcessingClassName()
+ "' does not extend the class org.dspace.app.rest.submit" +
".AbstractRestProcessingStep!"
+ " Therefore it cannot be used by the Configurable Submission as the" +
" <processing-class>!");
log.warn("The submission step class specified by '" + stepConfig.getProcessingClassName() +
"' does not extend the class org.dspace.app.rest.submit.AbstractRestProcessingStep!" +
" Therefore it cannot be used by the Configurable Submission as the " +
"<processing-class>!");
}
} catch (Exception e) {

View File

@@ -38,6 +38,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep
*/
@ControllerAdvice
public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionHandler {
@Autowired
private RestAuthenticationService restAuthenticationService;
@@ -51,6 +52,12 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
}
}
@ExceptionHandler(IllegalArgumentException.class)
protected void handleIllegalArgumentException(HttpServletRequest request, HttpServletResponse response,
Exception ex) throws IOException {
sendErrorResponse(request, response, ex, ex.getMessage(), HttpServletResponse.SC_BAD_REQUEST);
}
@ExceptionHandler(SQLException.class)
protected void handleSQLException(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws IOException {

View File

@@ -28,6 +28,13 @@ public abstract class BaseObjectRest<T extends Serializable> implements Identifi
@JsonInclude(Include.NON_EMPTY)
private List<ErrorRest> errors;
private Boolean status;
@JsonInclude(Include.NON_NULL)
public Boolean isStatus() {
return status;
}
@Override
public T getId() {
return id;
@@ -48,4 +55,7 @@ public abstract class BaseObjectRest<T extends Serializable> implements Identifi
this.errors = errors;
}
public void setStatus(Boolean status) {
this.status = status;
}
}

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest.model;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The Community REST Resource
@@ -61,6 +62,7 @@ public class CommunityRest extends DSpaceObjectRest {
}
@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getType() {
return NAME;
}

View File

@@ -21,6 +21,11 @@ public class ErrorRest {
private List<String> paths;
/**
* The error message as i18key
*
* @return The message as i18key
*/
public String getMessage() {
return message;
}
@@ -29,6 +34,13 @@ public class ErrorRest {
this.message = message;
}
/**
* The json paths where the error message apply. They can be as detailed as a specific value in a multivalues
* attributes (i.e. sections.traditionalpageone['dc.contributor.author'][1] to identify the second author - 0 based)
* or generic to apply to a whole section (sections.license)
*
* @return
*/
public List<String> getPaths() {
if (this.paths == null) {
this.paths = new ArrayList<String>();

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest.model;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.dspace.app.rest.RestResourceController;
/**
@@ -17,6 +18,7 @@ import org.dspace.app.rest.RestResourceController;
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GroupRest extends DSpaceObjectRest {
public static final String NAME = "group";
@@ -69,4 +71,4 @@ public class GroupRest extends DSpaceObjectRest {
public Class getController() {
return RestResourceController.class;
}
}
}

View File

@@ -10,7 +10,7 @@ package org.dspace.app.rest.model;
/**
* The possible restriction options for the scope attributes in the
* SubmissionPanel resource and InputFormPage resource
* SubmissionPanel resource and SubmissionForm's fields
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/

View File

@@ -17,74 +17,194 @@ import org.dspace.submit.model.LanguageFormField;
import org.dspace.submit.model.SelectableMetadata;
/**
* The InputFormField REST Resource. It is not addressable directly, only used
* The SubmissionFormField REST Resource. It is not addressable directly, only used
* as inline object in the InputForm resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@JsonInclude(value = Include.NON_NULL)
public class SubmissionFormFieldRest {
/**
* The SubmissionFormInputType for this field
*/
private SubmissionFormInputTypeRest input;
/**
* The main scope of the field
*/
private ScopeEnum scope;
/**
* The visibility restriction for the field
*/
private SubmissionVisibilityRest visibility;
/**
* The label of the field
*/
private String label;
/**
* <code>true</code> if the field is required
*/
private boolean mandatory;
/**
* <code>true</code> if the field allows multiple value
*/
private boolean repeatable;
/**
* The message to return if the information is missing
*/
private String mandatoryMessage;
/**
* A text to help field input
*/
private String hints;
/**
* Extra information to be used by the UI to customize the presentation of the field. The format is dependent from
* the UI implementation, the default Angular UI expects whitespace separated CSS class to add to the field
*/
private String style;
/**
* The list of metadata, often a single element, to offer for the storage of the information. This map the DSpace <
* 7 concepts of qualdrop
*/
private List<SelectableMetadata> selectableMetadata;
/**
* The list of language that can be used to fill the field
*/
private List<LanguageFormField> languageCodes;
/**
* Getter for {@link #selectableMetadata}
*
* @return {@link #selectableMetadata}
*/
public List<SelectableMetadata> getSelectableMetadata() {
return selectableMetadata;
}
/**
* Setter for {@link #selectableMetadata}
*
*/
public void setSelectableMetadata(List<SelectableMetadata> selectableMetadata) {
this.selectableMetadata = selectableMetadata;
}
/**
* Getter for {@link #label}
*
* @return {@link #label}
*/
public String getLabel() {
return label;
}
/**
* Setter for {@link #label}
*
*/
public void setLabel(String label) {
this.label = label;
}
/**
* Getter for {@link #mandatory}
*
* @return {@link #mandatory}
*/
public boolean isMandatory() {
return mandatory;
}
/**
* Setter for {@link #mandatory}
*
*/
public void setMandatory(boolean mandatory) {
this.mandatory = mandatory;
}
/**
* Getter for {@link #repeatable}
*
* @return {@link #repeatable}
*/
public boolean isRepeatable() {
return repeatable;
}
/**
* Setter for {@link #repeatable}
*
*/
public void setRepeatable(boolean repeatable) {
this.repeatable = repeatable;
}
/**
* Getter for {@link #mandatoryMessage}
*
* @return {@link #mandatoryMessage}
*/
public String getMandatoryMessage() {
return mandatoryMessage;
}
/**
* Setter for {@link #mandatoryMessage}
*
*/
public void setMandatoryMessage(String mandatoryMessage) {
this.mandatoryMessage = mandatoryMessage;
}
/**
* Getter for {@link #hints}
*
* @return {@link #hints}
*/
public String getHints() {
return hints;
}
/**
* Setter for {@link #hints}
*
*/
public void setHints(String hints) {
this.hints = hints;
}
/**
* Getter for {@link #style}
*
* @return {@link #style}
*/
public String getStyle() {
return style;
}
/**
* Setter for {@link #style}
*
*/
public void setStyle(String style) {
this.style = style;
}
/**
* Getter for {@link #languageCodes}
*
* @return {@link #languageCodes}
*/
public List<LanguageFormField> getLanguageCodes() {
if (languageCodes == null) {
languageCodes = new ArrayList<LanguageFormField>();
@@ -92,22 +212,44 @@ public class SubmissionFormFieldRest {
return languageCodes;
}
/**
* Setter for {@link #languageCodes}
*
*/
public void setLanguageCodes(List<LanguageFormField> languageCodes) {
this.languageCodes = languageCodes;
}
/**
* Getter for {@link #input}
*
* @return {@link #input}
*/
public SubmissionFormInputTypeRest getInput() {
return input;
}
/**
* Setter for {@link #input}
*
*/
public void setInput(SubmissionFormInputTypeRest input) {
this.input = input;
}
/**
* Getter for {@link #scope}
*
* @return {@link #selectableMetadata}
*/
public ScopeEnum getScope() {
return scope;
}
/**
* Setter for {@link #scope}
*
*/
public void setScope(ScopeEnum scope) {
this.scope = scope;
}
@@ -121,4 +263,5 @@ public class SubmissionFormFieldRest {
this.visibility = visibility;
}
}
}

View File

@@ -12,7 +12,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* The InputFormField REST Resource. It is not addressable directly, only used
* The SubmissionFormInputType REST Resource. It is not addressable directly, only used
* as inline object in the InputForm resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)

View File

@@ -12,7 +12,7 @@ import java.util.List;
import org.dspace.app.rest.RestResourceController;
/**
* The Input Form REST Resource
* The Submission Form REST Resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@@ -21,19 +21,37 @@ public class SubmissionFormRest extends BaseObjectRest<String> {
public static final String NAME_LINK_ON_PANEL = RestAddressableModel.CONFIGURATION;
public static final String CATEGORY = RestAddressableModel.CONFIGURATION;
/**
* An unique name identifying the submission form
*/
private String name;
private List<SubmissionFormFieldRest> fields;
/**
* The list of row in the submission form
*/
private List<SubmissionFormRowRest> rows;
@Override
/**
* The id of the submission form is its name
*/
public String getId() {
return name;
}
/**
* Setter for {@link #name}
*
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter for {@link #name}
*
* @return {@link #name}
*/
public String getName() {
return name;
}
@@ -53,11 +71,20 @@ public class SubmissionFormRest extends BaseObjectRest<String> {
return CATEGORY;
}
public List<SubmissionFormFieldRest> getFields() {
return fields;
/**
* Getter for {@link #rows}
*
* @return {@link #rows}
*/
public List<SubmissionFormRowRest> getRows() {
return rows;
}
public void setFields(List<SubmissionFormFieldRest> fields) {
this.fields = fields;
/**
* Setter for {@link #rows}
*
*/
public void setRows(List<SubmissionFormRowRest> rows) {
this.rows = rows;
}
}
}

View File

@@ -0,0 +1,42 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.model;
import java.util.List;
/**
* The InputFormRow REST Resource. It is not addressable directly, only used
* as inline object in the InputForm resource
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public class SubmissionFormRowRest {
/**
* The list of fields in the row
*/
private List<SubmissionFormFieldRest> fields;
/**
* Getter for {@link #fields}
*
* @return {@link #fields}
*/
public List<SubmissionFormFieldRest> getFields() {
return fields;
}
/**
* Setter for {@link #fields}
*
*/
public void setFields(List<SubmissionFormFieldRest> fields) {
this.fields = fields;
}
}

View File

@@ -12,13 +12,19 @@ import java.util.Objects;
/**
* The SubmissionVisibility REST Resource. It is not addressable directly, only
* used as inline object in the SubmissionPanel resource and InputFormPage
* resource
* used as inline object in the SubmissionPanel resource and SubmissionForm's fields
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public class SubmissionVisibilityRest {
/**
* The visibility to apply within the main scope
*/
private VisibilityEnum main;
/**
* The visibility to apply outside the main scope
*/
private VisibilityEnum other;
public SubmissionVisibilityRest(VisibilityEnum main, VisibilityEnum other) {

View File

@@ -10,7 +10,7 @@ package org.dspace.app.rest.model;
/**
* The possible restriction options for the visibility attributes in the
* SubmissionPanel resource and InputFormPage resource
* SubmissionPanel resource and SubmissionForm's fields
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/

View File

@@ -129,7 +129,9 @@ public abstract class DSpaceResource<T extends RestAddressableModel> extends HAL
page.map(resourceRepository::wrapResource),
linkedRMList, name);
} else {
wrapObject = null;
PageImpl<RestAddressableModel> page = new PageImpl(linkedRMList);
wrapObject = new EmbeddedPage(linkToSubResource.getHref(), page,
linkedRMList, name);
}
}
}

View File

@@ -7,16 +7,24 @@
*/
package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.converter.CommunityConverter;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.MetadataEntryRest;
import org.dspace.app.rest.model.hateoas.CommunityResource;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
@@ -29,7 +37,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible to manage Item Rest object
* This is the repository responsible to manage Community Rest object
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@@ -47,6 +55,38 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
System.out.println("Repository initialized by Spring");
}
@Override
@PreAuthorize("hasAuthority('ADMIN')")
protected CommunityRest createAndReturn(Context context) throws AuthorizeException {
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ObjectMapper mapper = new ObjectMapper();
CommunityRest communityRest = null;
try {
ServletInputStream input = req.getInputStream();
communityRest = mapper.readValue(input, CommunityRest.class);
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
}
Community community = null;
try {
community = cs.create(null, context);
cs.update(context, community);
if (communityRest.getMetadata() != null) {
for (MetadataEntryRest mer : communityRest.getMetadata()) {
String[] metadatakey = mer.getKey().split("\\.");
cs.addMetadata(context, community, metadatakey[0], metadatakey[1],
metadatakey.length == 3 ? metadatakey[2] : null, mer.getLanguage(), mer.getValue());
}
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
return converter.convert(community);
}
@Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'READ')")
public CommunityRest findOne(Context context, UUID id) {

View File

@@ -7,8 +7,11 @@
*/
package org.dspace.app.rest.repository;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
@@ -19,7 +22,6 @@ import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.model.step.UploadStatusResponse;
import org.dspace.app.util.DCInputsReaderException;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
@@ -66,48 +68,94 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
}
}
/**
* Method to implement to support full update of a REST object. This is usually required by a PUT request.
*
* @param context
* the dspace context
* @param entity
* the REST object to update
* @return the new state of the REST object after persistence
* @throws AuthorizeException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*/
protected <S extends T> S save(Context context, S entity) throws AuthorizeException,
RepositoryMethodNotImplementedException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
@Override
/**
* Method to implement to support bulk update of a REST objects via a PUT request
*/
public <S extends T> Iterable<S> save(Iterable<S> entities) {
// TODO Auto-generated method stub
return null;
}
@Override
/**
* Return a specific REST object
*
* @return the REST object identified by its ID
*/
public T findOne(ID id) {
Context context = obtainContext();
return thisRepository.findOne(context, id);
}
/**
* Method to implement to support retrieval of a specific REST object instance
*
* @param context
* the dspace context
* @param id
* the rest object id
* @return the REST object identified by its ID
*/
public abstract T findOne(Context context, ID id);
@Override
/**
* Return true if an object exist for the specified ID. The default implementation is inefficient as it retrieves
* the actual object to state that it exists. This could lead to retrieve and inizialize lot of linked objects
*/
public boolean exists(ID id) {
// TODO Auto-generated method stub
return false;
return findOne(id) != null;
}
@Override
public Iterable<T> findAll() {
/**
* This method cannot be implemented we required all the find method to be paginated
*/
public final Iterable<T> findAll() {
throw new RuntimeException("findAll MUST be paginated");
}
@Override
/**
* This method could be implemented to support bulk retrieval of specific object by their IDs. Unfortunately, this
* method doesn't allow pagination and it could be misused to retrieve thousand objects at once
*/
public Iterable<T> findAll(Iterable<ID> ids) {
throw new RuntimeException("findAll MUST be paginated");
}
@Override
/**
* This method return the number of object instances of the type managed by the repository class available in the
* system
*/
public long count() {
// TODO Auto-generated method stub
// FIXME DS-4038
return 0;
}
@Override
/**
* Delete the object identified by its ID
*/
public void delete(ID id) {
Context context = obtainContext();
try {
@@ -120,44 +168,96 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
}
}
/**
* Method to implement to support delete of a single object instance
*
* @param context
* the dspace context
* @param id
* the id of the rest object to delete
* @throws AuthorizeException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*/
protected void delete(Context context, ID id) throws AuthorizeException, RepositoryMethodNotImplementedException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
@Override
/**
* Method to implement to allow delete of a specific entity instance
*/
public void delete(T entity) {
// TODO Auto-generated method stub
}
@Override
/**
* Method to implement to support bulk delete of multiple entity instances
*/
public void delete(Iterable<? extends T> entities) {
// TODO Auto-generated method stub
}
@Override
/**
* Method to implement to support bulk delete of ALL entity instances
*/
public void deleteAll() {
// TODO Auto-generated method stub
}
@Override
public Iterable<T> findAll(Sort sort) {
/**
* This method cannot be implemented we required all the find method to be paginated
*/
public final Iterable<T> findAll(Sort sort) {
throw new RuntimeException("findAll MUST be paginated");
}
@Override
/**
* Provide access to the manage entity instances in a paginated way
*/
public Page<T> findAll(Pageable pageable) {
Context context = obtainContext();
return thisRepository.findAll(context, pageable);
}
/**
* Method to implement to support scroll of entity instances from the collection resource endpoin
*
* @param context
* the dspace context
* @param pageable
* object embedding the requested pagination info
* @return
*/
public abstract Page<T> findAll(Context context, Pageable pageable);
/**
* The REST model supported by the repository
*/
public abstract Class<T> getDomainClass();
/**
* Wrap the REST model in a REST HAL Resource
*
* @param model
* the rest model instance
* @param rels
* the HAL links
* @return the REST Resource
*/
public abstract DSpaceResource<T> wrapResource(T model, String... rels);
/**
* Create and return a new instance. Data are usually retrieved from the thread bound http request
*
* @return the created REST object
*/
public T createAndReturn() {
Context context = null;
try {
@@ -172,15 +272,58 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
}
}
protected T createAndReturn(Context context) throws AuthorizeException, RepositoryMethodNotImplementedException {
/**
* Method to implement to support the creation of a new instance. Usually require to retrieve the http request from
* the thread bound attribute
*
* @param context
* the dspace context
* @return the created REST object
* @throws AuthorizeException
* @throws SQLException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*/
protected T createAndReturn(Context context)
throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
public <U extends UploadStatusResponse> U upload(HttpServletRequest request, String apiCategory, String model,
ID id, String extraField, MultipartFile file) throws Exception {
/**
* Method to implement to attach/upload a file to a specific REST object
*
* @param request
* the http request
* @param apiCategory
* @param model
* @param id
* the ID of the target REST object
* @param file
* the uploaded file
* @return the new state of the REST object
* @throws Exception
*/
public T upload(HttpServletRequest request, String apiCategory, String model,
ID id, MultipartFile file) throws Exception {
throw new RuntimeException("No implementation found; Method not allowed!");
}
/**
* Apply a partial update to the REST object via JSON Patch
*
* @param request
* the http request
* @param apiCategory
* @param model
* @param id
* the ID of the target REST object
* @param patch
* the JSON Patch (https://tools.ietf.org/html/rfc6902) operation
* @return
* @throws HttpRequestMethodNotSupportedException
* @throws UnprocessableEntityException
* @throws PatchBadRequestException
*/
public T patch(HttpServletRequest request, String apiCategory, String model, ID id, Patch patch)
throws HttpRequestMethodNotSupportedException, UnprocessableEntityException, PatchBadRequestException {
Context context = obtainContext();
@@ -195,10 +338,73 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
return findOne(id);
}
/**
* Method to implement to allow partial update of the REST object via JSON Patch
*
* @param request
* the http request
* @param apiCategory
* @param model
* @param id
* the ID of the target REST object
* @param patch
* the JSON Patch (https://tools.ietf.org/html/rfc6902) operation
* @return the full new state of the REST object after patching
* @throws HttpRequestMethodNotSupportedException
* @throws UnprocessableEntityException
* @throws PatchBadRequestException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*
* @throws SQLException
* @throws AuthorizeException
* @throws DCInputsReaderException
*/
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, ID id,
Patch patch)
throws RepositoryMethodNotImplementedException, SQLException, AuthorizeException, DCInputsReaderException {
throw new RepositoryMethodNotImplementedException(apiCategory, model);
}
/**
* Bulk create object instances from an uploaded file
*
* @param request
* the http request
* @param uploadfile
* the file to process
* @return the created objects
* @throws SQLException
* @throws FileNotFoundException
* @throws IOException
* @throws AuthorizeException
*/
public Iterable<T> upload(HttpServletRequest request, MultipartFile uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException {
Context context = obtainContext();
Iterable<T> entity = upload(context, request, uploadfile);
context.commit();
return entity;
}
/**
* Method to implement to support bulk creation of objects from a file
*
* @param request
* the http request
* @param uploadfile
* the file to process
* @return the created objects
* @throws SQLException
* @throws FileNotFoundException
* @throws IOException
* @throws AuthorizeException
* @throws RepositoryMethodNotImplementedException
*/
protected Iterable<T> upload(Context context, HttpServletRequest request,
MultipartFile uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
}

View File

@@ -9,6 +9,8 @@ package org.dspace.app.rest.repository;
import java.util.List;
import javax.ws.rs.BadRequestException;
import org.apache.log4j.Logger;
import org.dspace.app.rest.converter.DiscoverConfigurationConverter;
import org.dspace.app.rest.converter.DiscoverFacetConfigurationConverter;
@@ -91,7 +93,7 @@ public class DiscoveryRestRepository extends AbstractDSpaceRestRepository {
public SearchResultsRest getSearchObjects(final String query, final String dsoType, final String dsoScope,
final String configurationName,
final List<SearchFilter> searchFilters, final Pageable page)
throws InvalidRequestException {
throws InvalidRequestException, BadRequestException {
Context context = obtainContext();
DSpaceObject scopeObject = scopeResolver.resolveScope(context, dsoScope);
@@ -108,6 +110,7 @@ public class DiscoveryRestRepository extends AbstractDSpaceRestRepository {
} catch (SearchServiceException e) {
log.error("Error while searching with Discovery", e);
throw new IllegalArgumentException("Error while searching with Discovery: " + e.getMessage());
}
return discoverResultConverter

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
@@ -19,11 +20,15 @@ import org.apache.commons.lang.StringUtils;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.converter.EPersonConverter;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.RESTAuthorizationException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.MetadataEntryRest;
import org.dspace.app.rest.model.hateoas.EPersonResource;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.repository.patch.EPersonPatch;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
@@ -34,6 +39,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@@ -54,6 +60,9 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
@Autowired
EPersonConverter converter;
@Autowired
EPersonPatch epersonPatch;
@Override
protected EPersonRest createAndReturn(Context context)
throws AuthorizeException {
@@ -179,6 +188,56 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
return converter.fromModel(eperson);
}
@Override
@PreAuthorize("hasAuthority('ADMIN')")
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
Patch patch)
throws UnprocessableEntityException, PatchBadRequestException, AuthorizeException,
ResourceNotFoundException {
try {
EPerson eperson = es.find(context, uuid);
if (eperson == null) {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + uuid + " not found");
}
List<Operation> operations = patch.getOperations();
EPersonRest ePersonRest = findOne(context, uuid);
EPersonRest patchedModel = (EPersonRest) epersonPatch.patch(ePersonRest, operations);
updatePatchedValues(context, patchedModel, eperson);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Applies changes in the rest model.
* @param context
* @param ePersonRest the updated eperson rest
* @param ePerson the eperson content object
* @throws SQLException
* @throws AuthorizeException
*/
private void updatePatchedValues(Context context, EPersonRest ePersonRest, EPerson ePerson)
throws SQLException, AuthorizeException {
if (ePersonRest.getPassword() != null) {
es.setPassword(ePerson, ePersonRest.getPassword());
}
if (ePersonRest.isRequireCertificate() != ePerson.getRequireCertificate()) {
ePerson.setRequireCertificate(ePersonRest.isRequireCertificate());
}
if (ePersonRest.isCanLogIn() != ePerson.canLogIn()) {
ePerson.setCanLogIn(ePersonRest.isCanLogIn());
}
if (!Objects.equals(ePersonRest.getNetid(), ePerson.getNetid())) {
ePerson.setNetid(ePersonRest.getNetid());
}
es.update(context, ePerson);
}
@Override
protected void delete(Context context, UUID id) throws AuthorizeException {
EPerson eperson = null;

View File

@@ -7,16 +7,23 @@
*/
package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.converter.GroupConverter;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.GroupRest;
import org.dspace.app.rest.model.MetadataEntryRest;
import org.dspace.app.rest.model.hateoas.GroupResource;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.GroupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -33,11 +40,46 @@ import org.springframework.stereotype.Component;
@Component(GroupRest.CATEGORY + "." + GroupRest.NAME)
public class GroupRestRepository extends DSpaceRestRepository<GroupRest, UUID> {
GroupService gs = EPersonServiceFactory.getInstance().getGroupService();
@Autowired
GroupService gs;
@Autowired
GroupConverter converter;
@Override
@PreAuthorize("hasAuthority('ADMIN')")
protected GroupRest createAndReturn(Context context)
throws AuthorizeException, RepositoryMethodNotImplementedException {
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ObjectMapper mapper = new ObjectMapper();
GroupRest groupRest = null;
try {
groupRest = mapper.readValue(req.getInputStream(), GroupRest.class);
} catch (IOException excIO) {
throw new UnprocessableEntityException("error parsing the body ..." + excIO.getMessage());
}
Group group = null;
try {
group = gs.create(context);
gs.setName(group, groupRest.getName());
gs.update(context, group);
if (groupRest.getMetadata() != null) {
for (MetadataEntryRest mer: groupRest.getMetadata()) {
String[] metadatakey = mer.getKey().split("\\.");
gs.addMetadata(context, group, metadatakey[0], metadatakey[1],
metadatakey.length == 3 ? metadatakey[2] : null, mer.getLanguage(), mer.getValue());
}
}
} catch (SQLException excSQL) {
throw new RuntimeException(excSQL.getMessage(), excSQL);
}
return converter.convert(group);
}
@Override
@PreAuthorize("hasPermission(#id, 'GROUP', 'READ')")
public GroupRest findOne(Context context, UUID id) {
@@ -78,4 +120,4 @@ public class GroupRestRepository extends DSpaceRestRepository<GroupRest, UUID> {
return new GroupResource(eperson, utils, rels);
}
}
}

View File

@@ -21,6 +21,7 @@ import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.ItemResource;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.repository.patch.ItemPatch;
import org.dspace.authorize.AuthorizeException;
@@ -52,9 +53,6 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
@Autowired
ItemConverter converter;
/**
* Proposed helper class for Item patches.
*/
@Autowired
ItemPatch itemPatch;
@@ -99,16 +97,51 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
}
@Override
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid, Patch
patch)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException,
ResourceNotFoundException {
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
Patch patch)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException,
ResourceNotFoundException {
ItemRest restModel = findOne(context, uuid);
if (restModel == null) {
Item item = is.find(context, uuid);
if (item == null) {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + uuid + " not found");
}
itemPatch.patch(restModel, context, patch);
List<Operation> operations = patch.getOperations();
ItemRest itemRest = findOne(uuid);
ItemRest patchedModel = (ItemRest) itemPatch.patch(itemRest, operations);
updatePatchedValues(context, patchedModel, item);
}
/**
* Persists changes to the rest model.
* @param context
* @param itemRest the updated item rest resource
* @param item the item content object
* @throws SQLException
* @throws AuthorizeException
*/
private void updatePatchedValues(Context context, ItemRest itemRest, Item item)
throws SQLException, AuthorizeException {
try {
if (itemRest.getWithdrawn() != item.isWithdrawn()) {
if (itemRest.getWithdrawn()) {
is.withdraw(context, item);
} else {
is.reinstate(context, item);
}
}
if (itemRest.getDiscoverable() != item.isDiscoverable()) {
item.setDiscoverable(itemRest.getDiscoverable());
is.update(context, item);
}
} catch (SQLException | AuthorizeException e) {
e.printStackTrace();
throw e;
}
}
@Override

View File

@@ -7,51 +7,65 @@
*/
package org.dspace.app.rest.repository;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import gr.ekt.bte.core.TransformationEngine;
import gr.ekt.bte.core.TransformationSpec;
import gr.ekt.bte.exceptions.BadTransformationSpec;
import gr.ekt.bte.exceptions.MalformedSourceException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.converter.WorkspaceItemConverter;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.model.WorkspaceItemRest;
import org.dspace.app.rest.model.hateoas.WorkspaceItemResource;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.model.step.UploadBitstreamRest;
import org.dspace.app.rest.submit.AbstractRestProcessingStep;
import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.rest.submit.UploadableStep;
import org.dspace.app.rest.utils.Utils;
import org.dspace.app.util.SubmissionConfig;
import org.dspace.app.util.SubmissionConfigReader;
import org.dspace.app.util.SubmissionConfigReaderException;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.content.Collection;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
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.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.EPersonServiceImpl;
import org.dspace.event.Event;
import org.dspace.services.ConfigurationService;
import org.dspace.submit.AbstractProcessingStep;
import org.dspace.submit.lookup.DSpaceWorkspaceItemOutputGenerator;
import org.dspace.submit.lookup.MultipleSubmissionLookupDataLoader;
import org.dspace.submit.lookup.SubmissionItemDataLoader;
import org.dspace.submit.lookup.SubmissionLookupOutputGenerator;
import org.dspace.submit.lookup.SubmissionLookupService;
import org.dspace.submit.util.ItemSubmissionLookupDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.json.patch.PatchException;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -60,7 +74,6 @@ import org.springframework.web.multipart.MultipartFile;
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@Component(WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME)
public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceItemRest, Integer> {
@@ -87,6 +100,12 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
@Autowired
EPersonServiceImpl epersonService;
@Autowired
SubmissionLookupService submissionLookupService;
@Autowired
CollectionService collectionService;
private SubmissionConfigReader submissionConfigReader;
public WorkspaceItemRestRepository() throws SubmissionConfigReaderException {
@@ -132,8 +151,8 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
try {
Context context = obtainContext();
EPerson ep = epersonService.find(context, submitterID);
witems = wis.findByEPerson(context, ep);
total = witems.size();
witems = wis.findByEPerson(context, ep, pageable.getPageSize(), pageable.getOffset());
total = wis.countByEPerson(context, ep);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
@@ -142,7 +161,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
}
@Override
protected WorkspaceItemRest createAndReturn(Context context) {
protected WorkspaceItemRest createAndReturn(Context context) throws SQLException, AuthorizeException {
WorkspaceItem source = submissionService.createWorkspaceItem(context, getRequestService().getCurrentRequest());
return converter.convert(source);
}
@@ -170,7 +189,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
// load the JSPStep interface for this step
AbstractProcessingStep stepProcessing = (AbstractProcessingStep) stepClass
.newInstance();
stepProcessing.doProcessing(context, getRequestService().getCurrentRequest(), source);
stepProcessing.doPreProcessing(context, source);
} else {
throw new Exception("The submission step class specified by '"
+ stepConfig.getProcessingClassName()
@@ -199,52 +218,55 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@Override
public UploadBitstreamRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
String extraField, MultipartFile file) throws Exception {
UploadBitstreamRest result;
Bitstream source = null;
BitstreamFormat bf = null;
public WorkspaceItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
MultipartFile file) throws Exception {
Context context = obtainContext();
WorkspaceItem wsi = wis.find(context, id);
Item item = wsi.getItem();
// do we already have a bundle?
List<Bundle> bundles = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME);
WorkspaceItemRest wsi = findOne(id);
WorkspaceItem source = wis.find(context, id);
List<ErrorRest> errors = new ArrayList<ErrorRest>();
SubmissionConfig submissionConfig =
submissionConfigReader.getSubmissionConfigByName(wsi.getSubmissionDefinition().getName());
for (int i = 0; i < submissionConfig.getNumberOfSteps(); i++) {
SubmissionStepConfig stepConfig = submissionConfig.getStep(i);
try {
InputStream inputStream = new BufferedInputStream(file.getInputStream());
if (bundles.size() < 1) {
// set bundle's name to ORIGINAL
source = itemService.createSingleBitstream(context, inputStream, item, Constants.CONTENT_BUNDLE_NAME);
} else {
// we have a bundle already, just add bitstream
source = bitstreamService.create(context, bundles.get(0), inputStream);
/*
* First, load the step processing class (using the current
* class loader)
*/
ClassLoader loader = this.getClass().getClassLoader();
Class stepClass;
try {
stepClass = loader.loadClass(stepConfig.getProcessingClassName());
Object stepInstance = stepClass.newInstance();
if (UploadableStep.class.isAssignableFrom(stepClass)) {
UploadableStep uploadableStep = (UploadableStep) stepInstance;
uploadableStep.doPreProcessing(context, source);
ErrorRest err =
uploadableStep.upload(context, submissionService, stepConfig, source, file);
uploadableStep.doPostProcessing(context, source);
if (err != null) {
errors.add(err);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
result = new UploadBitstreamRest();
result.setMessage(e.getMessage());
result.setStatus(false);
return result;
}
wsi = converter.convert(source);
if (errors.isEmpty()) {
wsi.setStatus(true);
} else {
wsi.setStatus(false);
wsi.getErrors().addAll(errors);
}
source.setName(context, file.getOriginalFilename());
// TODO how retrieve this information?
source.setSource(context, extraField);
// Identify the format
bf = bitstreamFormatService.guessFormat(context, source);
source.setFormat(context, bf);
// Update to DB
bitstreamService.update(context, source);
itemService.update(context, item);
context.commit();
result = submissionService.buildUploadBitstream(configurationService, source);
result.setStatus(true);
return result;
return wsi;
}
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@@ -290,20 +312,21 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
if (stepInstance instanceof AbstractRestProcessingStep) {
// load the JSPStep interface for this step
AbstractRestProcessingStep stepProcessing = (AbstractRestProcessingStep) stepClass
.newInstance();
AbstractRestProcessingStep stepProcessing =
(AbstractRestProcessingStep) stepClass.newInstance();
stepProcessing.doPreProcessing(context, source);
stepProcessing.doPatchProcessing(context, getRequestService().getCurrentRequest(), source, op);
stepProcessing.doPostProcessing(context, source);
} else {
throw new PatchBadRequestException("The submission step class specified by '"
+ stepConfig.getProcessingClassName()
+ "' does not extend the class org.dspace.submit" +
".AbstractProcessingStep!"
+ " Therefore it cannot be used by the Configurable " +
"Submission as the <processing-class>!");
throw new PatchBadRequestException(
"The submission step class specified by '" + stepConfig.getProcessingClassName() +
"' does not extend the class org.dspace.submit.AbstractProcessingStep!" +
" Therefore it cannot be used by the Configurable Submission as the <processing-class>!");
}
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new PatchException("Error processing the patch request", e);
}
}
}
@@ -316,8 +339,161 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
try {
witem = wis.find(context, id);
wis.deleteAll(context, witem);
context.addEvent(new Event(Event.DELETE, Constants.ITEM, witem.getItem().getID(), null,
itemService.getIdentifiers(context, witem.getItem())));
} catch (SQLException | IOException e) {
log.error(e.getMessage(), e);
}
}
@Override
public Iterable<WorkspaceItemRest> upload(Context context, HttpServletRequest request,
MultipartFile uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException {
File file = Utils.getFile(uploadfile, "upload-loader", "filedataloader");
List<WorkspaceItemRest> results = new ArrayList<>();
try {
String uuid = request.getParameter("collection");
if (StringUtils.isBlank(uuid)) {
uuid = configurationService.getProperty("submission.default.collection");
}
Collection collection = null;
if (StringUtils.isNotBlank(uuid)) {
collection = collectionService.find(context, UUID.fromString(uuid));
} else {
collection = collectionService.findAuthorizedOptimized(context, Constants.ADD).get(0);
}
SubmissionConfig submissionConfig =
submissionConfigReader.getSubmissionConfigByCollection(collection.getHandle());
List<ItemSubmissionLookupDTO> tmpResult = new ArrayList<ItemSubmissionLookupDTO>();
TransformationEngine transformationEngine1 = submissionLookupService.getPhase1TransformationEngine();
TransformationSpec spec = new TransformationSpec();
// FIXME this is mostly due to the need to test. The BTE framework has an assert statement that check if the
// number of found record is less than the requested and treat 0 as is, instead, the implementation assume
// 0=unlimited this lead to test failure.
// It is unclear if BTE really respect values other than 0/MAX allowing us to put a protection against heavy
// load
spec.setNumberOfRecords(Integer.MAX_VALUE);
if (transformationEngine1 != null) {
MultipleSubmissionLookupDataLoader dataLoader =
(MultipleSubmissionLookupDataLoader) transformationEngine1.getDataLoader();
List<String> fileDataLoaders = submissionLookupService.getFileProviders();
for (String fileDataLoader : fileDataLoaders) {
dataLoader.setFile(file.getAbsolutePath(), fileDataLoader);
try {
SubmissionLookupOutputGenerator outputGenerator =
(SubmissionLookupOutputGenerator) transformationEngine1.getOutputGenerator();
outputGenerator.setDtoList(new ArrayList<ItemSubmissionLookupDTO>());
log.debug("BTE transformation is about to start!");
transformationEngine1.transform(spec);
log.debug("BTE transformation finished!");
tmpResult.addAll(outputGenerator.getDtoList());
if (!tmpResult.isEmpty()) {
//exit with the results founded on the first data provided
break;
}
} catch (BadTransformationSpec e1) {
log.error(e1.getMessage(), e1);
} catch (MalformedSourceException e1) {
log.error(e1.getMessage(), e1);
}
}
}
List<WorkspaceItem> result = null;
//try to ingest workspaceitems
if (!tmpResult.isEmpty()) {
TransformationEngine transformationEngine2 = submissionLookupService.getPhase2TransformationEngine();
if (transformationEngine2 != null) {
SubmissionItemDataLoader dataLoader =
(SubmissionItemDataLoader) transformationEngine2.getDataLoader();
dataLoader.setDtoList(tmpResult);
// dataLoader.setProviders()
DSpaceWorkspaceItemOutputGenerator outputGenerator =
(DSpaceWorkspaceItemOutputGenerator) transformationEngine2.getOutputGenerator();
outputGenerator.setCollection(collection);
outputGenerator.setContext(context);
outputGenerator.setFormName(submissionConfig.getSubmissionName());
outputGenerator.setDto(tmpResult.get(0));
try {
transformationEngine2.transform(spec);
result = outputGenerator.getWitems();
} catch (BadTransformationSpec e1) {
e1.printStackTrace();
} catch (MalformedSourceException e1) {
e1.printStackTrace();
}
}
}
//we have to create the workspaceitem to push the file also if nothing found before
if (result == null) {
WorkspaceItem source =
submissionService.createWorkspaceItem(context, getRequestService().getCurrentRequest());
result = new ArrayList<>();
result.add(source);
}
//perform upload of bitstream if there is exact one result and convert workspaceitem to entity rest
if (result != null && !result.isEmpty()) {
for (WorkspaceItem wi : result) {
List<ErrorRest> errors = new ArrayList<ErrorRest>();
//load bitstream into bundle ORIGINAL only if there is one result (approximately this is the
// right behaviour for pdf file but not for other bibliographic format e.g. bibtex)
if (result.size() == 1) {
for (int i = 0; i < submissionConfig.getNumberOfSteps(); i++) {
SubmissionStepConfig stepConfig = submissionConfig.getStep(i);
ClassLoader loader = this.getClass().getClassLoader();
Class stepClass;
try {
stepClass = loader.loadClass(stepConfig.getProcessingClassName());
Object stepInstance = stepClass.newInstance();
if (UploadableStep.class.isAssignableFrom(stepClass)) {
UploadableStep uploadableStep = (UploadableStep) stepInstance;
ErrorRest err = uploadableStep.upload(context, submissionService, stepConfig, wi,
uploadfile);
if (err != null) {
errors.add(err);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
WorkspaceItemRest wsi = converter.convert(wi);
if (result.size() == 1) {
if (errors.isEmpty()) {
wsi.setStatus(true);
} else {
wsi.setStatus(false);
wsi.getErrors().addAll(errors);
}
}
results.add(wsi);
}
}
} finally {
file.delete();
}
return results;
}
}

View File

@@ -7,16 +7,12 @@
*/
package org.dspace.app.rest.repository.patch;
import java.sql.SQLException;
import java.util.List;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
/**
* The base class for resource PATCH operations.
@@ -26,85 +22,80 @@ import org.dspace.core.Context;
public abstract class AbstractResourcePatch<R extends RestModel> {
/**
* Handles the patch operations, delegating actions to sub-class implementations. If no sub-class method
* is provided, the default method throws a UnprocessableEntityException.
* Handles the patch operations. Patch implementations are provided by subclasses.
* The default methods throw an UnprocessableEntityException.
*
* @param restModel the REST resource to patch
* @param context
* @param patch
* @param restModel the rest resource to patch
* @param operations list of patch operations
* @throws UnprocessableEntityException
* @throws PatchBadRequestException
* @throws SQLException
* @throws AuthorizeException
*/
public void patch(R restModel, Context context, Patch patch)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
List<Operation> operations = patch.getOperations();
public RestModel patch(R restModel, List<Operation> operations) {
// Note: the list of possible operations is taken from JsonPatchConverter class. Does not implement
// test https://tools.ietf.org/html/rfc6902#section-4.6
ops: for (Operation op : operations) {
switch (op.getOp()) {
case "add":
add(restModel, context, op);
restModel = add(restModel, op);
continue ops;
case "replace":
replace(restModel, context, op);
restModel = replace(restModel, op);
continue ops;
case "remove":
remove(restModel, context, op);
restModel = remove(restModel, op);
continue ops;
case "copy":
copy(restModel, context, op);
restModel = copy(restModel, op);
continue ops;
case "move":
move(restModel, context, op);
restModel = move(restModel, op);
continue ops;
default:
// JsonPatchConverter should have thrown error before this point.
throw new PatchBadRequestException("Missing or illegal patch operation: " + op.getOp());
}
}
return restModel;
}
// The default patch methods throw an error when no sub-class implementation is provided.
protected void add(R restModel, Context context, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
protected R add(R restModel, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException {
throw new UnprocessableEntityException(
"The add operation is not supported."
"The add operation is not supported."
);
}
protected void replace(R restModel, Context context, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
// The replace operation is functionally identical to a "remove" operation for
// a value, followed immediately by an "add" operation at the same
// location with the replacement value. https://tools.ietf.org/html/rfc6902#section-4.3
remove(restModel, context, operation);
add(restModel, context, operation);
}
protected void remove(R restModel, Context context, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
protected R replace(R restModel, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException {
throw new UnprocessableEntityException(
"The remove operation is not supported."
"The replace operation is not supported."
);
}
protected void copy(R restModel, Context context, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
protected R remove(R restModel, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException {
throw new UnprocessableEntityException(
"The copy operation is not supported."
"The remove operation is not supported."
);
}
protected void move(R restModel, Context context, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
protected R copy(R restModel, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException {
throw new UnprocessableEntityException(
"The move operation is not supported."
"The copy operation is not supported."
);
}
}
protected R move(R restModel, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException {
throw new UnprocessableEntityException(
"The move operation is not supported."
);
}
}

View File

@@ -0,0 +1,43 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.repository.patch.factories.EPersonOperationFactory;
import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Provides patch operations for eperson updates.
*/
@Component
public class EPersonPatch extends AbstractResourcePatch<EPersonRest> {
@Autowired
EPersonOperationFactory patchFactory;
/**
* Performs the replace operation.
* @param eperson the eperson rest representation
* @param operation the replace operation
* @throws UnprocessableEntityException
* @throws PatchBadRequestException
*/
protected EPersonRest replace(EPersonRest eperson, Operation operation) {
ResourcePatchOperation<EPersonRest> patchOperation =
patchFactory.getReplaceOperationForPath(operation.getPath());
return patchOperation.perform(eperson, operation);
}
}

View File

@@ -7,137 +7,37 @@
*/
package org.dspace.app.rest.repository.patch;
import java.sql.SQLException;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.app.rest.repository.patch.factories.ItemOperationFactory;
import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This is the implementation for Item resource patches.
*
* @author Michael Spalti
* Provides PATCH operations for item updates.
*/
@Component
public class ItemPatch extends AbstractResourcePatch<ItemRest> {
private static final String OPERATION_PATH_WITHDRAW = "/withdrawn";
private static final String OPERATION_PATH_DISCOVERABLE = "/discoverable";
private static final Logger log = Logger.getLogger(ItemPatch.class);
@Autowired
ItemService is;
ItemOperationFactory patchFactory;
/**
* Implementation of the PATCH replace operation.
*
* @param restModel
* @param context
* @param operation
* Peforms the replace operation.
* @param item the rest representation of the item
* @param operation the replace operation
* @throws UnprocessableEntityException
* @throws PatchBadRequestException
* @throws SQLException
* @throws AuthorizeException
*/
@Override
protected void replace(ItemRest restModel, Context context, Operation operation)
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException {
protected ItemRest replace(ItemRest item, Operation operation) {
switch (operation.getPath()) {
case OPERATION_PATH_WITHDRAW:
withdraw(restModel, context, (Boolean) operation.getValue());
break;
case OPERATION_PATH_DISCOVERABLE:
discoverable(restModel, context, (Boolean) operation.getValue());
break;
default:
throw new UnprocessableEntityException(
"Unrecognized patch operation path: " + operation.getPath()
);
}
}
ResourcePatchOperation<ItemRest> patchOperation =
patchFactory.getReplaceOperationForPath(operation.getPath());
/**
* Withdraws or reinstates the item based on boolean value provided in the patch request.
*
* @param restModel
* @param context
* @param withdrawItem
* @throws PatchBadRequestException
* @throws SQLException
* @throws AuthorizeException
*/
private void withdraw(ItemRest restModel, Context context, Boolean withdrawItem)
throws PatchBadRequestException, SQLException, AuthorizeException {
try {
if (withdrawItem == null) {
throw new PatchBadRequestException("Boolean value not provided for withdrawal operation.");
}
if (withdrawItem) {
// Item is not withdrawn but is also NOT archived. Is this a possible situation?
if (!restModel.getWithdrawn() && !restModel.getInArchive()) {
throw new UnprocessableEntityException("Cannot withdraw item because it is not archived.");
}
// Item is already withdrawn. No-op, 200 response.
// (The operation is not idempotent since it results in a provenance note in the record.)
if (restModel.getWithdrawn()) {
return;
}
Item item = is.find(context, UUID.fromString(restModel.getUuid()));
is.withdraw(context, item);
} else {
// No need to reinstate item if it has not previously been not withdrawn.
// No-op, 200 response. (The operation is not idempotent since it results
// in a provenance note in the record.)
if (!restModel.getWithdrawn()) {
return;
}
Item item = is.find(context, UUID.fromString(restModel.getUuid()));
is.reinstate(context, item);
}
} catch (SQLException | AuthorizeException e) {
log.error(e.getMessage(), e);
throw e;
}
}
/**
* Sets discoverable field on the item.
*
* @param restModel
* @param context
* @param isDiscoverable
* @throws SQLException
* @throws AuthorizeException
*/
private void discoverable(ItemRest restModel, Context context, Boolean isDiscoverable)
throws SQLException, AuthorizeException {
if (isDiscoverable == null) {
throw new PatchBadRequestException("Boolean value not provided for discoverable operation.");
}
try {
Item item = is.find(context, UUID.fromString(restModel.getUuid()));
item.setDiscoverable(isDiscoverable);
is.update(context, item);
} catch (SQLException | AuthorizeException e) {
log.error(e.getMessage(), e);
throw e;
}
return patchOperation.perform(item, operation);
}
}

View File

@@ -0,0 +1,68 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.repository.patch.factories.impl.EPersonCertificateReplaceOperation;
import org.dspace.app.rest.repository.patch.factories.impl.EPersonLoginReplaceOperation;
import org.dspace.app.rest.repository.patch.factories.impl.EPersonNetidReplaceOperation;
import org.dspace.app.rest.repository.patch.factories.impl.EPersonPasswordReplaceOperation;
import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Provides factory methods for obtaining instances of eperson patch operations.
*
* @author Michael Spalti
*/
@Component
public class EPersonOperationFactory {
@Autowired
EPersonPasswordReplaceOperation passwordReplaceOperation;
@Autowired
EPersonLoginReplaceOperation loginReplaceOperation;
@Autowired
EPersonCertificateReplaceOperation certificateReplaceOperation;
@Autowired
EPersonNetidReplaceOperation netidReplaceOperation;
private static final String OPERATION_PASSWORD_CHANGE = "/password";
private static final String OPERATION_CAN_LOGIN = "/canLogin";
private static final String OPERATION_REQUIRE_CERTIFICATE = "/certificate";
private static final String OPERATION_SET_NETID = "/netid";
/**
* Returns the patch instance for the replace operation (based on the operation path).
*
* @param path the operation path
* @return the patch operation implementation
* @throws PatchBadRequestException
*/
public ResourcePatchOperation<EPersonRest> getReplaceOperationForPath(String path) {
switch (path) {
case OPERATION_PASSWORD_CHANGE:
return passwordReplaceOperation;
case OPERATION_CAN_LOGIN:
return loginReplaceOperation;
case OPERATION_REQUIRE_CERTIFICATE:
return certificateReplaceOperation;
case OPERATION_SET_NETID:
return netidReplaceOperation;
default:
throw new PatchBadRequestException("Missing patch operation for: " + path);
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.repository.patch.factories.impl.ItemDiscoverableReplaceOperation;
import org.dspace.app.rest.repository.patch.factories.impl.ItemWithdrawReplaceOperation;
import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Provides factory methods for obtaining instances of item patch operations.
*
* @author Michael Spalti
*/
@Component
public class ItemOperationFactory {
@Autowired
ItemDiscoverableReplaceOperation itemDiscoverableReplaceOperation;
@Autowired
ItemWithdrawReplaceOperation itemWithdrawReplaceOperation;
private static final String OPERATION_PATH_WITHDRAW = "/withdrawn";
private static final String OPERATION_PATH_DISCOVERABLE = "/discoverable";
/**
* Returns the patch instance for the replace operation (based on the operation path).
*
* @param path the operation path
* @return the patch operation implementation
* @throws PatchBadRequestException
*/
public ResourcePatchOperation<ItemRest> getReplaceOperationForPath(String path) {
switch (path) {
case OPERATION_PATH_DISCOVERABLE:
return itemDiscoverableReplaceOperation;
case OPERATION_PATH_WITHDRAW:
return itemWithdrawReplaceOperation;
default:
throw new PatchBadRequestException("Missing patch operation for: " + path);
}
}
}

View File

@@ -0,0 +1,58 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories.impl;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.stereotype.Component;
/**
* Implementation for EPerson requires certificate patches.
*
* Example: <code>
* curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H "
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
* /certificate", "value": true|false]'
* </code>
*
* @author Michael Spalti
*/
@Component
public class EPersonCertificateReplaceOperation extends ReplacePatchOperation<EPersonRest, Boolean>
implements ResourcePatchOperation<EPersonRest> {
@Override
public EPersonRest replace(EPersonRest eperson, Operation operation) {
Boolean requireCert = getBooleanOperationValue(operation.getValue());
eperson.setRequireCertificate(requireCert);
return eperson;
}
@Override
void checkModelForExistingValue(EPersonRest resource) {
// TODO: many (all?) boolean values on the rest model should never be null.
// So perhaps the error to throw in this case is different...IllegalStateException?
// Or perhaps do nothing (no check is required).
if ((Object) resource.isRequireCertificate() == null) {
throw new PatchBadRequestException("Attempting to replace a non-existent value.");
}
}
@Override
protected Class<Boolean[]> getArrayClassForEvaluation() {
return Boolean[].class;
}
@Override
protected Class<Boolean> getClassForEvaluation() {
return Boolean.class;
}
}

View File

@@ -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.rest.repository.patch.factories.impl;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.stereotype.Component;
/**
* Implementation for EPerson canLogin patches.
*
* Example: <code>
* curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H "
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
* /canLogin", "value": true|false]'
* </code>
*
* @author Michael Spalti
*/
@Component
public class EPersonLoginReplaceOperation extends ReplacePatchOperation<EPersonRest, Boolean>
implements ResourcePatchOperation<EPersonRest> {
@Override
public EPersonRest replace(EPersonRest eperson, Operation operation) {
Boolean canLogin = getBooleanOperationValue(operation.getValue());
eperson.setCanLogIn(canLogin);
return eperson;
}
@Override
void checkModelForExistingValue(EPersonRest resource) {
if ((Object) resource.isCanLogIn() == null) {
throw new PatchBadRequestException("Attempting to replace a non-existent value.");
}
}
@Override
protected Class<Boolean[]> getArrayClassForEvaluation() {
return Boolean[].class;
}
@Override
protected Class<Boolean> getClassForEvaluation() {
return Boolean.class;
}
}

View File

@@ -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.rest.repository.patch.factories.impl;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.stereotype.Component;
/**
* Implementation for EPerson netid patches.
*
* Example: <code>
* curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H "
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
* /netid", "value": "newNetId"]'
* </code>
*
* @author Michael Spalti
*/
@Component
public class EPersonNetidReplaceOperation extends ReplacePatchOperation<EPersonRest, String>
implements ResourcePatchOperation<EPersonRest> {
@Override
EPersonRest replace(EPersonRest eperson, Operation operation) {
eperson.setNetid((String) operation.getValue());
return eperson;
}
@Override
void checkModelForExistingValue(EPersonRest resource) {
if (resource.getNetid() == null) {
throw new PatchBadRequestException("Attempting to replace a non-existent value.");
}
}
@Override
protected Class<String[]> getArrayClassForEvaluation() {
return String[].class;
}
@Override
protected Class<String> getClassForEvaluation() {
return String.class;
}
}

View File

@@ -0,0 +1,55 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories.impl;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.stereotype.Component;
/**
* Implementation for EPerson password patches.
*
* Example: <code>
* curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H "
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
* /password", "value": "newpassword"]'
* </code>
*
* @author Michael Spalti
*/
@Component
public class EPersonPasswordReplaceOperation extends ReplacePatchOperation<EPersonRest, String> {
@Override
EPersonRest replace(EPersonRest eperson, Operation operation) {
eperson.setPassword((String) operation.getValue());
return eperson;
}
@Override
void checkModelForExistingValue(EPersonRest resource) {
/*
* FIXME: the password field in eperson rest model is always null because
* the value is not set in the rest converter.
* We would normally throw an exception here since replace
* operations are not allowed on non-existent values, but that
* would prevent the password update from ever taking place.
*/
}
@Override
protected Class<String[]> getArrayClassForEvaluation() {
return String[].class;
}
@Override
protected Class<String> getClassForEvaluation() {
return String.class;
}
}

View File

@@ -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.app.rest.repository.patch.factories.impl;
import org.apache.log4j.Logger;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.stereotype.Component;
/**
* This is the implementation for Item resource patches.
*
* Example: <code>
* curl -X PATCH http://${dspace.url}/api/item/<:id-item> -H "
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
* /discoverable", "value": true|false]'
* </code>
*
* @author Michael Spalti
*/
@Component
public class ItemDiscoverableReplaceOperation extends ReplacePatchOperation<ItemRest, Boolean> {
private static final Logger log = Logger.getLogger(ItemDiscoverableReplaceOperation.class);
@Override
public ItemRest replace(ItemRest item, Operation operation) {
Boolean discoverable = getBooleanOperationValue(operation.getValue());
item.setDiscoverable(discoverable);
return item;
}
@Override
void checkModelForExistingValue(ItemRest resource) {
if ((Object) resource.getDiscoverable() == null) {
throw new PatchBadRequestException("Attempting to replace a non-existent value.");
}
}
protected Class<Boolean[]> getArrayClassForEvaluation() {
return Boolean[].class;
}
protected Class<Boolean> getClassForEvaluation() {
return Boolean.class;
}
}

View File

@@ -0,0 +1,81 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories.impl;
import org.apache.log4j.Logger;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.stereotype.Component;
/**
* This is the implementation for Item resource patches.
* <p>
* Example: <code>
* curl -X PATCH http://${dspace.url}/api/item/<:id-item> -H "
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
* /withdrawn", "value": true|false]'
* </code>
*
* @author Michael Spalti
*/
@Component
public class ItemWithdrawReplaceOperation extends ReplacePatchOperation<ItemRest, Boolean> {
private static final Logger log = Logger.getLogger(ItemWithdrawReplaceOperation.class);
@Override
public ItemRest replace(ItemRest item, Operation operation) {
Boolean withdraw = getBooleanOperationValue(operation.getValue());
// This is a request to withdraw the item.
if (withdraw) {
// The item is currently not withdrawn and also not archived. Is this a possible situation?
if (!item.getWithdrawn() && !item.getInArchive()) {
throw new UnprocessableEntityException("Cannot withdraw item when it is not in archive.");
}
// Item is already withdrawn. No-op, 200 response.
// (The operation is not idempotent since it results in a provenance note in the record.)
if (item.getWithdrawn()) {
return item;
}
item.setWithdrawn(true);
return item;
} else {
// No need to reinstate item if it has not previously been not withdrawn.
// No-op, 200 response. (The operation is not idempotent since it results
// in a provenance note in the record.)
if (!item.getWithdrawn()) {
return item;
}
item.setWithdrawn(false);
return item;
}
}
@Override
void checkModelForExistingValue(ItemRest resource) {
if ((Object) resource.getWithdrawn() == null) {
throw new PatchBadRequestException("Attempting to replace a non-existent value.");
}
}
protected Class<Boolean[]> getArrayClassForEvaluation() {
return Boolean[].class;
}
protected Class<Boolean> getClassForEvaluation() {
return Boolean.class;
}
}

View File

@@ -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.rest.repository.patch.factories.impl;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.BooleanUtils;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.patch.Operation;
import org.springframework.data.rest.webmvc.json.patch.LateObjectEvaluator;
/**
* Base class for all resource patch operations.
*
* @author Michael Spalti
*/
public abstract class PatchOperation<R extends RestModel, T>
implements ResourcePatchOperation<R> {
/**
* Updates the rest model by applying the patch operation.
*
* @param resource the rest model.
* @param operation the patch operation.
* @return the updated rest model.
* @throws PatchBadRequestException
*/
public abstract R perform(R resource, Operation operation);
/**
* Throws PatchBadRequestException for missing operation value.
*
* @param value the value to test
*/
void checkOperationValue(Object value) {
if (value == null) {
throw new PatchBadRequestException("No value provided for the operation.");
}
}
/**
* Allows clients to use either a boolean or a string representation of boolean value.
*
* @param value the operation value
* @return the original or derived boolean value
* @throws PatchBadRequestException
*/
Boolean getBooleanOperationValue(Object value) {
Boolean bool;
if (value instanceof String) {
bool = BooleanUtils.toBooleanObject((String) value);
if (bool == null) {
// make sure the string was converted to boolean.
throw new PatchBadRequestException("Boolean value not provided.");
}
} else {
bool = (Boolean) value;
}
return bool;
}
// This is duplicated code (see org.dspace.app.rest.submit.factory.impl.PatchOperation)
// If it stays here, it should be DRY. Current patch resource patch operations do not
// use these methods since operation values are either strings or booleans.
// These methods handle JsonValueEvaluator instances for json objects and arrays,
// as returned by the JsonPatchConverter. A complete implementation of the PatchOperation
// class will need these methods.
public List<T> evaluateArrayObject(LateObjectEvaluator value) {
List<T> results = new ArrayList<T>();
T[] list = null;
if (value != null) {
LateObjectEvaluator object = (LateObjectEvaluator) value;
list = (T[]) object.evaluate(getArrayClassForEvaluation());
}
for (T t : list) {
results.add(t);
}
return results;
}
public T evaluateSingleObject(LateObjectEvaluator value) {
T single = null;
if (value != null) {
LateObjectEvaluator object = (LateObjectEvaluator) value;
single = (T) object.evaluate(getClassForEvaluation());
}
return single;
}
/**
* This method should return the typed array to be used in the
* LateObjectEvaluator evaluation of json arrays.
*
* @return
*/
protected abstract Class<T[]> getArrayClassForEvaluation();
/**
* This method should return the object type to be used in the
* LateObjectEvaluator evaluation of json objects.
*
* @return
*/
protected abstract Class<T> getClassForEvaluation();
}

View File

@@ -0,0 +1,64 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories.impl;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.patch.Operation;
/**
* Base class for replace patch operations.
*
* @author Michael Spalti
*/
public abstract class ReplacePatchOperation<R extends RestModel, T>
extends PatchOperation<R, T> {
/**
* Implements the patch operation for replace operations.
* Before performing the replace operation this method checks
* for a non-null operation value and a non-null value on the rest model
* (replace operations should only be applied to an existing value).
* @param resource the rest model.
* @param operation the replace patch operation.
* @return the updated rest model.
* @throws PatchBadRequestException
* @throws UnprocessableEntityException
*/
@Override
public R perform(R resource, Operation operation) {
checkOperationValue(operation.getValue());
checkModelForExistingValue(resource);
return replace(resource, operation);
}
/**
* Executes the replace patch operation.
*
* @param resource the rest model.
* @param operation the replace patch operation.
* @return the updated rest model.
* @throws PatchBadRequestException
* @throws UnprocessableEntityException
*/
abstract R replace(R resource, Operation operation);
/**
* Replace operations are not allowed on non-existent values.
* Null values may exist in the RestModel for certain fields
* (usually non-boolean). This method should be implemented
* to assure that the replace operation acts only on an existing value.
* @param resource the rest model.
* @throws PatchBadRequestException
*/
abstract void checkModelForExistingValue(R resource);
}

View File

@@ -0,0 +1,23 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository.patch.factories.impl;
import org.dspace.app.rest.exception.PatchBadRequestException;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.patch.Operation;
/**
* The patch interface used by repository classes.
* @param <R>
*/
public interface ResourcePatchOperation<R extends RestModel> {
R perform(R resource, Operation operation)
throws PatchBadRequestException;
}

View File

@@ -21,11 +21,12 @@ import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.services.model.Request;
/**
* Interface to retrieve information about section
* Interface for the submission steps to populate sections in the in progress submission and react to patch requests.
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public interface AbstractRestProcessingStep {
public interface AbstractRestProcessingStep extends ListenerProcessingStep {
public static final String DESCRIBE_STEP_METADATA_OPERATION_ENTRY = "itemmetadata";
public static final String COLLECTION_STEP_OPERATION_ENTRY = "collection";
@@ -37,9 +38,33 @@ public interface AbstractRestProcessingStep {
public static final String UPLOAD_STEP_METADATA_PATH = "metadata";
/**
* Method to expose data in the a dedicated section of the in progress submission. The step needs to return a
* serializable object that will be included in a section with the name (id) assigned to the step in the
* item-submission.xml file
*
* @param submissionService
* the submission service
* @param obj
* the in progress submission
* @param config
* the submission step configuration
* @return the serializable object to include in the step generated section
* @throws Exception
*/
public <T extends Serializable> T getData(SubmissionService submissionService, WorkspaceItem obj,
SubmissionStepConfig config) throws Exception;
/**
* The method will expose the list of validation errors identified by the step. The default implementation will
* found a {@link Validation} spring bean in the context with the same name that the step id
*
* @param submissionService
* @param obj
* @param config
* @return
* @throws Exception
*/
default public List<ErrorRest> validate(SubmissionService submissionService, WorkspaceItem obj,
SubmissionStepConfig config) throws Exception {
List<ErrorRest> errors = new ArrayList<ErrorRest>();
@@ -55,6 +80,19 @@ public interface AbstractRestProcessingStep {
return errors;
}
/**
* Method to react to a patch request against the step managed section data
*
* @param context
* the DSpace context
* @param currentRequest
* the http request
* @param source
* the in progress submission
* @param op
* the json patch operation
* @throws Exception
*/
public void doPatchProcessing(Context context, Request currentRequest, WorkspaceItem source, Operation op)
throws Exception;

View File

@@ -0,0 +1,28 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.submit;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
/**
* This interface allows a submission step to access and modify if needed an inprogress submission also when changes are
* requested to sections other than the one managed by the step itself.
*
* This could be useful to allow a step wide validations or changes over multiple sections.
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public interface ListenerProcessingStep {
public void doPreProcessing(Context context, InProgressSubmission wsi);
public void doPostProcessing(Context context, InProgressSubmission wsi);
}

View File

@@ -18,12 +18,14 @@ import org.apache.log4j.Logger;
import org.atteo.evo.inflector.English;
import org.dspace.app.rest.converter.BitstreamFormatConverter;
import org.dspace.app.rest.converter.ResourcePolicyConverter;
import org.dspace.app.rest.exception.RESTAuthorizationException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.CheckSumRest;
import org.dspace.app.rest.model.MetadataValueRest;
import org.dspace.app.rest.model.ResourcePolicyRest;
import org.dspace.app.rest.model.step.UploadBitstreamRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
@@ -31,12 +33,14 @@ import org.dspace.content.MetadataValue;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.Utils;
import org.dspace.services.ConfigurationService;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.init.UncategorizedScriptException;
import org.springframework.stereotype.Component;
/**
@@ -64,23 +68,35 @@ public class SubmissionService {
public WorkspaceItem createWorkspaceItem(Context context, Request request) {
WorkspaceItem wsi = null;
Collection collection = null;
String collectionUUID = request.getHttpServletRequest().getParameter("collection");
if (StringUtils.isBlank(collectionUUID)) {
String uuid = configurationService.getProperty("submission.default.collection");
Collection collection = null;
try {
if (StringUtils.isNotBlank(uuid)) {
collection = collectionService.find(context, UUID.fromString(uuid));
collectionUUID = configurationService.getProperty("submission.default.collection");
}
try {
if (StringUtils.isNotBlank(collectionUUID)) {
collection = collectionService.find(context, UUID.fromString(collectionUUID));
} else {
final List<Collection> findAuthorizedOptimized = collectionService.findAuthorizedOptimized(context,
Constants.ADD);
if (findAuthorizedOptimized != null && findAuthorizedOptimized.size() > 0) {
collection = findAuthorizedOptimized.get(0);
} else {
collection = collectionService.findAll(context, 1, 0).get(0);
throw new RESTAuthorizationException("No collection suitable for submission for the current user");
}
wsi = workspaceItemService.create(context, collection, true);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} else {
//TODO manage setup of default collection in the case WSI it is not null
//TODO manage setup of collection discovered into request
if (collection == null) {
throw new RESTAuthorizationException("collectionUUID=" + collectionUUID + " not found");
}
wsi = workspaceItemService.create(context, collection, true);
} catch (SQLException e) {
// wrap in a runtime exception as we cannot change the method signature
throw new UncategorizedScriptException(e.getMessage(), e);
} catch (AuthorizeException ae) {
throw new RESTAuthorizationException(ae);
}
return wsi;
}

View File

@@ -0,0 +1,46 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.submit;
import java.io.IOException;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.InProgressSubmission;
import org.dspace.core.Context;
import org.springframework.web.multipart.MultipartFile;
/**
* The interface for submission Steps that need to deal with file upload
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public interface UploadableStep extends ListenerProcessingStep {
/**
* The method to implement to support upload of a file in the submission section (aka panel / step)
*
* @param context
* the dspace context
* @param submissionService
* the submission service
* @param stepConfig
* the configuration of the submission section
* @param wsi
* the inprogress submission
* @param file
* the multipart file, please note that it is a complex object containing additional information other
* than just the binary such as the filename and the mimetype
* @return the encountered error if any
* @throws IOException
*/
public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig,
InProgressSubmission wsi, MultipartFile file) throws IOException;
}

View File

@@ -52,7 +52,13 @@ public class LicenseAddPatchOperation extends AddPatchOperation<String> {
void add(Context context, Request currentRequest, WorkspaceItem source, String path, Object value)
throws Exception {
Boolean grant = BooleanUtils.toBooleanObject((String) value);
Boolean grant = null;
// we are friendly with the client and accept also a string representation for the boolean
if (value instanceof String) {
grant = BooleanUtils.toBooleanObject((String) value);
} else {
grant = (Boolean) value;
}
if (grant == null) {
throw new IllegalArgumentException(

View File

@@ -33,7 +33,13 @@ public class LicenseReplacePatchOperation extends ReplacePatchOperation<String>
void replace(Context context, Request currentRequest, WorkspaceItem source, String path, Object value)
throws Exception {
Boolean grant = BooleanUtils.toBooleanObject((String) value);
Boolean grant = null;
// we are friendly with the client and accept also a string representation for the boolean
if (value instanceof String) {
grant = BooleanUtils.toBooleanObject((String) value);
} else {
grant = (Boolean) value;
}
if (grant == null) {
throw new IllegalArgumentException(

View File

@@ -23,6 +23,7 @@ import org.dspace.app.util.DCInputSet;
import org.dspace.app.util.DCInputsReader;
import org.dspace.app.util.DCInputsReaderException;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.MetadataValue;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.Context;
@@ -30,9 +31,11 @@ import org.dspace.core.Utils;
import org.dspace.services.model.Request;
/**
* Describe step for DSpace Spring Rest. Handle the exposition of metadata own by the in progress submission.
* Describe step for DSpace Spring Rest. Expose and allow patching of the in progress submission metadata. It is
* configured via the config/submission-forms.xml file
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
public class DescribeStep extends org.dspace.submit.step.DescribeStep implements AbstractRestProcessingStep {
@@ -49,7 +52,17 @@ public class DescribeStep extends org.dspace.submit.step.DescribeStep implements
DataDescribe data = new DataDescribe();
try {
DCInputSet inputConfig = inputReader.getInputsByFormName(config.getId());
for (DCInput input : inputConfig.getFields()) {
readField(obj, config, data, inputConfig);
} catch (DCInputsReaderException e) {
log.error(e.getMessage(), e);
}
return data;
}
private void readField(InProgressSubmission obj, SubmissionStepConfig config, DataDescribe data,
DCInputSet inputConfig) throws DCInputsReaderException {
for (DCInput[] row : inputConfig.getFields()) {
for (DCInput input : row) {
List<String> fieldsName = new ArrayList<String>();
if (input.isQualdropValue()) {
@@ -93,10 +106,7 @@ public class DescribeStep extends org.dspace.submit.step.DescribeStep implements
}
}
}
} catch (DCInputsReaderException e) {
log.error(e.getMessage(), e);
}
return data;
}
@Override

View File

@@ -0,0 +1,114 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.submit.step;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import gr.ekt.bte.core.Record;
import gr.ekt.bte.core.RecordSet;
import gr.ekt.bte.core.Value;
import gr.ekt.bte.dataloader.FileDataLoader;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.repository.WorkspaceItemRestRepository;
import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.rest.submit.UploadableStep;
import org.dspace.app.rest.utils.Utils;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.submit.extraction.MetadataExtractor;
import org.dspace.submit.step.ExtractionStep;
import org.springframework.web.multipart.MultipartFile;
/**
* This submission step allows to extract metadata from an uploaded file to enrich or initialize a submission. The
* processing is delegated to a list of extractor specialized by format (i.e. a Grobid extractor to get data from a PDF
* file, an extractor to get data from bibliographic file such as BibTeX, etc)
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class ExtractMetadataStep extends ExtractionStep implements UploadableStep {
private static final Logger log = Logger.getLogger(ExtractMetadataStep.class);
@Override
public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig,
InProgressSubmission wsi, MultipartFile multipartFile)
throws IOException {
Item item = wsi.getItem();
try {
List<MetadataExtractor> extractors =
DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(MetadataExtractor.class);
File file = null;
for (MetadataExtractor extractor : extractors) {
FileDataLoader dataLoader = extractor.getDataLoader();
RecordSet recordSet = null;
if (extractor.getExtensions()
.contains(FilenameUtils.getExtension(multipartFile.getOriginalFilename()))) {
if (file == null) {
file = Utils.getFile(multipartFile, "submissionlookup-loader", stepConfig.getId());
}
FileDataLoader fdl = (FileDataLoader) dataLoader;
fdl.setFilename(Utils.getFileName(multipartFile));
recordSet = convertFields(dataLoader.getRecords(), bteBatchImportService.getOutputMap());
enrichItem(context, recordSet.getRecords(), item);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
ErrorRest result = new ErrorRest();
result.setMessage(e.getMessage());
result.getPaths().add("/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + stepConfig.getId());
return result;
}
return null;
}
private RecordSet convertFields(RecordSet recordSet, Map<String, String> fieldMap) {
RecordSet result = new RecordSet();
for (Record publication : recordSet.getRecords()) {
for (String fieldName : fieldMap.keySet()) {
String md = null;
if (fieldMap != null) {
md = fieldMap.get(fieldName);
}
if (StringUtils.isBlank(md)) {
continue;
} else {
md = md.trim();
}
if (publication.isMutable()) {
List<Value> values = publication.getValues(md);
publication.makeMutable().removeField(md);
publication.makeMutable().addField(fieldName, values);
}
}
result.addRecord(publication);
}
return result;
}
}

View File

@@ -7,22 +7,33 @@
*/
package org.dspace.app.rest.submit.step;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.step.DataUpload;
import org.dspace.app.rest.model.step.UploadBitstreamRest;
import org.dspace.app.rest.repository.WorkspaceItemRestRepository;
import org.dspace.app.rest.submit.AbstractRestProcessingStep;
import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.rest.submit.UploadableStep;
import org.dspace.app.rest.submit.factory.PatchOperationFactory;
import org.dspace.app.rest.submit.factory.impl.PatchOperation;
import org.dspace.app.rest.utils.Utils;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.services.model.Request;
import org.springframework.web.multipart.MultipartFile;
/**
* Upload step for DSpace Spring Rest. Expose information about the bitstream
@@ -30,8 +41,10 @@ import org.dspace.services.model.Request;
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class UploadStep extends org.dspace.submit.step.UploadStep implements AbstractRestProcessingStep {
public class UploadStep extends org.dspace.submit.step.UploadStep
implements AbstractRestProcessingStep, UploadableStep {
private static final Logger log = Logger.getLogger(UploadStep.class);
@Override
public DataUpload getData(SubmissionService submissionService, WorkspaceItem obj, SubmissionStepConfig config)
@@ -79,4 +92,54 @@ public class UploadStep extends org.dspace.submit.step.UploadStep implements Abs
}
@Override
public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig,
InProgressSubmission wsi, MultipartFile file) {
Bitstream source = null;
BitstreamFormat bf = null;
Item item = wsi.getItem();
List<Bundle> bundles = null;
try {
// do we already have a bundle?
bundles = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME);
InputStream inputStream = new BufferedInputStream(file.getInputStream());
if (bundles.size() < 1) {
// set bundle's name to ORIGINAL
source = itemService.createSingleBitstream(context, inputStream, item, Constants.CONTENT_BUNDLE_NAME);
} else {
// we have a bundle already, just add bitstream
source = bitstreamService.create(context, bundles.get(0), inputStream);
}
source.setName(context, Utils.getFileName(file));
source.setSource(context, file.getOriginalFilename());
// Identify the format
bf = bitstreamFormatService.guessFormat(context, source);
source.setFormat(context, bf);
// Update to DB
bitstreamService.update(context, source);
itemService.update(context, item);
} catch (Exception e) {
log.error(e.getMessage(), e);
ErrorRest result = new ErrorRest();
result.setMessage(e.getMessage());
if (bundles != null && bundles.size() > 0) {
result.getPaths().add(
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + stepConfig.getId() + "/files/" +
bundles.get(0).getBitstreams().size());
} else {
result.getPaths()
.add("/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + stepConfig.getId());
}
return result;
}
return null;
}
}

View File

@@ -14,7 +14,7 @@ import org.apache.commons.lang.StringUtils;
import org.dspace.app.rest.model.ErrorRest;
/**
* Abstract class to manage errors on validation during submission process
* Abstract class to provide basic management of errors resulting from a validation on a submission
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
@@ -24,6 +24,9 @@ public abstract class AbstractValidation implements Validation {
private List<ErrorRest> errors = new ArrayList<ErrorRest>();
/**
* An unique name to identify the validation implementation
*/
public String getName() {
return name;
}
@@ -32,6 +35,15 @@ public abstract class AbstractValidation implements Validation {
this.name = name;
}
/**
* Add an error message (i18nKey) for a specific json path
*
* @param i18nKey
* the validation error message as a key to internationalize
* @param path
* the json path that identify the wrong data in the submission. It could be as specific as a single
* value in a multivalued attribute or general of a "whole" section
*/
public void addError(String i18nKey, String path) {
boolean found = false;
if (StringUtils.isNotBlank(i18nKey)) {
@@ -51,6 +63,11 @@ public abstract class AbstractValidation implements Validation {
}
}
/**
* Expose the identified errors
*
* @return the list of identified {@link ErrorRest}
*/
public List<ErrorRest> getErrors() {
return errors;
}

View File

@@ -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.rest.submit.step.validation;
import java.sql.SQLException;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.repository.WorkspaceItemRestRepository;
import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.util.DCInputsReaderException;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.Bitstream;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Constants;
import org.springframework.beans.factory.annotation.Autowired;
/**
* This submission validation check that the license has been grant for the inprogress submission looking for the
* presence of a license bitstream in the license bundle,
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class LicenseValidation extends AbstractValidation {
private static final String ERROR_VALIDATION_LICENSEREQUIRED = "error.validation.license.notgranted";
private static final Logger log = Logger.getLogger(LicenseValidation.class);
@Autowired
private BitstreamService bitstreamService;
@Override
public List<ErrorRest> validate(SubmissionService submissionService, WorkspaceItem obj,
SubmissionStepConfig config) throws DCInputsReaderException, SQLException {
Bitstream bitstream = bitstreamService
.getBitstreamByName(obj.getItem(), Constants.LICENSE_BUNDLE_NAME, Constants.LICENSE_BITSTREAM_NAME);
if (bitstream == null) {
addError(ERROR_VALIDATION_LICENSEREQUIRED,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId());
}
return getErrors();
}
public BitstreamService getBitstreamService() {
return bitstreamService;
}
public void setBitstreamService(BitstreamService bitstreamService) {
this.bitstreamService = bitstreamService;
}
}

View File

@@ -55,43 +55,46 @@ public class MetadataValidation extends AbstractValidation {
SubmissionStepConfig config) throws DCInputsReaderException, SQLException {
DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId());
for (DCInput input : inputConfig.getFields()) {
for (DCInput[] row : inputConfig.getFields()) {
for (DCInput input : row) {
String fieldKey =
metadataAuthorityService.makeFieldKey(input.getSchema(), input.getElement(), input.getQualifier());
boolean isAuthorityControlled = metadataAuthorityService.isAuthorityControlled(fieldKey);
String fieldKey = metadataAuthorityService.makeFieldKey(input.getSchema(), input.getElement(),
input.getQualifier());
boolean isAuthorityControlled = metadataAuthorityService.isAuthorityControlled(fieldKey);
List<String> fieldsName = new ArrayList<String>();
if (input.isQualdropValue()) {
for (Object qualifier : input.getPairs()) {
fieldsName.add(input.getFieldName() + "." + (String) qualifier);
}
} else {
fieldsName.add(input.getFieldName());
}
for (String fieldName : fieldsName) {
List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName);
for (MetadataValue md : mdv) {
if (!(input.validate(md.getValue()))) {
addError(ERROR_VALIDATION_REGEX, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/"
+ config.getId() + "/" + input.getFieldName() + "/" + md.getPlace());
List<String> fieldsName = new ArrayList<String>();
if (input.isQualdropValue()) {
for (Object qualifier : input.getPairs()) {
fieldsName.add(input.getFieldName() + "." + (String) qualifier);
}
if (isAuthorityControlled) {
String authKey = md.getAuthority();
if (metadataAuthorityService.isAuthorityRequired(fieldKey) && StringUtils.isNotBlank(authKey)) {
addError(ERROR_VALIDATION_AUTHORITY_REQUIRED,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId()
+ "/" + input.getFieldName() + "/" + md.getPlace());
} else {
fieldsName.add(input.getFieldName());
}
for (String fieldName : fieldsName) {
List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName);
for (MetadataValue md : mdv) {
if (!(input.validate(md.getValue()))) {
addError(ERROR_VALIDATION_REGEX,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" +
input.getFieldName() + "/" + md.getPlace());
}
if (isAuthorityControlled) {
String authKey = md.getAuthority();
if (metadataAuthorityService.isAuthorityRequired(fieldKey) &&
StringUtils.isNotBlank(authKey)) {
addError(ERROR_VALIDATION_AUTHORITY_REQUIRED,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() +
"/" + input.getFieldName() + "/" + md.getPlace());
}
}
}
}
if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
// since this field is missing add to list of error
// fields
addError(ERROR_VALIDATION_REQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/"
+ config.getId() + "/" + input.getFieldName());
if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
// since this field is missing add to list of error
// fields
addError(ERROR_VALIDATION_REQUIRED,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" +
input.getFieldName());
}
}
}
}

View File

@@ -181,9 +181,7 @@ public class DiscoverQueryBuilder implements InitializingBean {
//Set search query
if (StringUtils.isNotBlank(query)) {
//Note that these quotes are needed incase we try to query OR for example.
//If the quotes aren't present, it'll crash.
queryArgs.setQuery("\"" + searchService.escapeQueryChars(query) + "\"");
queryArgs.setQuery(query);
}
//Limit results to DSO type

View File

@@ -41,8 +41,8 @@ public class MultipartFileSender {
private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES";
private static final String CONTENT_TYPE_MULTITYPE_WITH_BOUNDARY = "multipart/byteranges; boundary=" +
MULTIPART_BOUNDARY;
private static final String CONTENT_DISPOSITION_INLINE = "inline";
private static final String CONTENT_DISPOSITION_ATTACHMENT = "attachment";
public static final String CONTENT_DISPOSITION_INLINE = "inline";
public static final String CONTENT_DISPOSITION_ATTACHMENT = "attachment";
private static final String IF_NONE_MATCH = "If-None-Match";
private static final String IF_MODIFIED_SINCE = "If-Modified-Since";
private static final String ETAG = "ETag";
@@ -134,6 +134,10 @@ public class MultipartFileSender {
}
return this;
}
public MultipartFileSender withDisposition(String contentDisposition) {
this.disposition = contentDisposition;
return this;
}
public void serveResource() throws IOException {
@@ -172,9 +176,9 @@ public class MultipartFileSender {
CONTENT_DISPOSITION_ATTACHMENT;
}
response.setHeader(CONTENT_DISPOSITION, String.format(CONTENT_DISPOSITION_FORMAT, disposition, fileName));
log.debug("Content-Disposition : {}", disposition);
}
response.setHeader(CONTENT_DISPOSITION, String.format(CONTENT_DISPOSITION_FORMAT, disposition, fileName));
log.debug("Content-Disposition : {}", disposition);
// Content phase
if (METHOD_HEAD.equals(request.getMethod())) {

View File

@@ -9,6 +9,13 @@ package org.dspace.app.rest.utils;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
@@ -23,6 +30,7 @@ import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.repository.LinkRestRepository;
import org.dspace.core.ConfigurationManager;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@@ -31,6 +39,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
/**
* Collection of utility methods
@@ -155,4 +164,59 @@ public class Utils {
public String getMetadataKey(String schema, String element, String qualifier) {
return org.dspace.core.Utils.standardize(schema, element, qualifier, ".");
}
/**
* Create a temporary file from a multipart file upload
*
* @param multipartFile
* the multipartFile representing the uploaded file. Please note that it is a complex object including
* additional information other than the binary like the orginal file name and the mimetype
* @param prefixTempName
* the prefix to use to generate the filename of the temporary file
* @param suffixTempName
* the suffic to use to generate the filename of the temporary file
* @return the temporary file on the server
* @throws IOException
* @throws FileNotFoundException
*/
public static File getFile(MultipartFile multipartFile, String prefixTempName, String suffixTempName)
throws IOException, FileNotFoundException {
// TODO after change item-submission into
String tempDir = (ConfigurationManager.getProperty("upload.temp.dir") != null)
? ConfigurationManager.getProperty("upload.temp.dir")
: System.getProperty("java.io.tmpdir");
File uploadDir = new File(tempDir);
if (!uploadDir.exists()) {
if (!uploadDir.mkdir()) {
uploadDir = null;
}
}
File file = File.createTempFile(prefixTempName + "-" + suffixTempName, ".temp", uploadDir);
InputStream io = new BufferedInputStream(multipartFile.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
org.dspace.core.Utils.bufferedCopy(io, out);
return file;
}
/**
* Return the filename part from a multipartFile upload that could eventually contains the fullpath on the client
* filesystem
*
* @param multipartFile
* the file uploaded
* @return the filename part of the file on the client filesystem
* @throws IOException
* @throws FileNotFoundException
*/
public static String getFileName(MultipartFile multipartFile)
throws IOException, FileNotFoundException {
String originalFilename = multipartFile.getOriginalFilename();
if (originalFilename != null) {
// split by \ or / as we don't know the client OS (Win, Linux)
String[] parts = originalFilename.split("[\\/]");
return parts[parts.length - 1];
} else {
return multipartFile.getName();
}
}
}

View File

@@ -7,13 +7,55 @@
*/
HAL.Http.Client = function(opts) {
this.vent = opts.vent;
this.defaultHeaders = { 'Accept': 'application/hal+json, application/json, */*; q=0.01' };
cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserToken' + '\\s*=\\s*([^;]+)');
cookie ? this.defaultHeaders.Authorization = 'Bearer ' + cookie.pop() : '';
this.defaultHeaders = {'Accept': 'application/hal+json, application/json, */*; q=0.01'};
var authorizationHeader = getAuthorizationHeader();
authorizationHeader ? this.defaultHeaders.Authorization = authorizationHeader : '';
console.log(this.defaultHeaders);
this.headers = this.defaultHeaders;
};
function getAuthorizationHeader() {
var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserToken' + '\\s*=\\s*([^;]+)');
if(cookie != undefined) {
return 'Bearer ' + cookie.pop();
} else {
return undefined;
}
}
function downloadFile(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
var authorizationHeader = getAuthorizationHeader();
if (authorizationHeader != undefined) {
request.setRequestHeader("Authorization", authorizationHeader);
}
request.onload = function () {
// Only handle status code 200
if (request.status === 200) {
// Try to find out the filename from the content disposition `filename` value
var disposition = request.getResponseHeader('content-disposition');
var matches = /"([^"]*)"/.exec(disposition);
var filename = (matches != null && matches[1] ? matches[1] : 'content');
// The actual download
var contentTypeHeader = request.getResponseHeader("content-type");
if (contentTypeHeader === undefined || contentTypeHeader === "") {
contentTypeHeader = "application/octet-stream";
}
var blob = new Blob([request.response], {type: contentTypeHeader});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// some error handling should be done here...
};
request.send();
}
HAL.Http.Client.prototype.get = function(url) {
var self = this;
this.vent.trigger('location-change', { url: url });
@@ -31,9 +73,14 @@ HAL.Http.Client.prototype.get = function(url) {
headers: jqXHR.getAllResponseHeaders()
});
}
}).error(function() {
self.vent.trigger('fail-response', { jqxhr: jqxhr });
});
}).error(function (response) {
self.vent.trigger('fail-response', {jqxhr: jqxhr});
var contentTypeResponseHeader = jqxhr.getResponseHeader("content-type");
if (contentTypeResponseHeader != undefined
&& !contentTypeResponseHeader.startsWith("application/hal")
&& !contentTypeResponseHeader.startsWith("application/json")) {
downloadFile(url);
}});
};
HAL.Http.Client.prototype.request = function(opts) {

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