diff --git a/.gitattributes b/.gitattributes
index 03b42152e9..c00b03e686 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,12 @@
# Auto detect text files and perform LF normalization
* text=auto
+# Ensure Unix files always keep Unix line endings
+*.sh text eol=lf
+
+# Ensure Windows files always keep Windows line endings
+*.bat text eol=crlf
+
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
diff --git a/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java b/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java
index 194da3eba5..fc62f0df19 100644
--- a/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java
@@ -153,7 +153,7 @@ public class AuthorityValueServiceImpl implements AuthorityValueService {
public List findByValue(Context context, String schema, String element, String qualifier,
String value) {
String field = fieldParameter(schema, element, qualifier);
- return findByValue(context, field, qualifier);
+ return findByValue(context, field, value);
}
@Override
diff --git a/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java b/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java
index ca8efc09c4..398097a9ec 100644
--- a/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java
+++ b/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java
@@ -8,22 +8,59 @@
package org.dspace.submit.model;
/**
+ * This class represents an option available in the submission upload section to
+ * set permission on a file. An option is defined by a name such as "open
+ * access", "embargo", "restricted access", etc. and some optional attributes to
+ * better clarify the constraints and input available to the user. For instance
+ * an embargo option could allow to set a start date not longer than 3 years,
+ * etc
+ *
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
public class AccessConditionOption {
-
+ /** An unique name identifying the access contion option **/
private String name;
+ /**
+ * the name of the group that will be bound to the resource policy created if
+ * such option is used
+ */
private String groupName;
+ /**
+ * this is in alternative to the {@link #groupName}. The sub-groups listed in
+ * the DSpace group identified by the name here specified will be available to
+ * the user to personalize the access condition. They can be for instance
+ * University Staff, University Students, etc. so that a "restricted access"
+ * option can be further specified without the need to create separate access
+ * condition options for each group
+ */
private String selectGroupName;
+ /**
+ * set to true
if this option requires a start date to be indicated
+ * for the underlying resource policy to create
+ */
private Boolean hasStartDate;
+ /**
+ * set to true
if this option requires an end date to be indicated
+ * for the underlying resource policy to create
+ */
private Boolean hasEndDate;
+ /**
+ * It contains, if applicable, the maximum start date (i.e. when the "embargo
+ * expires") that can be selected. It accepts date math via joda library (such as
+ * +3years)
+ */
private String startDateLimit;
+ /**
+ * It contains, if applicable, the maximum end date (i.e. when the "lease
+ * expires") that can be selected. It accepts date math via joda library (such as
+ * +3years)
+ */
private String endDateLimit;
public String getName() {
@@ -81,6 +118,4 @@ public class AccessConditionOption {
public void setSelectGroupName(String selectGroupName) {
this.selectGroupName = selectGroupName;
}
-
-
}
diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml
new file mode 100644
index 0000000000..b734c78937
--- /dev/null
+++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java
index 1738373420..d668261a3c 100644
--- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java
+++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java
@@ -22,7 +22,7 @@ import org.dspace.core.ConfigurationManager;
public class DSpaceResourceResolver implements ResourceResolver {
private static final TransformerFactory transformerFactory = TransformerFactory
- .newInstance();
+ .newInstance("net.sf.saxon.TransformerFactoryImpl", null);
private final String basePath = ConfigurationManager.getProperty("oai",
"config.dir");
diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java
index 2262c1bbe8..6fab56b526 100644
--- a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java
+++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java
@@ -19,7 +19,8 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.IOUtils;
public abstract class AbstractXSLTest {
- private static final TransformerFactory factory = TransformerFactory.newInstance();
+ private static final TransformerFactory factory = TransformerFactory
+ .newInstance("net.sf.saxon.TransformerFactoryImpl", null);
protected TransformBuilder apply(String xslLocation) throws Exception {
return new TransformBuilder(xslLocation);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java
index 42ae152461..cc7459955f 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java
@@ -10,6 +10,7 @@ package org.dspace.app.rest.converter;
import org.dspace.app.rest.model.ClaimedTaskRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.discovery.IndexableObject;
+import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.springframework.beans.factory.annotation.Autowired;
@@ -28,6 +29,9 @@ public class ClaimedTaskConverter
@Autowired
private ConverterService converter;
+ @Autowired
+ protected XmlWorkflowFactory xmlWorkflowFactory;
+
@Override
public ClaimedTaskRest convert(ClaimedTask obj, Projection projection) {
ClaimedTaskRest taskRest = new ClaimedTaskRest();
@@ -35,8 +39,7 @@ public class ClaimedTaskConverter
XmlWorkflowItem witem = obj.getWorkflowItem();
taskRest.setId(obj.getID());
taskRest.setWorkflowitem(converter.toRest(witem, projection));
- taskRest.setAction(obj.getActionID());
- taskRest.setStep(obj.getStepID());
+ taskRest.setAction(converter.toRest(xmlWorkflowFactory.getActionByName(obj.getActionID()), projection));
taskRest.setOwner(converter.toRest(obj.getOwner(), projection));
return taskRest;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java
index 9e4f6c00f1..295634599b 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java
@@ -32,6 +32,7 @@ import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
+import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@@ -155,9 +156,30 @@ public class ConverterService {
* @throws ClassCastException if the resource type is not compatible with the inferred return type.
*/
public T toResource(RestModel restObject) {
+ return toResource(restObject, new Link[] {});
+ }
+
+ /**
+ * Converts the given rest object to a {@link HALResource} object.
+ *
+ * If the rest object is a {@link RestAddressableModel}, the projection returned by
+ * {@link RestAddressableModel#getProjection()} will be used to determine which optional
+ * embeds and links will be added, and {@link Projection#transformResource(HALResource)}
+ * will be automatically called before returning the final, fully converted resource.
+ *
+ * In all cases, the {@link HalLinkService} will be used immediately after the resource is constructed,
+ * to ensure all {@link HalLinkFactory}s have had a chance to add links as needed.
+ *
+ *
+ * @param restObject the input rest object.
+ * @param oldLinks The old links fo the Resource Object
+ * @param the return type, a subclass of {@link HALResource}.
+ * @return the fully converted resource, with all automatic links and embeds applied.
+ */
+ public T toResource(RestModel restObject, Link... oldLinks) {
T halResource = getResource(restObject);
if (restObject instanceof RestAddressableModel) {
- utils.embedOrLinkClassLevelRels(halResource);
+ utils.embedOrLinkClassLevelRels(halResource, oldLinks);
halLinkService.addLinks(halResource);
Projection projection = ((RestAddressableModel) restObject).getProjection();
return projection.transformResource(halResource);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java
index 7a58fda864..48299dd362 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java
@@ -43,7 +43,6 @@ public class PoolTaskConverter
taskRest.setGroup(converter.toRest(obj.getGroup(), projection));
}
taskRest.setAction(obj.getActionID());
- taskRest.setStep(obj.getStepID());
return taskRest;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java
index 1e7834d7ed..ab8694874c 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java
@@ -26,12 +26,6 @@ public class ResourcePolicyConverter implements DSpaceConverter {
public static final String NAME = "claimedtask";
public static final String CATEGORY = RestAddressableModel.WORKFLOW;
- private String step;
+ public static final String STEP = "step";
- private String action;
+ @JsonIgnore
+ private WorkflowActionRest action;
@JsonIgnore
private EPersonRest owner;
@@ -45,27 +51,15 @@ public class ClaimedTaskRest extends BaseObjectRest {
return RestResourceController.class;
}
- /**
- * @see ClaimedTask#getStepID()
- * @return the step
- */
- public String getStep() {
- return step;
- }
-
- public void setStep(String step) {
- this.step = step;
- }
-
/**
* @see ClaimedTaskRest#getAction()
* @return the action
*/
- public String getAction() {
+ public WorkflowActionRest getAction() {
return action;
}
- public void setAction(String action) {
+ public void setAction(WorkflowActionRest action) {
this.action = action;
}
@@ -82,7 +76,7 @@ public class ClaimedTaskRest extends BaseObjectRest {
}
/**
- *
+ *
* @return the WorkflowItemRest that belong to this claimed task
*/
public WorkflowItemRest getWorkflowitem() {
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java
index 6ab445659e..b3dedd23b9 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java
@@ -16,11 +16,17 @@ import org.dspace.xmlworkflow.storedcomponents.PoolTask;
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
+@LinksRest(links = {
+ @LinkRest(
+ name = PoolTaskRest.STEP,
+ method = "getStep"
+ )
+})
public class PoolTaskRest extends BaseObjectRest {
public static final String NAME = "pooltask";
public static final String CATEGORY = RestAddressableModel.WORKFLOW;
- private String step;
+ public static final String STEP = "step";
private String action;
@@ -48,18 +54,6 @@ public class PoolTaskRest extends BaseObjectRest {
return RestResourceController.class;
}
- /**
- * @see PoolTask#getStepID()
- * @return
- */
- public String getStep() {
- return step;
- }
-
- public void setStep(String step) {
- this.step = step;
- }
-
/**
* @see PoolTask#getActionID()
* @return
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java
index 606bd8c273..656d9049fa 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest.model;
import java.util.Date;
-import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -34,12 +33,6 @@ public class ResourcePolicyRest extends BaseObjectRest {
private String description;
- @JsonInclude(Include.NON_NULL)
- private UUID groupUUID;
-
- @JsonInclude(Include.NON_NULL)
- private UUID epersonUUID;
-
@JsonIgnore
private EPersonRest eperson;
@@ -55,14 +48,6 @@ public class ResourcePolicyRest extends BaseObjectRest {
private Date endDate;
- public UUID getGroupUUID() {
- return groupUUID;
- }
-
- public void setGroupUUID(UUID groupUuid) {
- this.groupUUID = groupUuid;
- }
-
public Date getEndDate() {
return endDate;
}
@@ -111,14 +96,6 @@ public class ResourcePolicyRest extends BaseObjectRest {
this.description = description;
}
- public UUID getEpersonUUID() {
- return epersonUUID;
- }
-
- public void setEpersonUUID(UUID epersonUUID) {
- this.epersonUUID = epersonUUID;
- }
-
public EPersonRest getEperson() {
return eperson;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/UploadBitstreamAccessConditionDTO.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/UploadBitstreamAccessConditionDTO.java
new file mode 100644
index 0000000000..120cb6a9d2
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/UploadBitstreamAccessConditionDTO.java
@@ -0,0 +1,101 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import java.util.Date;
+import java.util.UUID;
+
+import org.dspace.app.rest.model.step.UploadBitstreamRest;
+
+/**
+ * The UploadAccessConditionDTO is a partial representation of the DSpace
+ * {@link ResourcePolicyRest} as used in the patch payload for the upload
+ * submission section (see {@link UploadBitstreamRest}. The main reason for this
+ * class is to have a DTO to use serialize/deserialize the REST model, that
+ * include reference to the GroupRest and EPersonRest object, in the upload
+ * section data in a simpler way where such reference are just UUID. Indeed, due
+ * to the fact that the RestModel class are serialized according to the HAL
+ * format and the reference are only exposed in the _links section of the
+ * RestResource it was not possible to use the {@link ResourcePolicyRest} class
+ * directly in the upload section
+ *
+ * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
+ */
+public class UploadBitstreamAccessConditionDTO {
+
+ private Integer id;
+
+ private UUID groupUUID;
+
+ private UUID epersonUUID;
+
+ private String name;
+
+ private String description;
+
+ private Date startDate;
+
+ private Date endDate;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public UUID getGroupUUID() {
+ return groupUUID;
+ }
+
+ public void setGroupUUID(UUID groupUUID) {
+ this.groupUUID = groupUUID;
+ }
+
+ public UUID getEpersonUUID() {
+ return epersonUUID;
+ }
+
+ public void setEpersonUUID(UUID epersonUUID) {
+ this.epersonUUID = epersonUUID;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java
index bf00ba0e4f..8f580f4414 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java
@@ -14,10 +14,18 @@ import org.dspace.app.rest.RestResourceController;
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
+@LinksRest(links = {
+ @LinkRest(
+ name = WorkflowItemRest.STEP,
+ method = "getStep"
+ )
+})
public class WorkflowItemRest extends AInprogressSubmissionRest {
public static final String NAME = "workflowitem";
public static final String CATEGORY = RestAddressableModel.WORKFLOW;
+ public static final String STEP = "step";
+
@Override
public String getCategory() {
return CATEGORY;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java
index 80e36fdb59..7024e5cdb1 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest.model.step;
import java.util.ArrayList;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -16,13 +17,19 @@ import java.util.UUID;
import org.dspace.app.rest.model.BitstreamFormatRest;
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.UploadBitstreamAccessConditionDTO;
+/**
+ * This Java Bean is used to represent a single bitstream with all its metadata
+ * and access conditions ({@link UploadBitstreamAccessConditionDTO}) inside an
+ * upload submission section ({@link DataUpload}
+ *
+ */
public class UploadBitstreamRest extends UploadStatusResponse {
private UUID uuid;
private Map> metadata = new HashMap<>();
- private List accessConditions;
+ private List accessConditions;
private BitstreamFormatRest format;
private Long sizeBytes;
private CheckSumRest checkSum;
@@ -68,14 +75,14 @@ public class UploadBitstreamRest extends UploadStatusResponse {
this.metadata = metadata;
}
- public List getAccessConditions() {
+ public List getAccessConditions() {
if (accessConditions == null) {
accessConditions = new ArrayList<>();
}
return accessConditions;
}
- public void setAccessConditions(List accessConditions) {
+ public void setAccessConditions(List accessConditions) {
this.accessConditions = accessConditions;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java
index 12a492191d..bbf38fd97f 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java
@@ -8,8 +8,10 @@
package org.dspace.app.rest.projection;
import org.dspace.app.rest.model.LinkRest;
+import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource;
+import org.springframework.hateoas.Link;
/**
* Abstract base class for projections.
@@ -34,7 +36,8 @@ public abstract class AbstractProjection implements Projection {
}
@Override
- public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) {
+ public boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks) {
return false;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/CompositeProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/CompositeProjection.java
new file mode 100644
index 0000000000..d9a28cec04
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/CompositeProjection.java
@@ -0,0 +1,84 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.projection;
+
+import java.util.List;
+
+import org.dspace.app.rest.model.LinkRest;
+import org.dspace.app.rest.model.RestAddressableModel;
+import org.dspace.app.rest.model.RestModel;
+import org.dspace.app.rest.model.hateoas.HALResource;
+import org.springframework.hateoas.Link;
+
+/**
+ * A projection that combines the behavior of multiple projections.
+ *
+ * Model, rest, and resource transformations will be performed in the order of the projections given in
+ * the constructor. Embedding will be allowed if any of the given projections allow them. Linking will
+ * be allowed if all of the given projections allow them.
+ */
+public class CompositeProjection implements Projection {
+
+ public final static String NAME = "composite";
+
+ private final List projections;
+
+ public CompositeProjection(List projections) {
+ this.projections = projections;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public T transformModel(T modelObject) {
+ for (Projection projection : projections) {
+ modelObject = projection.transformModel(modelObject);
+ }
+ return modelObject;
+ }
+
+ @Override
+ public T transformRest(T restObject) {
+ for (Projection projection : projections) {
+ restObject = projection.transformRest(restObject);
+ }
+ return restObject;
+ }
+
+ @Override
+ public T transformResource(T halResource) {
+ for (Projection projection : projections) {
+ halResource = projection.transformResource(halResource);
+ }
+ return halResource;
+ }
+
+ @Override
+ public boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks) {
+ for (Projection projection : projections) {
+ if (projection.allowEmbedding(halResource, linkRest, oldLinks)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean allowLinking(HALResource halResource, LinkRest linkRest) {
+ for (Projection projection : projections) {
+ if (!projection.allowLinking(halResource, linkRest)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/EmbedRelsProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/EmbedRelsProjection.java
new file mode 100644
index 0000000000..1db0c6a74d
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/EmbedRelsProjection.java
@@ -0,0 +1,65 @@
+/**
+ * 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.projection;
+
+import java.util.Set;
+
+import org.dspace.app.rest.model.LinkRest;
+import org.dspace.app.rest.model.RestAddressableModel;
+import org.dspace.app.rest.model.hateoas.HALResource;
+import org.springframework.hateoas.Link;
+
+/**
+ * Projection that allows a given set of rels to be embedded.
+ * A Rel refers to a Link Relation, this is an Embedded Object of the HalResource and the HalResource contains
+ * a link to this
+ */
+public class EmbedRelsProjection extends AbstractProjection {
+
+ public final static String NAME = "embedrels";
+
+ private final Set embedRels;
+
+ public EmbedRelsProjection(Set embedRels) {
+ this.embedRels = embedRels;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks) {
+ // If level 0, and the name is present, the link can be embedded (e.g. the logo on a collection page)
+ if (halResource.getContent().getEmbedLevel() == 0 && embedRels.contains(linkRest.name())) {
+ return true;
+ }
+
+ StringBuilder fullName = new StringBuilder();
+ for (Link oldLink : oldLinks) {
+ fullName.append(oldLink.getRel()).append("/");
+ }
+ fullName.append(linkRest.name());
+ // If the full name matches, the link can be embedded (e.g. mappedItems/owningCollection on a collection page)
+ if (embedRels.contains(fullName.toString())) {
+ return true;
+ }
+
+ fullName.append("/");
+ // If the full name starts with the allowed embed, but the embed goes deeper, the link can be embedded
+ // (e.g. making sure mappedItems/owningCollection also embeds mappedItems on a collection page)
+ for (String embedRel : embedRels) {
+ if (embedRel.startsWith(fullName.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java
index 9c5a4a9f9e..99719c8ac3 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java
@@ -8,7 +8,10 @@
package org.dspace.app.rest.projection;
import org.dspace.app.rest.model.LinkRest;
+import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.hateoas.HALResource;
+import org.dspace.services.factory.DSpaceServicesFactory;
+import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
/**
@@ -18,14 +21,17 @@ import org.springframework.stereotype.Component;
public class FullProjection extends AbstractProjection {
public final static String NAME = "full";
+ private final int maxEmbed = DSpaceServicesFactory.getInstance().getConfigurationService()
+ .getIntProperty("rest.projections.full.max", 2);
public String getName() {
return NAME;
}
@Override
- public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) {
- return true;
+ public boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks) {
+ return halResource.getContent().getEmbedLevel() < maxEmbed;
}
@Override
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java
index d9e0b10261..cb16093464 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java
@@ -10,9 +10,12 @@ package org.dspace.app.rest.projection;
import javax.persistence.Entity;
import org.dspace.app.rest.model.LinkRest;
+import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.dspace.app.rest.repository.DSpaceRestRepository;
+import org.dspace.app.rest.utils.Utils;
+import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
@@ -44,7 +47,7 @@ import org.springframework.web.bind.annotation.RestController;
* After it is converted to a {@link RestModel}, the projection may modify it
* via {@link #transformRest(RestModel)}.
* During conversion to a {@link HALResource}, the projection may opt in to certain annotation-discovered
- * HAL embeds and links via {@link #allowEmbedding(HALResource, LinkRest)}
+ * HAL embeds and links via {@link #allowEmbedding(HALResource, LinkRest, Link...)}
* and {@link #allowLinking(HALResource, LinkRest)}
* After conversion to a {@link HALResource}, the projection may modify it
* via {@link #transformResource(HALResource)}.
@@ -52,8 +55,7 @@ import org.springframework.web.bind.annotation.RestController;
*
* How a projection is chosen
*
- * When a REST request is made, the projection argument, if present, is used to look up the projection to use,
- * by name. If no argument is present, {@link DefaultProjection} will be used.
+ * See {@link Utils#obtainProjection()}.
*/
public interface Projection {
@@ -115,16 +117,18 @@ public interface Projection {
*
* @param halResource the resource from which the embed may or may not be made.
* @param linkRest the LinkRest annotation through which the related resource was discovered on the rest object.
+ * @param oldLinks The previously traversed links
* @return true if allowed, false otherwise.
*/
- boolean allowEmbedding(HALResource halResource, LinkRest linkRest);
+ boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks);
/**
* Tells whether this projection permits the linking of a particular linkable subresource.
*
* This gives the projection an opportunity to opt in to to certain links, by returning {@code true}.
*
- * Note: If {@link #allowEmbedding(HALResource, LinkRest)} returns {@code true} for a given subresource,
+ * Note: If {@link #allowEmbedding(HALResource, LinkRest, Link...)} returns {@code true} for a given subresource,
* it will be automatically linked regardless of what this method returns.
*
* @param halResource the resource from which the link may or may not be made.
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/SpecificLevelProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/SpecificLevelProjection.java
new file mode 100644
index 0000000000..7ef603b23c
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/SpecificLevelProjection.java
@@ -0,0 +1,69 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.projection;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.exception.MissingParameterException;
+import org.dspace.app.rest.model.LinkRest;
+import org.dspace.app.rest.model.RestAddressableModel;
+import org.dspace.app.rest.model.hateoas.HALResource;
+import org.dspace.services.RequestService;
+import org.dspace.services.factory.DSpaceServicesFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.hateoas.Link;
+
+/**
+ * This Projection will allow us to specify how many levels deep we're going to embed resources onto the requested
+ * HalResource.
+ * The projection is used by using the name combined with the embedLevelDepth parameter to specify how deep the embeds
+ * have to go. There is an upperlimit in place for this, which is specified on the bean through the maxEmbed property
+ */
+public class SpecificLevelProjection extends AbstractProjection {
+
+ @Autowired
+ private RequestService requestService;
+
+ public final static String NAME = "level";
+
+ private int maxEmbed = DSpaceServicesFactory.getInstance().getConfigurationService()
+ .getIntProperty("rest.projections.full.max", 2);
+
+ public int getMaxEmbed() {
+ return maxEmbed;
+ }
+
+ public void setMaxEmbed(int maxEmbed) {
+ this.maxEmbed = maxEmbed;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks) {
+ String embedLevelDepthString = requestService.getCurrentRequest().getHttpServletRequest()
+ .getParameter("embedLevelDepth");
+ if (StringUtils.isBlank(embedLevelDepthString)) {
+ throw new MissingParameterException("The embedLevelDepth parameter needs to be specified" +
+ " for this Projection");
+ }
+ Integer embedLevelDepth = Integer.parseInt(embedLevelDepthString);
+ if (embedLevelDepth > maxEmbed) {
+ throw new IllegalArgumentException("The embedLevelDepth may not exceed the configured max: " + maxEmbed);
+ }
+ return halResource.getContent().getEmbedLevel() < embedLevelDepth;
+ }
+
+ @Override
+ public boolean allowLinking(HALResource halResource, LinkRest linkRest) {
+ return true;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskStepLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskStepLinkRepository.java
new file mode 100644
index 0000000000..9ee277171e
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskStepLinkRepository.java
@@ -0,0 +1,63 @@
+/**
+ * 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;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.model.ClaimedTaskRest;
+import org.dspace.app.rest.model.WorkflowStepRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
+import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
+import org.dspace.xmlworkflow.storedcomponents.service.ClaimedTaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repository for the Steps subresources for an individual ClaimedTask
+ */
+@Component(ClaimedTaskRest.CATEGORY + "." + ClaimedTaskRest.NAME + "." + ClaimedTaskRest.STEP)
+public class ClaimedTaskStepLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
+
+ @Autowired
+ private ClaimedTaskService claimedTaskService;
+
+ @Autowired
+ private XmlWorkflowFactory xmlWorkflowFactory;
+
+ /**
+ * This method will retrieve the {@link WorkflowStepRest} object for the {@link ClaimedTask} with the given id
+ * @param request The current request
+ * @param claimedTaskId The id for the ClaimedTask to be used
+ * @param optionalPageable The pageable if relevant
+ * @param projection The Projection
+ * @return The {@link WorkflowStepRest} object related to the {@link ClaimedTask} specified by
+ * the given ID
+ */
+ public WorkflowStepRest getStep(@Nullable HttpServletRequest request,
+ Integer claimedTaskId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+
+ Context context = obtainContext();
+ try {
+ ClaimedTask claimedTask = claimedTaskService.find(context, claimedTaskId);
+ if (claimedTask == null) {
+ throw new ResourceNotFoundException("ClaimedTask with id: " + claimedTaskId + " wasn't found");
+ }
+ return converter.toRest(xmlWorkflowFactory.getStepByName(claimedTask.getStepID()), projection);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PoolTaskStepLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PoolTaskStepLinkRepository.java
new file mode 100644
index 0000000000..6e7f4f84ac
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PoolTaskStepLinkRepository.java
@@ -0,0 +1,63 @@
+/**
+ * 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;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.model.PoolTaskRest;
+import org.dspace.app.rest.model.WorkflowStepRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
+import org.dspace.xmlworkflow.storedcomponents.PoolTask;
+import org.dspace.xmlworkflow.storedcomponents.service.PoolTaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repositoy for the Steps subresources of an individual PoolTask
+ */
+@Component(PoolTaskRest.CATEGORY + "." + PoolTaskRest.NAME + "." + PoolTaskRest.STEP)
+public class PoolTaskStepLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
+
+ @Autowired
+ private PoolTaskService poolTaskService;
+
+ @Autowired
+ private XmlWorkflowFactory xmlWorkflowFactory;
+
+ /**
+ * This method will retrieve the {@link WorkflowStepRest} object for the {@link PoolTask} with the given id
+ * @param request The current request
+ * @param poolTaskId The id for the PoolTask to be used
+ * @param optionalPageable The pageable if relevant
+ * @param projection The Projection
+ * @return The {@link WorkflowStepRest} object related to the {@link PoolTask} specified by
+ * the given ID
+ */
+ public WorkflowStepRest getStep(@Nullable HttpServletRequest request,
+ Integer poolTaskId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+
+ Context context = obtainContext();
+ try {
+ PoolTask poolTask = poolTaskService.find(context, poolTaskId);
+ if (poolTask == null) {
+ throw new ResourceNotFoundException("ClaimedTask with id: " + poolTaskId + " wasn't found");
+ }
+ return converter.toRest(xmlWorkflowFactory.getStepByName(poolTask.getStepID()), projection);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemStepLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemStepLinkRepository.java
new file mode 100644
index 0000000000..30aac1579c
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemStepLinkRepository.java
@@ -0,0 +1,86 @@
+/**
+ * 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;
+
+import java.sql.SQLException;
+import java.util.List;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.model.WorkflowItemRest;
+import org.dspace.app.rest.model.WorkflowStepRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
+import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
+import org.dspace.xmlworkflow.storedcomponents.PoolTask;
+import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
+import org.dspace.xmlworkflow.storedcomponents.service.ClaimedTaskService;
+import org.dspace.xmlworkflow.storedcomponents.service.PoolTaskService;
+import org.dspace.xmlworkflow.storedcomponents.service.XmlWorkflowItemService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link Repository for the Steps subresources of an individual WorkflowItem
+ */
+@Component(WorkflowItemRest.CATEGORY + "." + WorkflowItemRest.NAME + "." + WorkflowItemRest.STEP)
+public class WorkflowItemStepLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
+
+ @Autowired
+ private XmlWorkflowItemService xmlWorkflowItemService;
+
+ @Autowired
+ private PoolTaskService poolTaskService;
+
+ @Autowired
+ private ClaimedTaskService claimedTaskService;
+
+ @Autowired
+ private XmlWorkflowFactory xmlWorkflowFactory;
+
+ /**
+ * This method will retrieve the {@link WorkflowStepRest} object for the {@link org.dspace.workflow.WorkflowItem}
+ * with the given id
+ * @param request The current request
+ * @param workflowItemId The id for the WorkflowItem to be used
+ * @param optionalPageable The pageable if relevant
+ * @param projection The Projection
+ * @return The {@link WorkflowStepRest} object related to the
+ * {@link org.dspace.workflow.WorkflowItem} specified by the given ID
+ */
+ public WorkflowStepRest getStep(@Nullable HttpServletRequest request,
+ Integer workflowItemId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+
+ Context context = obtainContext();
+ try {
+ XmlWorkflowItem xmlWorkflowItem = xmlWorkflowItemService.find(context, workflowItemId);
+ if (xmlWorkflowItem == null) {
+ throw new ResourceNotFoundException("XmlWorkflowItem with id: " + workflowItemId + " wasn't found");
+ }
+ List poolTasks = poolTaskService.find(context, xmlWorkflowItem);
+ List claimedTasks = claimedTaskService.find(context, xmlWorkflowItem);
+ for (PoolTask poolTask : poolTasks) {
+ return converter.toRest(xmlWorkflowFactory.getStepByName(poolTask.getStepID()), projection);
+ }
+ for (ClaimedTask claimedTask : claimedTasks) {
+ return converter.toRest(xmlWorkflowFactory.getStepByName(claimedTask.getStepID()), projection);
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ throw new ResourceNotFoundException("No workflowStep for this workflowItem with id: " + workflowItemId +
+ " was found");
+
+
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AuthorizeServicePermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AuthorizeServicePermissionEvaluatorPlugin.java
index c5e9541976..e0e5b880fa 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AuthorizeServicePermissionEvaluatorPlugin.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AuthorizeServicePermissionEvaluatorPlugin.java
@@ -85,7 +85,7 @@ public class AuthorizeServicePermissionEvaluatorPlugin extends RestObjectPermiss
}
return authorizeService.authorizeActionBoolean(context, ePerson, dSpaceObject,
- restPermission.getDspaceApiActionId(), false);
+ restPermission.getDspaceApiActionId(), true);
}
} catch (SQLException e) {
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java
index 6a4ed0b542..d8a86fa41a 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest.submit;
import java.io.IOException;
+
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@@ -23,8 +24,9 @@ import org.dspace.app.rest.exception.UnprocessableEntityException;
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.UploadBitstreamAccessConditionDTO;
import org.dspace.app.rest.model.WorkspaceItemRest;
+import org.dspace.app.rest.model.step.DataUpload;
import org.dspace.app.rest.model.step.UploadBitstreamRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
@@ -81,7 +83,7 @@ public class SubmissionService {
private org.dspace.app.rest.utils.Utils utils;
/**
- * Create a workspaceitem using the information in the reqest
+ * Create a workspaceitem using the information in the request
*
* @param context
* the dspace context
@@ -135,7 +137,17 @@ public class SubmissionService {
}
}
-
+/**
+ * Build the rest representation of a bitstream as used in the upload section
+ * ({@link DataUpload}. It contains all its metadata and the list of applied
+ * access conditions (@link {@link UploadBitstreamAccessConditionDTO}
+ *
+ * @param configurationService the DSpace ConfigurationService
+ * @param source the bitstream to translate in its rest submission
+ * representation
+ * @return
+ * @throws SQLException
+ */
public UploadBitstreamRest buildUploadBitstream(ConfigurationService configurationService, Bitstream source)
throws SQLException {
UploadBitstreamRest data = new UploadBitstreamRest();
@@ -170,8 +182,8 @@ public class SubmissionService {
for (ResourcePolicy rp : source.getResourcePolicies()) {
if (ResourcePolicy.TYPE_CUSTOM.equals(rp.getRpType())) {
- ResourcePolicyRest resourcePolicyRest = converter.toRest(rp, projection);
- data.getAccessConditions().add(resourcePolicyRest);
+ UploadBitstreamAccessConditionDTO uploadAccessCondition = createAccessConditionFromResourcePolicy(rp);
+ data.getAccessConditions().add(uploadAccessCondition);
}
}
@@ -187,7 +199,7 @@ public class SubmissionService {
}
/**
- * Create a workflowitem using the information in the reqest
+ * Create a workflowitem using the information in the request
*
* @param context
* the dspace context
@@ -237,6 +249,23 @@ public class SubmissionService {
return wi;
}
+ private UploadBitstreamAccessConditionDTO createAccessConditionFromResourcePolicy(ResourcePolicy rp) {
+ UploadBitstreamAccessConditionDTO accessCondition = new UploadBitstreamAccessConditionDTO();
+
+ accessCondition.setId(rp.getID());
+ accessCondition.setName(rp.getRpName());
+ accessCondition.setDescription(rp.getRpDescription());
+ accessCondition.setStartDate(rp.getStartDate());
+ accessCondition.setEndDate(rp.getEndDate());
+ if (rp.getGroup() != null) {
+ accessCondition.setGroupUUID(rp.getGroup().getID());
+ }
+ if (rp.getEPerson() != null) {
+ accessCondition.setEpersonUUID(rp.getEPerson().getID());
+ }
+ return accessCondition;
+ }
+
public void saveWorkflowItem(Context context, XmlWorkflowItem source) throws SQLException, AuthorizeException {
workflowItemService.update(context, source);
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java
similarity index 83%
rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyAddPatchOperation.java
rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java
index 7367ab2bd1..05e7c5052a 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyAddPatchOperation.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java
@@ -8,10 +8,11 @@
package org.dspace.app.rest.submit.factory.impl;
import java.util.ArrayList;
+
import java.util.Date;
import java.util.List;
-import org.dspace.app.rest.model.ResourcePolicyRest;
+import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO;
import org.dspace.app.rest.model.patch.LateObjectEvaluator;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
@@ -35,7 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired;
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
-public class ResourcePolicyAddPatchOperation extends AddPatchOperation {
+public class BitstreamResourcePolicyAddPatchOperation extends AddPatchOperation {
@Autowired
BitstreamService bitstreamService;
@@ -48,6 +49,7 @@ public class ResourcePolicyAddPatchOperation extends AddPatchOperation newAccessConditions = new ArrayList();
+ List newAccessConditions =
+ new ArrayList();
if (split.length == 3) {
authorizeService.removePoliciesActionFilter(context, b, Constants.READ);
newAccessConditions = evaluateArrayObject((LateObjectEvaluator) value);
@@ -74,7 +77,7 @@ public class ResourcePolicyAddPatchOperation extends AddPatchOperation getArrayClassForEvaluation() {
- return ResourcePolicyRest[].class;
+ protected Class getArrayClassForEvaluation() {
+ return UploadBitstreamAccessConditionDTO[].class;
}
@Override
- protected Class getClassForEvaluation() {
- return ResourcePolicyRest.class;
+ protected Class getClassForEvaluation() {
+ return UploadBitstreamAccessConditionDTO.class;
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyRemovePatchOperation.java
similarity index 86%
rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyRemovePatchOperation.java
rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyRemovePatchOperation.java
index 18b15dfba0..fcf96578a1 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyRemovePatchOperation.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyRemovePatchOperation.java
@@ -9,7 +9,7 @@ package org.dspace.app.rest.submit.factory.impl;
import java.util.List;
-import org.dspace.app.rest.model.ResourcePolicyRest;
+import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Bitstream;
@@ -28,7 +28,8 @@ import org.springframework.beans.factory.annotation.Autowired;
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
-public class ResourcePolicyRemovePatchOperation extends RemovePatchOperation {
+public class BitstreamResourcePolicyRemovePatchOperation
+ extends RemovePatchOperation {
@Autowired
ItemService itemService;
@@ -83,12 +84,12 @@ public class ResourcePolicyRemovePatchOperation extends RemovePatchOperation getArrayClassForEvaluation() {
- return ResourcePolicyRest[].class;
+ protected Class getArrayClassForEvaluation() {
+ return UploadBitstreamAccessConditionDTO[].class;
}
@Override
- protected Class getClassForEvaluation() {
- return ResourcePolicyRest.class;
+ protected Class getClassForEvaluation() {
+ return UploadBitstreamAccessConditionDTO.class;
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyReplacePatchOperation.java
similarity index 88%
rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyReplacePatchOperation.java
rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyReplacePatchOperation.java
index c03ea98271..7f0a23dbb9 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyReplacePatchOperation.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyReplacePatchOperation.java
@@ -9,6 +9,7 @@ package org.dspace.app.rest.submit.factory.impl;
import java.util.Date;
import java.util.List;
+import java.util.UUID;
import org.dspace.app.rest.model.ResourcePolicyRest;
import org.dspace.app.rest.model.patch.LateObjectEvaluator;
@@ -35,7 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired;
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
-public class ResourcePolicyReplacePatchOperation extends ReplacePatchOperation {
+public class BitstreamResourcePolicyReplacePatchOperation extends ReplacePatchOperation {
@Autowired
BitstreamService bitstreamService;
@@ -89,12 +90,14 @@ public class ResourcePolicyReplacePatchOperation extends ReplacePatchOperation
+ * Any number of individual {@code Projections} that are spring-registered {@link Component}s may be specified
+ * by name via the {@code projection} parameter. If multiple projections are specified, they will be wrapped in a
+ * {@link CompositeProjection} and applied in order as described there.
+ *
+ * In addition, any number of embeds may be specified by rel name via the {@code embed} parameter.
+ * When provided, these act as a whitelist of embeds that may be included in the response, as described
+ * and implemented by {@link EmbedRelsProjection}.
+ *
*
* @return the requested or default projection, never {@code null}.
* @throws IllegalArgumentException if the request specifies an unknown projection name.
*/
public Projection obtainProjection() {
- String projectionName = requestService.getCurrentRequest().getServletRequest().getParameter("projection");
- return converter.getProjection(projectionName);
+ ServletRequest servletRequest = requestService.getCurrentRequest().getServletRequest();
+ List projectionNames = getValues(servletRequest, "projection");
+ Set embedRels = new HashSet<>(getValues(servletRequest, "embed"));
+
+ List projections = new ArrayList<>();
+
+ for (String projectionName : projectionNames) {
+ projections.add(converter.getProjection(projectionName));
+ }
+
+ if (!embedRels.isEmpty()) {
+ projections.add(new EmbedRelsProjection(embedRels));
+ }
+
+ if (projections.isEmpty()) {
+ return Projection.DEFAULT;
+ } else if (projections.size() == 1) {
+ return projections.get(0);
+ } else {
+ return new CompositeProjection(projections);
+ }
+ }
+
+ /**
+ * Gets zero or more values for the given servlet request parameter.
+ *
+ * This convenience method reads multiple values that have been specified as request parameter in multiple ways:
+ * via * {@code ?paramName=value1¶mName=value2}, via {@code ?paramName=value1,value2},
+ * or a combination.
+ *
+ * It provides the values in the order they were given in the request, and automatically de-dupes them.
+ *
+ *
+ * @param servletRequest the servlet request.
+ * @param parameterName the parameter name.
+ * @return the ordered, de-duped values, possibly empty, never {@code null}.
+ */
+ private List getValues(ServletRequest servletRequest, String parameterName) {
+ String[] rawValues = servletRequest.getParameterValues(parameterName);
+ List values = new ArrayList<>();
+ if (rawValues != null) {
+ for (String rawValue : rawValues) {
+ for (String value : rawValue.split(",")) {
+ String trimmedValue = value.trim();
+ if (trimmedValue.length() > 0 && !values.contains(trimmedValue)) {
+ values.add(trimmedValue);
+ }
+ }
+ }
+ }
+ return values;
}
/**
* Adds embeds or links for all class-level LinkRel annotations for which embeds or links are allowed.
*
* @param halResource the resource.
+ * @param oldLinks previously traversed links
*/
- public void embedOrLinkClassLevelRels(HALResource halResource) {
+ public void embedOrLinkClassLevelRels(HALResource halResource, Link... oldLinks) {
Projection projection = halResource.getContent().getProjection();
getLinkRests(halResource.getContent().getClass()).stream().forEach((linkRest) -> {
Link link = linkToSubResource(halResource.getContent(), linkRest.name());
- if (projection.allowEmbedding(halResource, linkRest)) {
- embedRelFromRepository(halResource, linkRest.name(), link, linkRest);
+ if (projection.allowEmbedding(halResource, linkRest, oldLinks)) {
+ embedRelFromRepository(halResource, linkRest.name(), link, linkRest, oldLinks);
halResource.add(link); // unconditionally link if embedding was allowed
} else if (projection.allowLinking(halResource, linkRest)) {
halResource.add(link);
@@ -503,6 +566,32 @@ public class Utils {
*/
void embedRelFromRepository(HALResource extends RestAddressableModel> resource,
String rel, Link link, LinkRest linkRest) {
+ embedRelFromRepository(resource, rel, link, linkRest, new Link[] {});
+ }
+
+ /**
+ * Embeds a rel whose value comes from a {@link LinkRestRepository}, if the maximum embed level has not
+ * been exceeded yet.
+ *
+ * The embed will be skipped if 1) the link repository reports that it is not embeddable or 2) the returned
+ * value is null and the LinkRest annotation has embedOptional = true.
+ *
+ * Implementation note: The caller is responsible for ensuring that the projection allows the embed
+ * before calling this method.
+ *
+ *
+ * @param resource the resource from which the embed will be made.
+ * @param rel the name of the rel.
+ * @param link the link.
+ * @param linkRest the LinkRest annotation (must have method defined).
+ * @param oldLinks The previously traversed links
+ * @throws RepositoryNotFoundException if the link repository could not be found.
+ * @throws IllegalArgumentException if the method specified by the LinkRest could not be found in the
+ * link repository.
+ * @throws RuntimeException if any other problem occurs when trying to invoke the method.
+ */
+ void embedRelFromRepository(HALResource extends RestAddressableModel> resource,
+ String rel, Link link, LinkRest linkRest, Link... oldLinks) {
if (resource.getContent().getEmbedLevel() == EMBED_MAX_LEVELS) {
return;
}
@@ -514,7 +603,7 @@ public class Utils {
Object contentId = getContentIdForLinkMethod(resource.getContent(), method);
try {
Object linkedObject = method.invoke(linkRepository, null, contentId, null, projection);
- resource.embedResource(rel, wrapForEmbedding(resource, linkedObject, link));
+ resource.embedResource(rel, wrapForEmbedding(resource, linkedObject, link, oldLinks));
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
@@ -619,17 +708,37 @@ public class Utils {
*/
private Object wrapForEmbedding(HALResource extends RestAddressableModel> resource,
Object linkedObject, Link link) {
+ return wrapForEmbedding(resource, linkedObject, link, new Link[] {});
+ }
+
+ /**
+ * Wraps the given linked object (retrieved from a link repository or link method on the rest item)
+ * in an object that is appropriate for embedding, if needed. Does not perform the actual embed; the
+ * caller is responsible for that.
+ *
+ * @param resource the resource from which the embed will be made.
+ * @param linkedObject the linked object.
+ * @param link the link, which is used if the linked object is a list or page, to determine the self link
+ * and embed property name to use for the subresource.
+ * @param oldLinks The previously traversed links
+ * @return the wrapped object, which will have an "embed level" one greater than the given parent resource.
+ */
+ private Object wrapForEmbedding(HALResource extends RestAddressableModel> resource,
+ Object linkedObject, Link link, Link... oldLinks) {
int childEmbedLevel = resource.getContent().getEmbedLevel() + 1;
+ //Add the latest link to the list
+ Link[] newList = Arrays.copyOf(oldLinks, oldLinks.length + 1);
+ newList[oldLinks.length] = link;
if (linkedObject instanceof RestAddressableModel) {
RestAddressableModel restObject = (RestAddressableModel) linkedObject;
restObject.setEmbedLevel(childEmbedLevel);
- return converter.toResource(restObject);
+ return converter.toResource(restObject, newList);
} else if (linkedObject instanceof Page) {
// The first page has already been constructed by a link repository and we only need to wrap it
Page page = (Page) linkedObject;
return new EmbeddedPage(link.getHref(), page.map((restObject) -> {
restObject.setEmbedLevel(childEmbedLevel);
- return converter.toResource(restObject);
+ return converter.toResource(restObject, newList);
}), null, link.getRel());
} else if (linkedObject instanceof List) {
// The full list has been retrieved and we need to provide the first page for embedding
@@ -641,7 +750,7 @@ public class Utils {
return new EmbeddedPage(link.getHref(),
page.map((restObject) -> {
restObject.setEmbedLevel(childEmbedLevel);
- return converter.toResource(restObject);
+ return converter.toResource(restObject, newList);
}),
list, link.getRel());
} else {
diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml
index ed3e185839..61459f11d6 100644
--- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml
+++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml
@@ -50,7 +50,7 @@
+ class="org.dspace.app.rest.submit.factory.impl.BitstreamResourcePolicyAddPatchOperation"/>
@@ -75,7 +75,7 @@
+ class="org.dspace.app.rest.submit.factory.impl.BitstreamResourcePolicyRemovePatchOperation"/>
@@ -96,7 +96,7 @@
+ class="org.dspace.app.rest.submit.factory.impl.BitstreamResourcePolicyReplacePatchOperation"/>
idRef = new AtomicReference();
+ Step step = xmlWorkflowFactory.getStepByName("reviewstep");
// step 1
getClient(reviewer1Token).perform(get("/api/workflow/pooltasks/search/findByUser")
- .param("uuid", reviewer1.getID().toString()))
+ .param("uuid", reviewer1.getID().toString()).param("projection", "full"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.pooltasks", Matchers.contains(
Matchers.allOf(
Matchers.is(PoolTaskMatcher.matchPoolTask(null, "reviewstep")),
+ hasJsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)),
hasJsonPath("$._embedded.workflowitem",
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
witem, "Test item full workflow", "2019-03-06", "ExtraEntry")))
@@ -1821,17 +1833,22 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(status().isNoContent());
+ WorkflowActionConfig workflowAction = xmlWorkflowFactory.getActionByName("reviewaction");
+
// get the id of the claimed task
getClient(reviewer1Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
- .param("uuid", reviewer1.getID().toString()))
+ .param("uuid", reviewer1.getID().toString()).param("projection", "full"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
Matchers.allOf(
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
hasJsonPath("$.type", Matchers.is("claimedtask")),
+ hasJsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)),
hasJsonPath("$._embedded.workflowitem",
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
- witem, "Test item full workflow", "2019-03-06", "ExtraEntry")))
+ witem, "Test item full workflow", "2019-03-06", "ExtraEntry"))),
+ hasJsonPath("$._embedded.action",
+ WorkflowActionMatcher.matchWorkflowActionEntry(workflowAction))
))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
.andExpect(jsonPath("$.page.size", is(20)))
@@ -1850,13 +1867,16 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.inArchive", is(false)));
+ step = xmlWorkflowFactory.getStepByName("editstep");
+
// step 2
getClient(reviewer2Token).perform(get("/api/workflow/pooltasks/search/findByUser")
- .param("uuid", reviewer2.getID().toString()))
+ .param("uuid", reviewer2.getID().toString()).param("projection", "full"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.pooltasks", Matchers.contains(
Matchers.allOf(
Matchers.is(PoolTaskMatcher.matchPoolTask(null, "editstep")),
+ hasJsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)),
hasJsonPath("$._embedded.workflowitem",
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
witem, "Test item full workflow", "2019-03-06", "ExtraEntry")))
@@ -1872,17 +1892,22 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(status().isNoContent());
+ workflowAction = xmlWorkflowFactory.getActionByName("editaction");
+
// get the id of the claimed task
getClient(reviewer2Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
- .param("uuid", reviewer2.getID().toString()))
+ .param("uuid", reviewer2.getID().toString()).param("projection", "full"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
Matchers.allOf(
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
hasJsonPath("$.type", Matchers.is("claimedtask")),
+ hasJsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)),
hasJsonPath("$._embedded.workflowitem",
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
- witem, "Test item full workflow", "2019-03-06", "ExtraEntry")))
+ witem, "Test item full workflow", "2019-03-06", "ExtraEntry"))),
+ hasJsonPath("$._embedded.action",
+ WorkflowActionMatcher.matchWorkflowActionEntry(workflowAction))
))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
.andExpect(jsonPath("$.page.size", is(20)))
@@ -1901,17 +1926,20 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.inArchive", is(false)));
+ step = xmlWorkflowFactory.getStepByName("finaleditstep");
+
// step 3
getClient(reviewer3Token).perform(get("/api/workflow/pooltasks/search/findByUser")
- .param("uuid", reviewer3.getID().toString()))
+ .param("uuid", reviewer3.getID().toString()).param("projection", "full"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.pooltasks", Matchers.contains(
Matchers.allOf(
Matchers.is(PoolTaskMatcher.matchPoolTask(null, "finaleditstep")),
+ hasJsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)),
hasJsonPath("$._embedded.workflowitem",
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
witem, "Test item full workflow", "2019-03-06", "ExtraEntry")))
- ))))
+ ))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/pooltasks")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)))
@@ -1923,17 +1951,21 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(status().isNoContent());
+ workflowAction = xmlWorkflowFactory.getActionByName("finaleditaction");
// get the id of the claimed task
getClient(reviewer3Token).perform(get("/api/workflow/claimedtasks/search/findByUser")
- .param("uuid", reviewer3.getID().toString()))
+ .param("uuid", reviewer3.getID().toString()).param("projection", "full"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.claimedtasks", Matchers.contains(
Matchers.allOf(
hasJsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks/")),
hasJsonPath("$.type", Matchers.is("claimedtask")),
+ hasJsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)),
hasJsonPath("$._embedded.workflowitem",
Matchers.is(WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(
- witem, "Test item full workflow", "2019-03-06", "ExtraEntry")))
+ witem, "Test item full workflow", "2019-03-06", "ExtraEntry"))),
+ hasJsonPath("$._embedded.action",
+ WorkflowActionMatcher.matchWorkflowActionEntry(workflowAction))
))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/workflow/claimedtasks")))
.andExpect(jsonPath("$.page.size", is(20)))
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java
index 69abdb6c93..9e12fb3c74 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java
@@ -40,6 +40,7 @@ import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.ItemMatcher;
import org.dspace.app.rest.matcher.WorkflowItemMatcher;
+import org.dspace.app.rest.matcher.WorkflowStepMatcher;
import org.dspace.app.rest.matcher.WorkspaceItemMatcher;
import org.dspace.app.rest.model.patch.AddOperation;
import org.dspace.app.rest.model.patch.Operation;
@@ -53,6 +54,8 @@ import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.eperson.EPerson;
import org.dspace.services.ConfigurationService;
+import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
+import org.dspace.xmlworkflow.state.Step;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.hamcrest.Matchers;
@@ -71,6 +74,8 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Autowired
private ConfigurationService configurationService;
+ @Autowired
+ private XmlWorkflowFactory xmlWorkflowFactory;
@Before
@Override
@@ -85,7 +90,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* All the workflowitems should be returned regardless of the collection where they were created
- *
+ *
* @throws Exception
*/
public void findAllTest() throws Exception {
@@ -140,7 +145,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* The workflowitem endpoint must provide proper pagination
- *
+ *
* @throws Exception
*/
public void findAllWithPaginationTest() throws Exception {
@@ -208,7 +213,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* The findAll should be available only to admins regardless to having or less a role in the workflow
- *
+ *
* @throws Exception
*/
public void findAllForbiddenTest() throws Exception {
@@ -259,7 +264,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* The workflowitem resource endpoint must expose the proper structure
- *
+ *
* @throws Exception
*/
public void findOneTest() throws Exception {
@@ -297,7 +302,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* The workflowitem resource endpoint should be visible only to member of the corresponding workflow step
- *
+ *
* @throws Exception
*/
public void findOneForbiddenTest() throws Exception {
@@ -430,7 +435,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* The workflowitem resource endpoint must expose the proper structure
- *
+ *
* @throws Exception
*/
public void findOneRelsTest() throws Exception {
@@ -478,7 +483,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
@Test
/**
* Check the response code for unexistent workflowitem
- *
+ *
* @throws Exception
*/
public void findOneWrongIDTest() throws Exception {
@@ -495,7 +500,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
/**
* Create three workflowitem with two different submitter and verify that the findBySubmitter return the proper
* list of workflowitem for each submitter also paginating
- *
+ *
* @throws Exception
*/
public void findBySubmitterTest() throws Exception {
@@ -602,7 +607,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
/**
* A delete request over a workflowitem should result in abort the workflow sending the item back to the submitter
* workspace
- *
+ *
* @throws Exception
*/
public void deleteOneTest() throws Exception {
@@ -1682,4 +1687,84 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
.param("uuid", String.valueOf(witem.getItem().getID())))
.andExpect(status().isUnauthorized());
}
+
+ @Test
+ public void stepEmbedTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+ // 1. A community-collection structure with one parent community with sub-community and three collections
+ // (different workflow steps and reviewers).
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ EPerson reviewer1 = EPersonBuilder.createEPerson(context).withEmail("reviewer1@example.com")
+ .withPassword(password).build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
+ .withWorkflowGroup(1, reviewer1).build();
+
+ EPerson reviewer2 = EPersonBuilder.createEPerson(context).withEmail("reviewer2@example.com")
+ .withPassword(password).build();
+
+ Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2")
+ .withWorkflowGroup(2, reviewer2).build();
+
+ EPerson reviewer3 = EPersonBuilder.createEPerson(context).withEmail("reviewer3@example.com")
+ .withPassword(password).build();
+
+ Collection col3 = CollectionBuilder.createCollection(context, child1).withName("Collection 3")
+ .withWorkflowGroup(3, reviewer3).build();
+
+ //2. three workflow items in the three collections (this will lead to pool task)
+ XmlWorkflowItem witem1 = WorkflowItemBuilder.createWorkflowItem(context, col1)
+ .withTitle("Workflow Item 1")
+ .withIssueDate("2016-02-13")
+ .build();
+
+ XmlWorkflowItem witem2 = WorkflowItemBuilder.createWorkflowItem(context, col2)
+ .withTitle("Workflow Item 2")
+ .withIssueDate("2016-02-13")
+ .build();
+
+ XmlWorkflowItem witem3 = WorkflowItemBuilder.createWorkflowItem(context, col3)
+ .withTitle("Workflow Item 3")
+ .withIssueDate("2016-02-13")
+ .build();
+
+ Step step = xmlWorkflowFactory.getStepByName("reviewstep");
+ String token = getAuthToken(admin.getEmail(), password);
+
+ getClient(token).perform(get("/api/workflow/workflowitems/" + witem1.getID())
+ .param("projection", "full"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ WorkflowItemMatcher.matchItemWithTitleAndDateIssued(witem1,
+ "Workflow Item 1", "2016-02-13")))
+ .andExpect(jsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)));
+
+ step = xmlWorkflowFactory.getStepByName("editstep");
+
+ getClient(token).perform(get("/api/workflow/workflowitems/" + witem2.getID())
+ .param("projection", "full"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ WorkflowItemMatcher.matchItemWithTitleAndDateIssued(witem2,
+ "Workflow Item 2", "2016-02-13")))
+ .andExpect(jsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)));
+
+ step = xmlWorkflowFactory.getStepByName("finaleditstep");
+
+ getClient(token).perform(get("/api/workflow/workflowitems/" + witem3.getID())
+ .param("projection", "full"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ WorkflowItemMatcher.matchItemWithTitleAndDateIssued(witem3,
+ "Workflow Item 3", "2016-02-13")))
+ .andExpect(jsonPath("$._embedded.step", WorkflowStepMatcher.matchWorkflowStepEntry(step)));
+ }
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
index cd8abfe233..bfedc0d372 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.springframework.data.rest.webmvc.RestMediaTypes.TEXT_URI_LIST_VALUE;
import static org.springframework.http.MediaType.parseMediaType;
@@ -35,6 +36,7 @@ import org.dspace.app.rest.builder.BitstreamBuilder;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
+import org.dspace.app.rest.builder.GroupBuilder;
import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
@@ -52,6 +54,8 @@ import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.eperson.EPerson;
+import org.dspace.eperson.Group;
+import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.services.ConfigurationService;
import org.hamcrest.Matchers;
import org.junit.Before;
@@ -70,14 +74,34 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
@Autowired
private ConfigurationService configurationService;
+ private Group embargoedGroups;
+ private Group embargoedGroup1;
+ private Group embargoedGroup2;
+ private Group anonymousGroup;
+
@Before
@Override
public void setUp() throws Exception {
-
super.setUp();
+ context.turnOffAuthorisationSystem();
- //disable file upload mandatory
- configurationService.setProperty("webui.submit.upload.required", false);
+ embargoedGroups = GroupBuilder.createGroup(context)
+ .withName("Embargoed Groups")
+ .build();
+
+ embargoedGroup1 = GroupBuilder.createGroup(context)
+ .withName("Embargoed Group 1")
+ .withParent(embargoedGroups)
+ .build();
+
+ embargoedGroup2 = GroupBuilder.createGroup(context)
+ .withName("Embargoed Group 2")
+ .withParent(embargoedGroups)
+ .build();
+
+ anonymousGroup = EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS);
+
+ context.restoreAuthSystemState();
}
@Test
@@ -637,6 +661,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2017-10-17")
.build();
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItem1.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
@@ -697,6 +724,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withSubject("ExtraEntry")
.build();
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
// a simple patch to update an existent metadata
List updateTitle = new ArrayList();
Map value = new HashMap();
@@ -803,6 +833,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
null, "2017-10-17", "ExtraEntry"))))
;
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
// try to remove a metadata in a specific position
List removeMidSubject = new ArrayList();
removeMidSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/1"));
@@ -932,6 +965,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withSubject("ExtraEntry")
.build();
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
// try to add the title
List addTitle = new ArrayList();
@@ -989,6 +1024,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2017-10-17")
.build();
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
// try to add multiple subjects at once
List addSubjects = new ArrayList();
// create a list of values to use in add operation
@@ -1210,6 +1248,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(jsonPath("$.sections.license.url").isEmpty())
;
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
// try to grant the license with an add operation
List addGrant = new ArrayList();
addGrant.add(new AddOperation("/sections/license/granted", true));
@@ -1359,6 +1400,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.grantLicense()
.build();
+ //disable file upload mandatory
+ configurationService.setProperty("webui.submit.upload.required", false);
+
// check that our workspaceitems come with a license (all are build in the same way, just check the first)
getClient().perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
@@ -1601,6 +1645,147 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
}
+ @Test
+ public void patchUploadAddAccessConditionTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection collection1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
+
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, collection1)
+ .withTitle("Test WorkspaceItem")
+ .withIssueDate("2019-10-01")
+ .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf)
+ .build();
+
+ context.restoreAuthSystemState();
+
+ // create a list of values to use in add accessCondition
+ List addAccessCondition = new ArrayList();
+ Map value = new HashMap();
+ value.put("name", "embargoedWithGroupSelect");
+ value.put("groupUUID", embargoedGroup1.getID().toString());
+ value.put("endDate", "2030-10-02");
+ addAccessCondition.add(new AddOperation("/sections/upload/files/0/accessConditions/-", value));
+
+ String patchBody = getPatchContent(addAccessCondition);
+ String authToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",Matchers.allOf(
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].name",
+ is("embargoedWithGroupSelect")),
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID",
+ is(embargoedGroup1.getID().toString()))
+ )));
+
+ // verify that the patch changes have been persisted
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",Matchers.allOf(
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].name",
+ is("embargoedWithGroupSelect")),
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID",
+ is(embargoedGroup1.getID().toString()))
+ )));
+ }
+
+ @Test
+ public void patchUploadRemoveAccessConditionTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection collection1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
+
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, collection1)
+ .withTitle("Test WorkspaceItem")
+ .withIssueDate("2019-10-01")
+ .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf)
+ .build();
+
+ context.restoreAuthSystemState();
+
+ // create a list of values to use in add operation
+ List addAccessCondition = new ArrayList();
+ Map value = new HashMap();
+ value.put("name", "embargoedWithGroupSelect");
+ value.put("groupUUID", embargoedGroup1.getID().toString());
+ value.put("endDate", "2020-01-01");
+ addAccessCondition.add(new AddOperation("/sections/upload/files/0/accessConditions/-", value));
+
+ String patchBody = getPatchContent(addAccessCondition);
+ String authToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",Matchers.allOf(
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].name",
+ is("embargoedWithGroupSelect")),
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].endDate",
+ is("2020-01-01")),
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID",
+ is(embargoedGroup1.getID().toString()))
+ )));
+
+ // verify that the patch changes have been persisted
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",Matchers.allOf(
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].name",
+ is("embargoedWithGroupSelect")),
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].endDate",
+ is("2020-01-01")),
+ hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID",
+ is(embargoedGroup1.getID().toString()))
+ )));
+
+ // create a list of values to use in remove operation
+ List removeAccessCondition = new ArrayList();
+ removeAccessCondition.add(new RemoveOperation("/sections/upload/files/0/accessConditions"));
+
+ String patchReplaceBody = getPatchContent(removeAccessCondition);
+ getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchReplaceBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",Matchers.allOf(
+ hasJsonPath("$.sections.upload.files[0].accessConditions",hasSize(0)))));
+
+ // verify that the patch changes have been persisted
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",Matchers.allOf(
+ hasJsonPath("$.sections.upload.files[0].accessConditions", hasSize(0))
+ )));
+ }
+
@Test
/**
* Test the upload of files in the upload over section
@@ -1672,8 +1857,6 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2017-10-17")
.build();
- configurationService.setProperty("webui.submit.upload.required", true);
-
InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
final MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/simple-article.pdf",
"application/pdf", pdf);
@@ -1715,8 +1898,6 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2017-10-17")
.build();
- configurationService.setProperty("webui.submit.upload.required", true);
-
//Verify there is an error since no file was uploaded (with upload required set to true)
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java
index 229ff0d0fd..788aa502a6 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java
@@ -19,6 +19,8 @@ import org.dspace.content.Community;
import org.dspace.content.MetadataSchemaEnum;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.eperson.Group;
/**
* Builder to construct Community objects
@@ -74,6 +76,23 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder {
return this;
}
+ /**
+ * Create an admin group for the community with the specified members
+ *
+ * @param members epersons to add to the admin group
+ * @return this builder
+ * @throws SQLException
+ * @throws AuthorizeException
+ */
+ public CommunityBuilder withAdminGroup(EPerson... members) throws SQLException, AuthorizeException {
+ Group g = communityService.createAdministrators(context, community);
+ for (EPerson e : members) {
+ groupService.addMember(context, g, e);
+ }
+ groupService.update(context, g);
+ return this;
+ }
+
public CommunityBuilder addParentCommunity(final Context context, final Community parent)
throws SQLException, AuthorizeException {
communityService.addSubcommunity(context, parent, community);
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java
index 58840bb798..d9497f182b 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java
@@ -66,6 +66,19 @@ public class BitstreamMatcher {
);
}
+ public static Matcher super Object> matchBitstreamEntryWithoutEmbed(UUID uuid, long size) {
+ return allOf(
+ //Check ID and size
+ hasJsonPath("$.uuid", is(uuid.toString())),
+ hasJsonPath("$.sizeBytes", is((int) size)),
+ //Make sure we have a checksum
+ hasJsonPath("$.checkSum", matchChecksum()),
+ //Make sure we have a valid format
+ //Check links
+ matchLinks(uuid)
+ );
+ }
+
private static Matcher super Object> matchChecksum() {
return allOf(
hasJsonPath("$.checkSumAlgorithm", not(empty())),
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClaimedTaskMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClaimedTaskMatcher.java
index 317544b570..0d2ba67f16 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClaimedTaskMatcher.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClaimedTaskMatcher.java
@@ -36,7 +36,6 @@ public class ClaimedTaskMatcher {
*/
public static Matcher matchClaimedTask(ClaimedTask cTask, String step) {
return allOf(
- hasJsonPath("$.step", is(step)),
// Check workflowitem properties
matchProperties(cTask),
// Check links
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/PoolTaskMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/PoolTaskMatcher.java
index 9ebca9d572..aeed70e527 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/PoolTaskMatcher.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/PoolTaskMatcher.java
@@ -36,7 +36,6 @@ public class PoolTaskMatcher {
*/
public static Matcher matchPoolTask(PoolTask pTask, String step) {
return allOf(
- hasJsonPath("$.step", is(step)),
// Check workflowitem properties
matchProperties(pTask),
// Check links
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java
index 0c8c976a03..b34c945065 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java
@@ -12,6 +12,7 @@ import javax.annotation.Nullable;
import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.MockObject;
import org.dspace.app.rest.model.MockObjectRest;
+import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
@@ -87,8 +88,9 @@ public class MockProjection implements Projection {
return halResource;
}
- public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) {
- return true;
+ public boolean allowEmbedding(HALResource extends RestAddressableModel> halResource, LinkRest linkRest,
+ Link... oldLinks) {
+ return halResource.getContent().getEmbedLevel() < 2;
}
public boolean allowLinking(HALResource halResource, LinkRest linkRest) {
diff --git a/dspace/config/crosswalks/oai/metadataFormats/mets.xsl b/dspace/config/crosswalks/oai/metadataFormats/mets.xsl
index 41d683f075..ca41fcdb2b 100644
--- a/dspace/config/crosswalks/oai/metadataFormats/mets.xsl
+++ b/dspace/config/crosswalks/oai/metadataFormats/mets.xsl
@@ -1,9 +1,7 @@
+ xmlns:doc="http://www.lyncode.com/xoai" version="2.0">
@@ -20,7 +18,7 @@
-
+
diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg
index 2d1149c4d3..2e13d73f97 100644
--- a/dspace/config/modules/rest.cfg
+++ b/dspace/config/modules/rest.cfg
@@ -8,6 +8,12 @@
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
rest.cors.allowed-origins = *
+# This property determines the max embeddepth for a FullProjection. This is also used by the SpecificLevelProjection
+# as a fallback incase the property is defined on the bean
+rest.projections.full.max = 2
+
+# This property determines the max embed depth for a SpecificLevelProjection
+rest.projection.specificLevel.maxEmbed = 5
#---------------------------------------------------------------#
# These configs are used by the deprecated REST (v4-6) module #
diff --git a/dspace/config/spring/rest/projections.xml b/dspace/config/spring/rest/projections.xml
new file mode 100644
index 0000000000..6f082b7b5b
--- /dev/null
+++ b/dspace/config/spring/rest/projections.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+