mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge remote-tracking branch '4science-bitbucket/main' into CST-5288
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.access.status;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Plugin interface for the access status calculation.
|
||||
*/
|
||||
public interface AccessStatusHelper {
|
||||
/**
|
||||
* Calculate the access status for the item.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param item the item
|
||||
* @return an access status value
|
||||
* @throws SQLException An exception that provides information on a database access error or other errors.
|
||||
*/
|
||||
public String getAccessStatusFromItem(Context context, Item item, Date threshold)
|
||||
throws SQLException;
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.access.status;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.dspace.access.status.service.AccessStatusService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.service.PluginService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation for the access status calculation service.
|
||||
*/
|
||||
public class AccessStatusServiceImpl implements AccessStatusService {
|
||||
// Plugin implementation, set from the DSpace configuration by init().
|
||||
protected AccessStatusHelper helper = null;
|
||||
|
||||
protected Date forever_date = null;
|
||||
|
||||
@Autowired(required = true)
|
||||
protected ConfigurationService configurationService;
|
||||
|
||||
@Autowired(required = true)
|
||||
protected PluginService pluginService;
|
||||
|
||||
/**
|
||||
* Initialize the bean (after dependency injection has already taken place).
|
||||
* Ensures the configurationService is injected, so that we can get the plugin
|
||||
* and the forever embargo date threshold from the configuration.
|
||||
* Called by "init-method" in Spring configuration.
|
||||
*
|
||||
* @throws Exception on generic exception
|
||||
*/
|
||||
public void init() throws Exception {
|
||||
if (helper == null) {
|
||||
helper = (AccessStatusHelper) pluginService.getSinglePlugin(AccessStatusHelper.class);
|
||||
if (helper == null) {
|
||||
throw new IllegalStateException("The AccessStatusHelper plugin was not defined in "
|
||||
+ "DSpace configuration.");
|
||||
}
|
||||
|
||||
// Defines the embargo forever date threshold for the access status.
|
||||
// Look at EmbargoService.FOREVER for some improvements?
|
||||
int year = configurationService.getIntProperty("access.status.embargo.forever.year");
|
||||
int month = configurationService.getIntProperty("access.status.embargo.forever.month");
|
||||
int day = configurationService.getIntProperty("access.status.embargo.forever.day");
|
||||
|
||||
forever_date = new LocalDate(year, month, day).toDate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessStatus(Context context, Item item) throws SQLException {
|
||||
return helper.getAccessStatusFromItem(context, item, forever_date);
|
||||
}
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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.access.status;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.authorize.ResourcePolicy;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.authorize.service.ResourcePolicyService;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
/**
|
||||
* Default plugin implementation of the access status helper.
|
||||
* The getAccessStatusFromItem method provides a simple logic to
|
||||
* calculate the access status of an item based on the policies of
|
||||
* the primary or the first bitstream in the original bundle.
|
||||
* Users can override this method for enhanced functionality.
|
||||
*/
|
||||
public class DefaultAccessStatusHelper implements AccessStatusHelper {
|
||||
public static final String EMBARGO = "embargo";
|
||||
public static final String METADATA_ONLY = "metadata.only";
|
||||
public static final String OPEN_ACCESS = "open.access";
|
||||
public static final String RESTRICTED = "restricted";
|
||||
public static final String UNKNOWN = "unknown";
|
||||
|
||||
protected ItemService itemService =
|
||||
ContentServiceFactory.getInstance().getItemService();
|
||||
protected ResourcePolicyService resourcePolicyService =
|
||||
AuthorizeServiceFactory.getInstance().getResourcePolicyService();
|
||||
protected AuthorizeService authorizeService =
|
||||
AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
|
||||
public DefaultAccessStatusHelper() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look at the item's policies to determine an access status value.
|
||||
* It is also considering a date threshold for embargos and restrictions.
|
||||
*
|
||||
* If the item is null, simply returns the "unknown" value.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param item the item to embargo
|
||||
* @param threshold the embargo threshold date
|
||||
* @return an access status value
|
||||
*/
|
||||
@Override
|
||||
public String getAccessStatusFromItem(Context context, Item item, Date threshold)
|
||||
throws SQLException {
|
||||
if (item == null) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
// Consider only the original bundles.
|
||||
List<Bundle> bundles = item.getBundles(Constants.DEFAULT_BUNDLE_NAME);
|
||||
// Check for primary bitstreams first.
|
||||
Bitstream bitstream = bundles.stream()
|
||||
.map(bundle -> bundle.getPrimaryBitstream())
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (bitstream == null) {
|
||||
// If there is no primary bitstream,
|
||||
// take the first bitstream in the bundles.
|
||||
bitstream = bundles.stream()
|
||||
.map(bundle -> bundle.getBitstreams())
|
||||
.flatMap(List::stream)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
return caculateAccessStatusForDso(context, bitstream, threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look at the DSpace object's policies to determine an access status value.
|
||||
*
|
||||
* If the object is null, returns the "metadata.only" value.
|
||||
* If any policy attached to the object is valid for the anonymous group,
|
||||
* returns the "open.access" value.
|
||||
* Otherwise, if the policy start date is before the embargo threshold date,
|
||||
* returns the "embargo" value.
|
||||
* Every other cases return the "restricted" value.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param dso the DSpace object
|
||||
* @param threshold the embargo threshold date
|
||||
* @return an access status value
|
||||
*/
|
||||
private String caculateAccessStatusForDso(Context context, DSpaceObject dso, Date threshold)
|
||||
throws SQLException {
|
||||
if (dso == null) {
|
||||
return METADATA_ONLY;
|
||||
}
|
||||
// Only consider read policies.
|
||||
List<ResourcePolicy> policies = authorizeService
|
||||
.getPoliciesActionFilter(context, dso, Constants.READ);
|
||||
int openAccessCount = 0;
|
||||
int embargoCount = 0;
|
||||
int restrictedCount = 0;
|
||||
int unknownCount = 0;
|
||||
// Looks at all read policies.
|
||||
for (ResourcePolicy policy : policies) {
|
||||
boolean isValid = resourcePolicyService.isDateValid(policy);
|
||||
Group group = policy.getGroup();
|
||||
// The group must not be null here. However,
|
||||
// if it is, consider this as an unexpected case.
|
||||
if (group == null) {
|
||||
unknownCount++;
|
||||
} else if (StringUtils.equals(group.getName(), Group.ANONYMOUS)) {
|
||||
// Only calculate the status for the anonymous group.
|
||||
if (isValid) {
|
||||
// If the policy is valid, the anonymous group have access
|
||||
// to the bitstream.
|
||||
openAccessCount++;
|
||||
} else {
|
||||
Date startDate = policy.getStartDate();
|
||||
if (startDate != null && !startDate.before(threshold)) {
|
||||
// If the policy start date have a value and if this value
|
||||
// is equal or superior to the configured forever date, the
|
||||
// access status is also restricted.
|
||||
restrictedCount++;
|
||||
} else {
|
||||
// If the current date is not between the policy start date
|
||||
// and end date, the access status is embargo.
|
||||
embargoCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (openAccessCount > 0) {
|
||||
return OPEN_ACCESS;
|
||||
}
|
||||
if (embargoCount > 0 && restrictedCount == 0) {
|
||||
return EMBARGO;
|
||||
}
|
||||
if (unknownCount > 0) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
return RESTRICTED;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.access.status.factory;
|
||||
|
||||
import org.dspace.access.status.service.AccessStatusService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Abstract factory to get services for the access status package,
|
||||
* use AccessStatusServiceFactory.getInstance() to retrieve an implementation.
|
||||
*/
|
||||
public abstract class AccessStatusServiceFactory {
|
||||
|
||||
public abstract AccessStatusService getAccessStatusService();
|
||||
|
||||
public static AccessStatusServiceFactory getInstance() {
|
||||
return DSpaceServicesFactory.getInstance().getServiceManager()
|
||||
.getServiceByName("accessStatusServiceFactory", AccessStatusServiceFactory.class);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.access.status.factory;
|
||||
|
||||
import org.dspace.access.status.service.AccessStatusService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Factory implementation to get services for the access status package,
|
||||
* use AccessStatusServiceFactory.getInstance() to retrieve an implementation.
|
||||
*/
|
||||
public class AccessStatusServiceFactoryImpl extends AccessStatusServiceFactory {
|
||||
|
||||
@Autowired(required = true)
|
||||
private AccessStatusService accessStatusService;
|
||||
|
||||
@Override
|
||||
public AccessStatusService getAccessStatusService() {
|
||||
return accessStatusService;
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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/
|
||||
*/
|
||||
/**
|
||||
* <p>
|
||||
* Access status allows the users to view the bitstreams availability before
|
||||
* browsing into the item itself.
|
||||
* </p>
|
||||
* <p>
|
||||
* The access status is calculated through a pluggable class:
|
||||
* {@link org.dspace.access.status.AccessStatusHelper}.
|
||||
* The {@link org.dspace.access.status.AccessStatusServiceImpl}
|
||||
* must be configured to specify this class, as well as a forever embargo date
|
||||
* threshold year, month and day.
|
||||
* </p>
|
||||
* <p>
|
||||
* See {@link org.dspace.access.status.DefaultAccessStatusHelper} for a simple calculation
|
||||
* based on the primary or the first bitstream of the original bundle. You can
|
||||
* supply your own class to implement more complex access statuses.
|
||||
* </p>
|
||||
* <p>
|
||||
* For now, the access status is calculated when the item is shown in a list.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
package org.dspace.access.status;
|
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.access.status.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Public interface to the access status subsystem.
|
||||
* <p>
|
||||
* Configuration properties: (with examples)
|
||||
* {@code
|
||||
* # values for the forever embargo date threshold
|
||||
* # This threshold date is used in the default access status helper to dermine if an item is
|
||||
* # restricted or embargoed based on the start date of the primary (or first) file policies.
|
||||
* # In this case, if the policy start date is inferior to the threshold date, the status will
|
||||
* # be embargo, else it will be restricted.
|
||||
* # You might want to change this threshold based on your needs. For example: some databases
|
||||
* # doesn't accept a date superior to 31 december 9999.
|
||||
* access.status.embargo.forever.year = 10000
|
||||
* access.status.embargo.forever.month = 1
|
||||
* access.status.embargo.forever.day = 1
|
||||
* # implementation of access status helper plugin - replace with local implementation if applicable
|
||||
* # This default access status helper provides an item status based on the policies of the primary
|
||||
* # bitstream (or first bitstream in the original bundles if no primary file is specified).
|
||||
* plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.DefaultAccessStatusHelper
|
||||
* }
|
||||
*/
|
||||
public interface AccessStatusService {
|
||||
|
||||
/**
|
||||
* Calculate the access status for an Item while considering the forever embargo date threshold.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param item the item
|
||||
* @throws SQLException An exception that provides information on a database access error or other errors.
|
||||
*/
|
||||
public String getAccessStatus(Context context, Item item) throws SQLException;
|
||||
}
|
@@ -561,6 +561,15 @@ public class DCInput {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type bind list for use in determining whether
|
||||
* to display this field in angular dynamic form building
|
||||
* @return list of bound types
|
||||
*/
|
||||
public List<String> getTypeBindList() {
|
||||
return typeBind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify whether the current field contains an entity relationship
|
||||
* This also implies a relationship type is defined for this field
|
||||
|
@@ -140,6 +140,7 @@
|
||||
<dc-qualifier>ispartofseries</dc-qualifier>
|
||||
<repeatable>true</repeatable>
|
||||
<label>Series/Report No.</label>
|
||||
<type-bind>Technical Report</type-bind>
|
||||
<input-type>series</input-type>
|
||||
<hint>Enter the series and number assigned to this item by your community.</hint>
|
||||
<required></required>
|
||||
|
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.access.status;
|
||||
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.AbstractUnitTest;
|
||||
import org.dspace.access.status.factory.AccessStatusServiceFactory;
|
||||
import org.dspace.access.status.service.AccessStatusService;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.CommunityService;
|
||||
import org.dspace.content.service.InstallItemService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.WorkspaceItemService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit Tests for access status service
|
||||
*/
|
||||
public class AccessStatusServiceTest extends AbstractUnitTest {
|
||||
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(AccessStatusServiceTest.class);
|
||||
|
||||
private Collection collection;
|
||||
private Community owningCommunity;
|
||||
private Item item;
|
||||
|
||||
protected CommunityService communityService =
|
||||
ContentServiceFactory.getInstance().getCommunityService();
|
||||
protected CollectionService collectionService =
|
||||
ContentServiceFactory.getInstance().getCollectionService();
|
||||
protected ItemService itemService =
|
||||
ContentServiceFactory.getInstance().getItemService();
|
||||
protected WorkspaceItemService workspaceItemService =
|
||||
ContentServiceFactory.getInstance().getWorkspaceItemService();
|
||||
protected InstallItemService installItemService =
|
||||
ContentServiceFactory.getInstance().getInstallItemService();
|
||||
protected AccessStatusService accessStatusService =
|
||||
AccessStatusServiceFactory.getInstance().getAccessStatusService();
|
||||
|
||||
/**
|
||||
* This method will be run before every test as per @Before. It will
|
||||
* initialize resources required for the tests.
|
||||
*
|
||||
* Other methods can be annotated with @Before here or in subclasses
|
||||
* but no execution order is guaranteed
|
||||
*/
|
||||
@Before
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
try {
|
||||
context.turnOffAuthorisationSystem();
|
||||
owningCommunity = communityService.create(null, context);
|
||||
collection = collectionService.create(context, owningCommunity);
|
||||
item = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
context.restoreAuthSystemState();
|
||||
} catch (AuthorizeException ex) {
|
||||
log.error("Authorization Error in init", ex);
|
||||
fail("Authorization Error in init: " + ex.getMessage());
|
||||
} catch (SQLException ex) {
|
||||
log.error("SQL Error in init", ex);
|
||||
fail("SQL Error in init: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be run after every test as per @After. It will
|
||||
* clean resources initialized by the @Before methods.
|
||||
*
|
||||
* Other methods can be annotated with @After here or in subclasses
|
||||
* but no execution order is guaranteed
|
||||
*/
|
||||
@After
|
||||
@Override
|
||||
public void destroy() {
|
||||
context.turnOffAuthorisationSystem();
|
||||
try {
|
||||
itemService.delete(context, item);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
collectionService.delete(context, collection);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
communityService.delete(context, owningCommunity);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
context.restoreAuthSystemState();
|
||||
item = null;
|
||||
collection = null;
|
||||
owningCommunity = null;
|
||||
try {
|
||||
super.destroy();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessStatus() throws Exception {
|
||||
String status = accessStatusService.getAccessStatus(context, item);
|
||||
assertNotEquals("testGetAccessStatus 0", status, DefaultAccessStatusHelper.UNKNOWN);
|
||||
}
|
||||
}
|
@@ -0,0 +1,423 @@
|
||||
/**
|
||||
* 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.access.status;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.AbstractUnitTest;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.ResourcePolicy;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.ResourcePolicyService;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.content.service.BundleService;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.CommunityService;
|
||||
import org.dspace.content.service.InstallItemService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.WorkspaceItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DefaultAccessStatusHelperTest extends AbstractUnitTest {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(DefaultAccessStatusHelperTest.class);
|
||||
|
||||
private Collection collection;
|
||||
private Community owningCommunity;
|
||||
private Item itemWithoutBundle;
|
||||
private Item itemWithoutBitstream;
|
||||
private Item itemWithBitstream;
|
||||
private Item itemWithEmbargo;
|
||||
private Item itemWithDateRestriction;
|
||||
private Item itemWithGroupRestriction;
|
||||
private Item itemWithoutPolicy;
|
||||
private Item itemWithoutPrimaryBitstream;
|
||||
private Item itemWithPrimaryAndMultipleBitstreams;
|
||||
private Item itemWithoutPrimaryAndMultipleBitstreams;
|
||||
private DefaultAccessStatusHelper helper;
|
||||
private Date threshold;
|
||||
|
||||
protected CommunityService communityService =
|
||||
ContentServiceFactory.getInstance().getCommunityService();
|
||||
protected CollectionService collectionService =
|
||||
ContentServiceFactory.getInstance().getCollectionService();
|
||||
protected ItemService itemService =
|
||||
ContentServiceFactory.getInstance().getItemService();
|
||||
protected WorkspaceItemService workspaceItemService =
|
||||
ContentServiceFactory.getInstance().getWorkspaceItemService();
|
||||
protected InstallItemService installItemService =
|
||||
ContentServiceFactory.getInstance().getInstallItemService();
|
||||
protected BundleService bundleService =
|
||||
ContentServiceFactory.getInstance().getBundleService();
|
||||
protected BitstreamService bitstreamService =
|
||||
ContentServiceFactory.getInstance().getBitstreamService();
|
||||
protected ResourcePolicyService resourcePolicyService =
|
||||
AuthorizeServiceFactory.getInstance().getResourcePolicyService();
|
||||
protected GroupService groupService =
|
||||
EPersonServiceFactory.getInstance().getGroupService();
|
||||
|
||||
/**
|
||||
* This method will be run before every test as per @Before. It will
|
||||
* initialize resources required for the tests.
|
||||
*
|
||||
* Other methods can be annotated with @Before here or in subclasses
|
||||
* but no execution order is guaranteed
|
||||
*/
|
||||
@Before
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
try {
|
||||
context.turnOffAuthorisationSystem();
|
||||
owningCommunity = communityService.create(null, context);
|
||||
collection = collectionService.create(context, owningCommunity);
|
||||
itemWithoutBundle = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithoutBitstream = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithBitstream = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithEmbargo = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithDateRestriction = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithGroupRestriction = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithoutPolicy = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithoutPrimaryBitstream = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithPrimaryAndMultipleBitstreams = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
itemWithoutPrimaryAndMultipleBitstreams = installItemService.installItem(context,
|
||||
workspaceItemService.create(context, collection, true));
|
||||
context.restoreAuthSystemState();
|
||||
} catch (AuthorizeException ex) {
|
||||
log.error("Authorization Error in init", ex);
|
||||
fail("Authorization Error in init: " + ex.getMessage());
|
||||
} catch (SQLException ex) {
|
||||
log.error("SQL Error in init", ex);
|
||||
fail("SQL Error in init: " + ex.getMessage());
|
||||
}
|
||||
helper = new DefaultAccessStatusHelper();
|
||||
threshold = new LocalDate(10000, 1, 1).toDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be run after every test as per @After. It will
|
||||
* clean resources initialized by the @Before methods.
|
||||
*
|
||||
* Other methods can be annotated with @After here or in subclasses
|
||||
* but no execution order is guaranteed
|
||||
*/
|
||||
@After
|
||||
@Override
|
||||
public void destroy() {
|
||||
context.turnOffAuthorisationSystem();
|
||||
try {
|
||||
itemService.delete(context, itemWithoutBundle);
|
||||
itemService.delete(context, itemWithoutBitstream);
|
||||
itemService.delete(context, itemWithBitstream);
|
||||
itemService.delete(context, itemWithEmbargo);
|
||||
itemService.delete(context, itemWithDateRestriction);
|
||||
itemService.delete(context, itemWithGroupRestriction);
|
||||
itemService.delete(context, itemWithoutPolicy);
|
||||
itemService.delete(context, itemWithoutPrimaryBitstream);
|
||||
itemService.delete(context, itemWithPrimaryAndMultipleBitstreams);
|
||||
itemService.delete(context, itemWithoutPrimaryAndMultipleBitstreams);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
collectionService.delete(context, collection);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
communityService.delete(context, owningCommunity);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
context.restoreAuthSystemState();
|
||||
itemWithoutBundle = null;
|
||||
itemWithoutBitstream = null;
|
||||
itemWithBitstream = null;
|
||||
itemWithEmbargo = null;
|
||||
itemWithDateRestriction = null;
|
||||
itemWithGroupRestriction = null;
|
||||
itemWithoutPolicy = null;
|
||||
itemWithoutPrimaryBitstream = null;
|
||||
itemWithPrimaryAndMultipleBitstreams = null;
|
||||
itemWithoutPrimaryAndMultipleBitstreams = null;
|
||||
collection = null;
|
||||
owningCommunity = null;
|
||||
helper = null;
|
||||
threshold = null;
|
||||
communityService = null;
|
||||
collectionService = null;
|
||||
itemService = null;
|
||||
workspaceItemService = null;
|
||||
installItemService = null;
|
||||
bundleService = null;
|
||||
bitstreamService = null;
|
||||
resourcePolicyService = null;
|
||||
groupService = null;
|
||||
try {
|
||||
super.destroy();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a null item
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithNullItem() throws Exception {
|
||||
String status = helper.getAccessStatusFromItem(context, null, threshold);
|
||||
assertThat("testWithNullItem 0", status, equalTo(DefaultAccessStatusHelper.UNKNOWN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with no bundle
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithoutBundle() throws Exception {
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithoutBundle, threshold);
|
||||
assertThat("testWithoutBundle 0", status, equalTo(DefaultAccessStatusHelper.METADATA_ONLY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with no bitstream
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithoutBitstream() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
bundleService.create(context, itemWithoutBitstream, Constants.CONTENT_BUNDLE_NAME);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithoutBitstream, threshold);
|
||||
assertThat("testWithoutBitstream 0", status, equalTo(DefaultAccessStatusHelper.METADATA_ONLY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with a basic bitstream (open access)
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithBitstream() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithBitstream, Constants.CONTENT_BUNDLE_NAME);
|
||||
Bitstream bitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bitstream.setName(context, "primary");
|
||||
bundle.setPrimaryBitstreamID(bitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithBitstream, threshold);
|
||||
assertThat("testWithBitstream 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with an embargo
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithEmbargo() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithEmbargo, Constants.CONTENT_BUNDLE_NAME);
|
||||
Bitstream bitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bitstream.setName(context, "primary");
|
||||
bundle.setPrimaryBitstreamID(bitstream);
|
||||
List<ResourcePolicy> policies = new ArrayList<>();
|
||||
ResourcePolicy policy = resourcePolicyService.create(context);
|
||||
policy.setRpName("Embargo");
|
||||
Group group = groupService.findByName(context, Group.ANONYMOUS);
|
||||
policy.setGroup(group);
|
||||
policy.setAction(Constants.READ);
|
||||
policy.setStartDate(new LocalDate(9999, 12, 31).toDate());
|
||||
policies.add(policy);
|
||||
authorizeService.removeAllPolicies(context, bitstream);
|
||||
authorizeService.addPolicies(context, policies, bitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithEmbargo, threshold);
|
||||
assertThat("testWithEmbargo 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with an anonymous date restriction
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithDateRestriction() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithDateRestriction, Constants.CONTENT_BUNDLE_NAME);
|
||||
Bitstream bitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bitstream.setName(context, "primary");
|
||||
bundle.setPrimaryBitstreamID(bitstream);
|
||||
List<ResourcePolicy> policies = new ArrayList<>();
|
||||
ResourcePolicy policy = resourcePolicyService.create(context);
|
||||
policy.setRpName("Restriction");
|
||||
Group group = groupService.findByName(context, Group.ANONYMOUS);
|
||||
policy.setGroup(group);
|
||||
policy.setAction(Constants.READ);
|
||||
policy.setStartDate(new LocalDate(10000, 1, 1).toDate());
|
||||
policies.add(policy);
|
||||
authorizeService.removeAllPolicies(context, bitstream);
|
||||
authorizeService.addPolicies(context, policies, bitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithDateRestriction, threshold);
|
||||
assertThat("testWithDateRestriction 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with a group restriction
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithGroupRestriction() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithGroupRestriction, Constants.CONTENT_BUNDLE_NAME);
|
||||
Bitstream bitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bitstream.setName(context, "primary");
|
||||
bundle.setPrimaryBitstreamID(bitstream);
|
||||
List<ResourcePolicy> policies = new ArrayList<>();
|
||||
ResourcePolicy policy = resourcePolicyService.create(context);
|
||||
policy.setRpName("Restriction");
|
||||
Group group = groupService.findByName(context, Group.ADMIN);
|
||||
policy.setGroup(group);
|
||||
policy.setAction(Constants.READ);
|
||||
policies.add(policy);
|
||||
authorizeService.removeAllPolicies(context, bitstream);
|
||||
authorizeService.addPolicies(context, policies, bitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithGroupRestriction, threshold);
|
||||
assertThat("testWithGroupRestriction 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with no policy
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithoutPolicy() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithoutPolicy, Constants.CONTENT_BUNDLE_NAME);
|
||||
Bitstream bitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bitstream.setName(context, "primary");
|
||||
bundle.setPrimaryBitstreamID(bitstream);
|
||||
authorizeService.removeAllPolicies(context, bitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithoutPolicy, threshold);
|
||||
assertThat("testWithoutPolicy 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with no primary bitstream
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithoutPrimaryBitstream() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithoutPrimaryBitstream, Constants.CONTENT_BUNDLE_NAME);
|
||||
Bitstream bitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bitstream.setName(context, "first");
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryBitstream, threshold);
|
||||
assertThat("testWithoutPrimaryBitstream 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with an open access bitstream
|
||||
* and another primary bitstream on embargo
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithPrimaryAndMultipleBitstreams() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithPrimaryAndMultipleBitstreams,
|
||||
Constants.CONTENT_BUNDLE_NAME);
|
||||
bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
Bitstream primaryBitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
bundle.setPrimaryBitstreamID(primaryBitstream);
|
||||
List<ResourcePolicy> policies = new ArrayList<>();
|
||||
ResourcePolicy policy = resourcePolicyService.create(context);
|
||||
policy.setRpName("Embargo");
|
||||
Group group = groupService.findByName(context, Group.ANONYMOUS);
|
||||
policy.setGroup(group);
|
||||
policy.setAction(Constants.READ);
|
||||
policy.setStartDate(new LocalDate(9999, 12, 31).toDate());
|
||||
policies.add(policy);
|
||||
authorizeService.removeAllPolicies(context, primaryBitstream);
|
||||
authorizeService.addPolicies(context, policies, primaryBitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold);
|
||||
assertThat("testWithPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an item with an open access bitstream
|
||||
* and another bitstream on embargo
|
||||
* @throws java.lang.Exception passed through.
|
||||
*/
|
||||
@Test
|
||||
public void testWithNoPrimaryAndMultipleBitstreams() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Bundle bundle = bundleService.create(context, itemWithoutPrimaryAndMultipleBitstreams,
|
||||
Constants.CONTENT_BUNDLE_NAME);
|
||||
bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
Bitstream anotherBitstream = bitstreamService.create(context, bundle,
|
||||
new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8)));
|
||||
List<ResourcePolicy> policies = new ArrayList<>();
|
||||
ResourcePolicy policy = resourcePolicyService.create(context);
|
||||
policy.setRpName("Embargo");
|
||||
Group group = groupService.findByName(context, Group.ANONYMOUS);
|
||||
policy.setGroup(group);
|
||||
policy.setAction(Constants.READ);
|
||||
policy.setStartDate(new LocalDate(9999, 12, 31).toDate());
|
||||
policies.add(policy);
|
||||
authorizeService.removeAllPolicies(context, anotherBitstream);
|
||||
authorizeService.addPolicies(context, policies, anotherBitstream);
|
||||
context.restoreAuthSystemState();
|
||||
String status = helper.getAccessStatusFromItem(context, itemWithoutPrimaryAndMultipleBitstreams, threshold);
|
||||
assertThat("testWithNoPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS));
|
||||
}
|
||||
}
|
@@ -155,6 +155,7 @@ public class SubmissionFormConverter implements DSpaceConverter<DCInputSet, Subm
|
||||
inputField.setInput(inputRest);
|
||||
if (dcinput.isMetadataField()) {
|
||||
inputField.setSelectableMetadata(selectableMetadata);
|
||||
inputField.setTypeBind(dcinput.getTypeBindList());
|
||||
}
|
||||
if (dcinput.isRelationshipField()) {
|
||||
selectableRelationship = getSelectableRelationships(dcinput);
|
||||
|
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty.Access;
|
||||
|
||||
/**
|
||||
* The Access Status REST Resource.
|
||||
*/
|
||||
public class AccessStatusRest implements RestModel {
|
||||
public static final String NAME = "accessStatus";
|
||||
|
||||
String status;
|
||||
|
||||
@Override
|
||||
@JsonProperty(access = Access.READ_ONLY)
|
||||
public String getType() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public String getTypePlural() {
|
||||
return getType();
|
||||
}
|
||||
|
||||
public AccessStatusRest() {
|
||||
setStatus(null);
|
||||
}
|
||||
|
||||
public AccessStatusRest(String status) {
|
||||
setStatus(status);
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
@@ -17,6 +17,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
@LinksRest(links = {
|
||||
@LinkRest(
|
||||
name = ItemRest.ACCESS_STATUS,
|
||||
method = "getAccessStatus"
|
||||
),
|
||||
@LinkRest(
|
||||
name = ItemRest.BUNDLES,
|
||||
method = "getBundles"
|
||||
@@ -51,6 +55,7 @@ public class ItemRest extends DSpaceObjectRest {
|
||||
public static final String PLURAL_NAME = "items";
|
||||
public static final String CATEGORY = RestAddressableModel.CORE;
|
||||
|
||||
public static final String ACCESS_STATUS = "accessStatus";
|
||||
public static final String BUNDLES = "bundles";
|
||||
public static final String MAPPED_COLLECTIONS = "mappedCollections";
|
||||
public static final String OWNING_COLLECTION = "owningCollection";
|
||||
|
@@ -83,6 +83,11 @@ public class SubmissionFormFieldRest {
|
||||
*/
|
||||
private List<LanguageFormField> languageCodes;
|
||||
|
||||
/**
|
||||
* The list of type bind value
|
||||
*/
|
||||
private List<String> typeBind;
|
||||
|
||||
/**
|
||||
* Getter for {@link #selectableMetadata}
|
||||
*
|
||||
@@ -266,6 +271,14 @@ public class SubmissionFormFieldRest {
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getTypeBind() {
|
||||
return typeBind;
|
||||
}
|
||||
|
||||
public void setTypeBind(List<String> typeBind) {
|
||||
this.typeBind = typeBind;
|
||||
}
|
||||
|
||||
public SelectableRelationship getSelectableRelationship() {
|
||||
return selectableRelationship;
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.hateoas;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
import org.dspace.app.rest.model.AccessStatusRest;
|
||||
import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
|
||||
|
||||
/**
|
||||
* Access Status Rest HAL Resource. The HAL Resource wraps the REST Resource
|
||||
* adding support for the links and embedded resources
|
||||
*/
|
||||
@RelNameDSpaceResource(AccessStatusRest.NAME)
|
||||
public class AccessStatusResource extends HALResource<AccessStatusRest> {
|
||||
|
||||
@JsonUnwrapped
|
||||
private AccessStatusRest data;
|
||||
|
||||
public AccessStatusResource(AccessStatusRest entry) {
|
||||
super(entry);
|
||||
}
|
||||
|
||||
public AccessStatusRest getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
|
||||
package org.dspace.app.rest.repository;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.access.status.service.AccessStatusService;
|
||||
import org.dspace.app.rest.model.AccessStatusRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Link repository for calculating the access status of an Item
|
||||
*/
|
||||
@Component(ItemRest.CATEGORY + "." + ItemRest.NAME + "." + ItemRest.ACCESS_STATUS)
|
||||
public class ItemAccessStatusLinkRepository extends AbstractDSpaceRestRepository
|
||||
implements LinkRestRepository {
|
||||
|
||||
@Autowired
|
||||
ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
AccessStatusService accessStatusService;
|
||||
|
||||
@PreAuthorize("hasPermission(#itemId, 'ITEM', 'READ')")
|
||||
public AccessStatusRest getAccessStatus(@Nullable HttpServletRequest request,
|
||||
UUID itemId,
|
||||
@Nullable Pageable optionalPageable,
|
||||
Projection projection) {
|
||||
try {
|
||||
Context context = obtainContext();
|
||||
Item item = itemService.find(context, itemId);
|
||||
if (item == null) {
|
||||
throw new ResourceNotFoundException("No such item: " + itemId);
|
||||
}
|
||||
AccessStatusRest accessStatusRest = new AccessStatusRest();
|
||||
String accessStatus = accessStatusService.getAccessStatus(context, item);
|
||||
accessStatusRest.setStatus(accessStatus);
|
||||
return accessStatusRest;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -31,6 +31,8 @@ import org.dspace.content.InProgressSubmission;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.Utils;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Describe step for DSpace Spring Rest. Expose and allow patching of the in progress submission metadata. It is
|
||||
@@ -43,7 +45,11 @@ public class DescribeStep extends AbstractProcessingStep {
|
||||
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DescribeStep.class);
|
||||
|
||||
// Input reader for form configuration
|
||||
private DCInputsReader inputReader;
|
||||
// Configuration service
|
||||
private final ConfigurationService configurationService =
|
||||
DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
|
||||
public DescribeStep() throws DCInputsReaderException {
|
||||
inputReader = new DCInputsReader();
|
||||
@@ -64,8 +70,17 @@ public class DescribeStep extends AbstractProcessingStep {
|
||||
|
||||
private void readField(InProgressSubmission obj, SubmissionStepConfig config, DataDescribe data,
|
||||
DCInputSet inputConfig) throws DCInputsReaderException {
|
||||
String documentTypeValue = "";
|
||||
List<MetadataValue> documentType = itemService.getMetadataByMetadataString(obj.getItem(),
|
||||
configurationService.getProperty("submit.type-bind.field", "dc.type"));
|
||||
if (documentType.size() > 0) {
|
||||
documentTypeValue = documentType.get(0).getValue();
|
||||
}
|
||||
for (DCInput[] row : inputConfig.getFields()) {
|
||||
for (DCInput input : row) {
|
||||
// Is this input allowed for the document type, as per type bind config? If there is no type
|
||||
// bind set, this is always true
|
||||
boolean allowed = input.isAllowedFor(documentTypeValue);
|
||||
|
||||
List<String> fieldsName = new ArrayList<String>();
|
||||
if (input.isQualdropValue()) {
|
||||
@@ -91,20 +106,30 @@ public class DescribeStep extends AbstractProcessingStep {
|
||||
String[] metadataToCheck = Utils.tokenize(md.getMetadataField().toString());
|
||||
if (data.getMetadata().containsKey(
|
||||
Utils.standardize(metadataToCheck[0], metadataToCheck[1], metadataToCheck[2], "."))) {
|
||||
data.getMetadata()
|
||||
.get(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
|
||||
md.getMetadataField().getElement(),
|
||||
md.getMetadataField().getQualifier(),
|
||||
"."))
|
||||
.add(dto);
|
||||
// If field is allowed by type bind, add value to existing field set, otherwise remove
|
||||
// all values for this field
|
||||
if (allowed) {
|
||||
data.getMetadata()
|
||||
.get(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
|
||||
md.getMetadataField().getElement(),
|
||||
md.getMetadataField().getQualifier(),
|
||||
"."))
|
||||
.add(dto);
|
||||
} else {
|
||||
data.getMetadata().remove(Utils.standardize(metadataToCheck[0], metadataToCheck[1],
|
||||
metadataToCheck[2], "."));
|
||||
}
|
||||
} else {
|
||||
List<MetadataValueRest> listDto = new ArrayList<>();
|
||||
listDto.add(dto);
|
||||
data.getMetadata()
|
||||
.put(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
|
||||
md.getMetadataField().getElement(),
|
||||
md.getMetadataField().getQualifier(),
|
||||
"."), listDto);
|
||||
// Add values only if allowed by type bind
|
||||
if (allowed) {
|
||||
List<MetadataValueRest> listDto = new ArrayList<>();
|
||||
listDto.add(dto);
|
||||
data.getMetadata()
|
||||
.put(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
|
||||
md.getMetadataField().getElement(),
|
||||
md.getMetadataField().getQualifier(),
|
||||
"."), listDto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.rest.model.ErrorRest;
|
||||
import org.dspace.app.rest.repository.WorkspaceItemRestRepository;
|
||||
import org.dspace.app.rest.submit.SubmissionService;
|
||||
import org.dspace.app.rest.utils.ContextUtil;
|
||||
import org.dspace.app.util.DCInput;
|
||||
import org.dspace.app.util.DCInputSet;
|
||||
import org.dspace.app.util.DCInputsReader;
|
||||
@@ -25,6 +26,7 @@ import org.dspace.content.InProgressSubmission;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.authority.service.MetadataAuthorityService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
|
||||
/**
|
||||
* Execute three validation check on fields validation:
|
||||
@@ -50,12 +52,20 @@ public class MetadataValidation extends AbstractValidation {
|
||||
|
||||
private MetadataAuthorityService metadataAuthorityService;
|
||||
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Override
|
||||
public List<ErrorRest> validate(SubmissionService submissionService, InProgressSubmission obj,
|
||||
SubmissionStepConfig config) throws DCInputsReaderException, SQLException {
|
||||
|
||||
List<ErrorRest> errors = new ArrayList<>();
|
||||
String documentTypeValue = "";
|
||||
DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId());
|
||||
List<MetadataValue> documentType = itemService.getMetadataByMetadataString(obj.getItem(),
|
||||
configurationService.getProperty("submit.type-bind.field", "dc.type"));
|
||||
if (documentType.size() > 0) {
|
||||
documentTypeValue = documentType.get(0).getValue();
|
||||
}
|
||||
for (DCInput[] row : inputConfig.getFields()) {
|
||||
for (DCInput input : row) {
|
||||
String fieldKey =
|
||||
@@ -71,12 +81,21 @@ public class MetadataValidation extends AbstractValidation {
|
||||
for (int i = 1; i < inputPairs.size(); i += 2) {
|
||||
String fullFieldname = input.getFieldName() + "." + (String) inputPairs.get(i);
|
||||
List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fullFieldname);
|
||||
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
|
||||
if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
|
||||
foundResult = true;
|
||||
// If the input is not allowed for this type, strip it from item metadata.
|
||||
if (!input.isAllowedFor(documentTypeValue)) {
|
||||
itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(),
|
||||
obj.getItem(), mdv);
|
||||
} else {
|
||||
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
|
||||
if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
|
||||
foundResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input.isRequired() && ! foundResult) {
|
||||
// If the input is required but not allowed for this type, and we removed, don't throw
|
||||
// an error - this way, a field can be required for "Book" to which it is bound, but not
|
||||
// other types. A user may have switched between types before a final deposit
|
||||
if (input.isRequired() && !foundResult && input.isAllowedFor(documentTypeValue)) {
|
||||
// for this required qualdrop no value was found, add to the list of error fields
|
||||
addError(errors, ERROR_VALIDATION_REQUIRED,
|
||||
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" +
|
||||
@@ -89,6 +108,12 @@ public class MetadataValidation extends AbstractValidation {
|
||||
|
||||
for (String fieldName : fieldsName) {
|
||||
List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName);
|
||||
if (!input.isAllowedFor(documentTypeValue)) {
|
||||
itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(), obj.getItem(), mdv);
|
||||
// Continue here, this skips the required check since we've just removed values that previously
|
||||
// appeared, and the configuration already indicates this field shouldn't be included
|
||||
continue;
|
||||
}
|
||||
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
|
||||
if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
|
||||
// since this field is missing add to list of error
|
||||
@@ -124,6 +149,10 @@ public class MetadataValidation extends AbstractValidation {
|
||||
}
|
||||
}
|
||||
|
||||
public void setConfigurationService(ConfigurationService configurationService) {
|
||||
this.configurationService = configurationService;
|
||||
}
|
||||
|
||||
public void setItemService(ItemService itemService) {
|
||||
this.itemService = itemService;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
<bean name="metadataValidation" class="org.dspace.app.rest.submit.step.validation.MetadataValidation"
|
||||
scope="prototype">
|
||||
<property name="name" value="submission-form"/>
|
||||
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
|
||||
<property name="itemService" ref="org.dspace.content.ItemServiceImpl"/>
|
||||
<property name="metadataAuthorityService" ref="org.dspace.content.authority.MetadataAuthorityServiceImpl"/>
|
||||
</bean>
|
||||
|
@@ -12,6 +12,7 @@ import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadataDoesNotExist;
|
||||
import static org.dspace.core.Constants.WRITE;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.emptyOrNullString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
@@ -3861,6 +3862,8 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
.andExpect(jsonPath("$.inArchive", Matchers.is(false)))
|
||||
.andExpect(jsonPath("$._links.self.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString())))
|
||||
.andExpect(jsonPath("$._links.accessStatus.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/accessStatus")))
|
||||
.andExpect(jsonPath("$._links.bundles.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/bundles")))
|
||||
.andExpect(jsonPath("$._links.mappedCollections.href",
|
||||
@@ -3893,6 +3896,8 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
.andExpect(jsonPath("$.inArchive", Matchers.is(false)))
|
||||
.andExpect(jsonPath("$._links.self.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString())))
|
||||
.andExpect(jsonPath("$._links.accessStatus.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/accessStatus")))
|
||||
.andExpect(jsonPath("$._links.bundles.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/bundles")))
|
||||
.andExpect(jsonPath("$._links.mappedCollections.href",
|
||||
@@ -3926,6 +3931,8 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString())))
|
||||
.andExpect(jsonPath("$._links.self.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString())))
|
||||
.andExpect(jsonPath("$._links.accessStatus.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/accessStatus")))
|
||||
.andExpect(jsonPath("$._links.bundles.href",
|
||||
Matchers.containsString("/api/core/items/" + item.getID().toString() + "/bundles")))
|
||||
.andExpect(jsonPath("$._links.mappedCollections.href",
|
||||
@@ -4376,4 +4383,35 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAccessStatusForItemBadRequestTest() throws Exception {
|
||||
getClient().perform(get("/api/core/items/{uuid}/accessStatus", "1"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAccessStatusForItemNotFoundTest() throws Exception {
|
||||
UUID fakeUUID = UUID.randomUUID();
|
||||
getClient().perform(get("/api/core/items/{uuid}/accessStatus", fakeUUID))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAccessStatusForItemTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection owningCollection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Owning Collection")
|
||||
.build();
|
||||
Item item = ItemBuilder.createItem(context, owningCollection)
|
||||
.withTitle("Test item")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
getClient().perform(get("/api/core/items/{uuid}/accessStatus", item.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status", notNullValue()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
@@ -113,20 +114,20 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
|
||||
// check the first two rows
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", null,
|
||||
null, true,"Add an author", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", null,
|
||||
"You must enter a main title for this item.", false,
|
||||
"Enter the main title of the item.", "dc.title"))))
|
||||
// check a row with multiple fields
|
||||
.andExpect(jsonPath("$.rows[3].fields",
|
||||
contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
|
||||
"You must enter at least the year.", false,
|
||||
null, "You must enter at least the year.", false,
|
||||
"Please give the date", "col-sm-4",
|
||||
"dc.date.issued"),
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", null,
|
||||
null, false,"Enter the name of",
|
||||
"col-sm-8","dc.publisher"))))
|
||||
;
|
||||
@@ -144,18 +145,18 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", null,
|
||||
null, true,"Add an author", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", null,
|
||||
"You must enter a main title for this item.", false,
|
||||
"Enter the main title of the item.", "dc.title"))))
|
||||
.andExpect(jsonPath("$.rows[3].fields",contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue", null,
|
||||
"You must enter at least the year.", false,
|
||||
"Please give the date", "col-sm-4",
|
||||
"dc.date.issued"),
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", null,
|
||||
null, false,"Enter the name of",
|
||||
"col-sm-8","dc.publisher"))));
|
||||
}
|
||||
@@ -220,20 +221,20 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
// dc.subject fields with in separate rows all linked to an authority with different
|
||||
// presentation modes (suggestion, name-lookup, lookup)
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Author",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Author", null,
|
||||
null, true,
|
||||
"Author field that can be associated with an authority providing suggestion",
|
||||
null, "dc.contributor.author", "SolrAuthorAuthority")
|
||||
)))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup-name", "Editor",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup-name", "Editor", null,
|
||||
null, false,
|
||||
"Editor field that can be associated with an authority "
|
||||
+ "providing the special name lookup",
|
||||
null, "dc.contributor.editor", "SolrEditorAuthority")
|
||||
)))
|
||||
.andExpect(jsonPath("$.rows[2].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup", "Subject",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup", "Subject", null,
|
||||
null, true,
|
||||
"Subject field that can be associated with an authority providing lookup",
|
||||
null, "dc.subject", "SolrSubjectAuthority")
|
||||
@@ -266,7 +267,7 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
|
||||
// our test configuration include the dc.type field with a value pair in the 8th row
|
||||
.andExpect(jsonPath("$.rows[7].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("dropdown", "Type",
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("dropdown", "Type", null,
|
||||
null, true,
|
||||
"Select the type(s) of content of the item. To select more than one value in the " +
|
||||
"list, you may have to hold down the \"CTRL\" or \"Shift\" key.",
|
||||
@@ -275,6 +276,35 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFieldWithTypeBindConfig() throws Exception {
|
||||
String token = getAuthToken(admin.getEmail(), password);
|
||||
|
||||
getClient(token).perform(get("/api/config/submissionforms/traditionalpageone"))
|
||||
// The status has to be 200 OK
|
||||
.andExpect(status().isOk())
|
||||
// We expect the content type to be "application/hal+json;charset=UTF-8"
|
||||
.andExpect(content().contentType(contentType))
|
||||
// Check that the JSON root matches the expected "traditionalpageone" input forms
|
||||
.andExpect(jsonPath("$.id", is("traditionalpageone")))
|
||||
.andExpect(jsonPath("$.name", is("traditionalpageone")))
|
||||
.andExpect(jsonPath("$.type", is("submissionform")))
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
|
||||
// check a row with type-bind 'Technical Report'
|
||||
.andExpect(jsonPath("$.rows[5].fields", contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("series", "Series/Report No.",
|
||||
"Technical Report", null, true,
|
||||
"Enter the series and number assigned to this item by your community.",
|
||||
"dc.relation.ispartofseries"))))
|
||||
// check the same row with a NON-matching type-bind 'Article' (expect false)
|
||||
.andExpect(((jsonPath("$.rows[5].fields", not(contains(
|
||||
SubmissionFormFieldMatcher.matchFormFieldDefinition("series", "Series/Report No.",
|
||||
"Article", null, true,
|
||||
"Enter the series and number assigned to this item by your community.",
|
||||
"dc.relation.ispartofseries")))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOpenRelationshipConfig() throws Exception {
|
||||
String token = getAuthToken(admin.getEmail(), password);
|
||||
@@ -352,14 +382,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true,
|
||||
.matchFormFieldDefinition("name", "Autore", null,
|
||||
"\u00C8" + " richiesto almeno un autore", true,
|
||||
"Aggiungi un autore", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Titolo",
|
||||
.matchFormFieldDefinition("onebox", "Titolo", null,
|
||||
"\u00C8" + " necessario inserire un titolo principale per questo item", false,
|
||||
"Inserisci titolo principale di questo item", "dc.title"))))
|
||||
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("dropdown", "Lingua", null, false,
|
||||
.matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
|
||||
"Selezionare la lingua del contenuto principale dell'item."
|
||||
+ " Se la lingua non compare nell'elenco, selezionare (Altro)."
|
||||
+ " Se il contenuto non ha davvero una lingua"
|
||||
@@ -376,14 +407,14 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Автор", "Потрібно ввести хочаб одного автора!",
|
||||
.matchFormFieldDefinition("name", "Автор", null, "Потрібно ввести хочаб одного автора!",
|
||||
true, "Додати автора", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Заголовок",
|
||||
.matchFormFieldDefinition("onebox", "Заголовок", null,
|
||||
"Заговолок файла обов'язковий !", false,
|
||||
"Ввести основний заголовок файла", "dc.title"))))
|
||||
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("dropdown", "Мова", null, false,
|
||||
.matchFormFieldDefinition("dropdown", "Мова", null, null, false,
|
||||
"Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)."
|
||||
+ " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)",
|
||||
null, "dc.language.iso", "common_iso_languages"))));
|
||||
@@ -431,14 +462,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true,
|
||||
.matchFormFieldDefinition("name", "Autore", null,
|
||||
"\u00C8" + " richiesto almeno un autore", true,
|
||||
"Aggiungi un autore", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Titolo",
|
||||
.matchFormFieldDefinition("onebox", "Titolo", null,
|
||||
"\u00C8" + " necessario inserire un titolo principale per questo item", false,
|
||||
"Inserisci titolo principale di questo item", "dc.title"))))
|
||||
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("dropdown", "Lingua", null, false,
|
||||
.matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
|
||||
"Selezionare la lingua del contenuto principale dell'item."
|
||||
+ " Se la lingua non compare nell'elenco, selezionare (Altro)."
|
||||
+ " Se il contenuto non ha davvero una lingua"
|
||||
@@ -455,14 +487,14 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Автор", "Потрібно ввести хочаб одного автора!",
|
||||
.matchFormFieldDefinition("name", "Автор", null, "Потрібно ввести хочаб одного автора!",
|
||||
true, "Додати автора", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Заголовок",
|
||||
.matchFormFieldDefinition("onebox", "Заголовок", null,
|
||||
"Заговолок файла обов'язковий !", false,
|
||||
"Ввести основний заголовок файла", "dc.title"))))
|
||||
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("dropdown", "Мова", null, false,
|
||||
.matchFormFieldDefinition("dropdown", "Мова", null, null, false,
|
||||
"Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)."
|
||||
+ " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)",
|
||||
null, "dc.language.iso", "common_iso_languages"))));
|
||||
@@ -505,14 +537,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true,
|
||||
.matchFormFieldDefinition("name", "Autore", null,
|
||||
"\u00C8" + " richiesto almeno un autore", true,
|
||||
"Aggiungi un autore", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Titolo",
|
||||
.matchFormFieldDefinition("onebox", "Titolo", null,
|
||||
"\u00C8" + " necessario inserire un titolo principale per questo item", false,
|
||||
"Inserisci titolo principale di questo item", "dc.title"))))
|
||||
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("dropdown", "Lingua", null, false,
|
||||
.matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
|
||||
"Selezionare la lingua del contenuto principale dell'item."
|
||||
+ " Se la lingua non compare nell'elenco, selezionare (Altro)."
|
||||
+ " Se il contenuto non ha davvero una lingua"
|
||||
@@ -547,10 +580,10 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Autore", "\u00C8 richiesto almeno un autore", true,
|
||||
.matchFormFieldDefinition("name", "Autore", null, "\u00C8 richiesto almeno un autore", true,
|
||||
"Aggiungi un autore", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Titolo",
|
||||
.matchFormFieldDefinition("onebox", "Titolo", null,
|
||||
"\u00C8 necessario inserire un titolo principale per questo item", false,
|
||||
"Inserisci titolo principale di questo item", "dc.title"))));
|
||||
resetLocalesConfiguration();
|
||||
@@ -582,10 +615,10 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
||||
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
|
||||
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("name", "Autore", "\u00C8 richiesto almeno un autore", true,
|
||||
.matchFormFieldDefinition("name", "Autore", null, "\u00C8 richiesto almeno un autore", true,
|
||||
"Aggiungi un autore", "dc.contributor.author"))))
|
||||
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
|
||||
.matchFormFieldDefinition("onebox", "Titolo",
|
||||
.matchFormFieldDefinition("onebox", "Titolo", null,
|
||||
"\u00C8 necessario inserire un titolo principale per questo item", false,
|
||||
"Inserisci titolo principale di questo item", "dc.title"))));
|
||||
|
||||
|
@@ -1935,6 +1935,141 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
/**
|
||||
* Test the update of metadata for fields configured with type-bind
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void patchUpdateMetadataWithBindTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
//** GIVEN **
|
||||
//1. A community-collection structure with one parent community with sub-community and two collections.
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||
.withName("Sub Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
|
||||
.withTitle("Workspace Item 1")
|
||||
.withIssueDate("2017-10-17")
|
||||
.withSubject("ExtraEntry")
|
||||
.grantLicense()
|
||||
.build();
|
||||
|
||||
//disable file upload mandatory
|
||||
configurationService.setProperty("webui.submit.upload.required", false);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// Try to add isPartOfSeries (type bound to technical report) - this should not work and instead we'll get
|
||||
// no JSON path for that field
|
||||
List<Operation> updateSeries = new ArrayList<Operation>();
|
||||
List<Map<String, String>> seriesValues = new ArrayList<>();
|
||||
Map<String, String> value = new HashMap<String, String>();
|
||||
value.put("value", "New Series");
|
||||
seriesValues.add(value);
|
||||
updateSeries.add(new AddOperation("/sections/traditionalpageone/dc.relation.ispartofseries", seriesValues));
|
||||
|
||||
String patchBody = getPatchContent(updateSeries);
|
||||
|
||||
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
// Check this - we should match an item with no series or type
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, null, null))));
|
||||
|
||||
// Verify that the metadata isn't in the workspace item
|
||||
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
// Check this - we should match an item with no series or type
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, null, null))));
|
||||
|
||||
// Set the type to Technical Report confirm it worked
|
||||
List<Operation> updateType = new ArrayList<>();
|
||||
List<Map<String, String>> typeValues = new ArrayList<>();
|
||||
value = new HashMap<String, String>();
|
||||
value.put("value", "Technical Report");
|
||||
typeValues.add(value);
|
||||
updateType.add(new AddOperation("/sections/traditionalpageone/dc.type", typeValues));
|
||||
patchBody = getPatchContent(updateType);
|
||||
|
||||
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
// Check this - we should now match an item with the expected type and series
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Technical Report",
|
||||
null))));
|
||||
|
||||
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Technical Report",
|
||||
null))));
|
||||
|
||||
// Another test, this time adding the series value should be successful and we'll see the value
|
||||
patchBody = getPatchContent(updateSeries);
|
||||
|
||||
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
// Check this - we should match an item with the expected series and type
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem,
|
||||
"Technical Report", "New Series"))));
|
||||
|
||||
// Verify that the metadata isn't in the workspace item
|
||||
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
// Check this - we should match an item with the expected series and type
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem,
|
||||
"Technical Report", "New Series"))));
|
||||
|
||||
// One final update, to a different type, this should lose the series as we're back to a non-matching type
|
||||
updateType = new ArrayList<>();
|
||||
typeValues = new ArrayList<>();
|
||||
value = new HashMap<String, String>();
|
||||
value.put("value", "Article");
|
||||
typeValues.add(value);
|
||||
updateType.add(new AddOperation("/sections/traditionalpageone/dc.type", typeValues));
|
||||
patchBody = getPatchContent(updateType);
|
||||
|
||||
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
// Check this - we should NOT match an item with the series "New Series"
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Article",
|
||||
null))));
|
||||
|
||||
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.errors").doesNotExist())
|
||||
.andExpect(jsonPath("$",
|
||||
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Article",
|
||||
null))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patchUpdateMetadataForbiddenTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
@@ -50,6 +50,7 @@ public class ItemMatcher {
|
||||
*/
|
||||
public static Matcher<? super Object> matchFullEmbeds() {
|
||||
return matchEmbeds(
|
||||
"accessStatus",
|
||||
"bundles[]",
|
||||
"mappedCollections[]",
|
||||
"owningCollection",
|
||||
@@ -65,6 +66,7 @@ public class ItemMatcher {
|
||||
*/
|
||||
public static Matcher<? super Object> matchLinks(UUID uuid) {
|
||||
return HalMatcher.matchLinks(REST_SERVER_URL + "core/items/" + uuid,
|
||||
"accessStatus",
|
||||
"bundles",
|
||||
"mappedCollections",
|
||||
"owningCollection",
|
||||
|
@@ -10,6 +10,7 @@ package org.dspace.app.rest.matcher;
|
||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@@ -28,13 +29,15 @@ public class SubmissionFormFieldMatcher {
|
||||
|
||||
/**
|
||||
* Shortcut for the
|
||||
* {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, boolean, String, String, String, String)}
|
||||
* {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, String, boolean, String, String, String, String)}
|
||||
* with a null style and vocabulary name
|
||||
*
|
||||
* @param type
|
||||
* the expected input type
|
||||
* @param label
|
||||
* the expected label
|
||||
* @param typeBind
|
||||
* the expected type-bind field(s)
|
||||
* @param mandatoryMessage
|
||||
* the expected mandatoryMessage, can be null. If not empty the fiedl is expected to be flagged as
|
||||
* mandatory
|
||||
@@ -46,21 +49,23 @@ public class SubmissionFormFieldMatcher {
|
||||
* the expected metadata
|
||||
* @return a Matcher for all the condition above
|
||||
*/
|
||||
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String mandatoryMessage,
|
||||
boolean repeatable,
|
||||
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String typeBind,
|
||||
String mandatoryMessage, boolean repeatable,
|
||||
String hints, String metadata) {
|
||||
return matchFormFieldDefinition(type, label, mandatoryMessage, repeatable, hints, null, metadata);
|
||||
return matchFormFieldDefinition(type, label, typeBind, mandatoryMessage, repeatable, hints, null, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for the
|
||||
* {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, boolean, String, String, String, String)}
|
||||
* {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, String, boolean, String, String, String, String)}
|
||||
* with a null controlled vocabulary
|
||||
*
|
||||
* @param type
|
||||
* the expected input type
|
||||
* @param label
|
||||
* the expected label
|
||||
* @param typeBind
|
||||
* the expected type-bind field(s)
|
||||
* @param mandatoryMessage
|
||||
* the expected mandatoryMessage, can be null. If not empty the field is expected to be flagged as
|
||||
* mandatory
|
||||
@@ -75,10 +80,10 @@ public class SubmissionFormFieldMatcher {
|
||||
* the expected metadata
|
||||
* @return a Matcher for all the condition above
|
||||
*/
|
||||
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String mandatoryMessage,
|
||||
boolean repeatable,
|
||||
String hints, String style, String metadata) {
|
||||
return matchFormFieldDefinition(type, label, mandatoryMessage, repeatable, hints, style, metadata, null);
|
||||
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String typeBind,
|
||||
String mandatoryMessage, boolean repeatable, String hints, String style, String metadata) {
|
||||
return matchFormFieldDefinition(type, label, typeBind, mandatoryMessage, repeatable, hints, style, metadata,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +93,8 @@ public class SubmissionFormFieldMatcher {
|
||||
* the expected input type
|
||||
* @param label
|
||||
* the expected label
|
||||
* @param typeBind
|
||||
* the expected type-bind field(s)
|
||||
* @param mandatoryMessage
|
||||
* the expected mandatoryMessage, can be null. If not empty the field is expected to be flagged as
|
||||
* mandatory
|
||||
@@ -100,18 +107,20 @@ public class SubmissionFormFieldMatcher {
|
||||
* missing
|
||||
* @param metadata
|
||||
* the expected metadata
|
||||
* @param controlled vocabulary
|
||||
* @param controlledVocabulary
|
||||
* the expected controlled vocabulary, can be null. If null the corresponding json path is expected to be
|
||||
* missing
|
||||
* @return a Matcher for all the condition above
|
||||
*/
|
||||
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String mandatoryMessage,
|
||||
boolean repeatable, String hints, String style,
|
||||
String metadata, String controlledVocabulary) {
|
||||
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String typeBind,
|
||||
String mandatoryMessage, boolean repeatable,
|
||||
String hints, String style, String metadata,
|
||||
String controlledVocabulary) {
|
||||
return allOf(
|
||||
// check each field definition
|
||||
hasJsonPath("$.input.type", is(type)),
|
||||
hasJsonPath("$.label", containsString(label)),
|
||||
typeBind != null ? hasJsonPath("$.typeBind", contains(typeBind)) : hasNoJsonPath("$.typeBind[0]"),
|
||||
hasJsonPath("$.selectableMetadata[0].metadata", is(metadata)),
|
||||
controlledVocabulary != null ? hasJsonPath("$.selectableMetadata[0].controlledVocabulary",
|
||||
is(controlledVocabulary)) : hasNoJsonPath("$.selectableMetadata[0].controlledVocabulary"),
|
||||
@@ -166,7 +175,7 @@ public class SubmissionFormFieldMatcher {
|
||||
hasJsonPath("$.selectableRelationship.filter", is(filter)),
|
||||
hasJsonPath("$.selectableRelationship.searchConfiguration", is(searchConfiguration)),
|
||||
hasJsonPath("$.selectableRelationship.nameVariants", is(String.valueOf(nameVariants))),
|
||||
matchFormFieldDefinition(type, label, mandatoryMessage, repeatable, hints, metadata));
|
||||
matchFormFieldDefinition(type, label, null, mandatoryMessage, repeatable, hints, metadata));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -82,6 +82,30 @@ public class WorkspaceItemMatcher {
|
||||
matchLinks(witem));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the workspace item has the expected type and series values
|
||||
* (used in type bind evaluation)
|
||||
* @param witem the workspace item
|
||||
* @param type the dc.type value eg. Technical Report
|
||||
* @param series the series value eg. 11-23
|
||||
* @return Matcher result
|
||||
*/
|
||||
public static Matcher matchItemWithTypeAndSeries(WorkspaceItem witem, String type, String series) {
|
||||
return allOf(
|
||||
// Check workspaceitem properties
|
||||
matchProperties(witem),
|
||||
// Check type appears or is null
|
||||
type != null ?
|
||||
hasJsonPath("$.sections.traditionalpageone['dc.type'][0].value", is(type)) :
|
||||
hasNoJsonPath("$.sections.traditionalpageone['dc.type'][0].value"),
|
||||
// Check series as it appears (for type bind testing)
|
||||
series != null ?
|
||||
hasJsonPath("$.sections.traditionalpageone['dc.relation.ispartofseries'][0].value", is(series)) :
|
||||
hasNoJsonPath("$.sections.traditionalpageone['dc.relation.ispartofseries'][0].value"),
|
||||
matchLinks(witem)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the id and type are exposed
|
||||
*
|
||||
|
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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 static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.dspace.access.status.DefaultAccessStatusHelper;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test the AccessStatusRestTest class
|
||||
*/
|
||||
public class AccessStatusRestTest {
|
||||
|
||||
AccessStatusRest accessStatusRest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
accessStatusRest = new AccessStatusRest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessStatusIsNullBeforeStatusSet() throws Exception {
|
||||
assertNull(accessStatusRest.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessStatusIsNotNullAfterStatusSet() throws Exception {
|
||||
accessStatusRest.setStatus(DefaultAccessStatusHelper.UNKNOWN);
|
||||
assertNotNull(accessStatusRest.getStatus());
|
||||
}
|
||||
}
|
@@ -806,6 +806,22 @@ plugin.single.org.dspace.embargo.EmbargoSetter = org.dspace.embargo.DefaultEmbar
|
||||
# implementation of embargo lifter plugin - - replace with local implementation if applicable
|
||||
plugin.single.org.dspace.embargo.EmbargoLifter = org.dspace.embargo.DefaultEmbargoLifter
|
||||
|
||||
# values for the forever embargo date threshold
|
||||
# This threshold date is used in the default access status helper to dermine if an item is
|
||||
# restricted or embargoed based on the start date of the primary (or first) file policies.
|
||||
# In this case, if the policy start date is inferior to the threshold date, the status will
|
||||
# be embargo, else it will be restricted.
|
||||
# You might want to change this threshold based on your needs. For example: some databases
|
||||
# doesn't accept a date superior to 31 december 9999.
|
||||
access.status.embargo.forever.year = 10000
|
||||
access.status.embargo.forever.month = 1
|
||||
access.status.embargo.forever.day = 1
|
||||
|
||||
# implementation of access status helper plugin - replace with local implementation if applicable
|
||||
# This default access status helper provides an item status based on the policies of the primary
|
||||
# bitstream (or first bitstream in the original bundles if no primary file is specified).
|
||||
plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.DefaultAccessStatusHelper
|
||||
|
||||
#### Checksum Checker Settings ####
|
||||
# Default dispatcher in case none specified
|
||||
plugin.single.org.dspace.checker.BitstreamDispatcher=org.dspace.checker.SimpleDispatcher
|
||||
@@ -920,6 +936,11 @@ metadata.hide.dc.description.provenance = true
|
||||
# Defaults to true; If set to 'false', submitter has option to skip upload
|
||||
#webui.submit.upload.required = true
|
||||
|
||||
# Which field should be used for type-bind
|
||||
# Defaults to 'dc.type'; If changing this value, you must also update the related
|
||||
# dspace-angular environment configuration property submission.typeBind.field
|
||||
#submit.type-bind.field = dc.type
|
||||
|
||||
#### Creative Commons settings ######
|
||||
|
||||
# The url to the web service API
|
||||
@@ -1584,6 +1605,12 @@ request.item.type = all
|
||||
# Should all Request Copy emails go to the helpdesk instead of the item submitter?
|
||||
request.item.helpdesk.override = false
|
||||
|
||||
#------------------------------------------------------------------#
|
||||
#------------------SUBMISSION CONFIGURATION------------------------#
|
||||
#------------------------------------------------------------------#
|
||||
# Field to use for type binding, default dc.type
|
||||
submit.type-bind.field = dc.type
|
||||
|
||||
#------------------------------------------------------------------#
|
||||
#-------------------MODULE CONFIGURATIONS--------------------------#
|
||||
#------------------------------------------------------------------#
|
||||
|
@@ -33,6 +33,7 @@ rest.projection.specificLevel.maxEmbed = 5
|
||||
rest.properties.exposed = plugin.named.org.dspace.curate.CurationTask
|
||||
rest.properties.exposed = google.analytics.key
|
||||
rest.properties.exposed = versioning.item.history.include.submitter
|
||||
rest.properties.exposed = submit.type-bind.field
|
||||
|
||||
#---------------------------------------------------------------#
|
||||
# These configs are used by the deprecated REST (v4-6) module #
|
||||
|
@@ -27,6 +27,7 @@
|
||||
<bean id="coreServiceFactory" class="org.dspace.core.factory.CoreServiceFactoryImpl"/>
|
||||
|
||||
|
||||
<bean id="accessStatusServiceFactory" class="org.dspace.access.status.factory.AccessStatusServiceFactoryImpl"/>
|
||||
<bean id="disseminateServiceFactory" class="org.dspace.disseminate.factory.DisseminateServiceFactoryImpl"/>
|
||||
<bean id="embargoServiceFactory" class="org.dspace.embargo.factory.EmbargoServiceFactoryImpl"/>
|
||||
<bean id="ePersonServiceFactory" class="org.dspace.eperson.factory.EPersonServiceFactoryImpl"/>
|
||||
|
@@ -86,8 +86,9 @@
|
||||
|
||||
<bean class="org.dspace.disseminate.CitationDocumentServiceImpl"/>
|
||||
|
||||
<!-- Ensure EmbargoService is initialized properly via init() method -->
|
||||
<!-- Ensure EmbargoService and AccessStatusService are initialized properly via init() method -->
|
||||
<bean class="org.dspace.embargo.EmbargoServiceImpl" init-method="init"/>
|
||||
<bean class="org.dspace.access.status.AccessStatusServiceImpl" init-method="init"/>
|
||||
|
||||
<bean class="org.dspace.eperson.AccountServiceImpl"/>
|
||||
<bean class="org.dspace.eperson.EPersonServiceImpl"/>
|
||||
|
Reference in New Issue
Block a user