mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-18 07:23:08 +00:00
Merge branch 'dspace-origin-master' into w2p-76575_IT-for-deleting-items-and-populating-virtual-md
# Conflicts: # dspace-api/src/main/java/org/dspace/content/RelationshipServiceImpl.java # dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
This commit is contained in:
@@ -145,6 +145,7 @@ public class DCInput {
|
|||||||
private String relationshipType = null;
|
private String relationshipType = null;
|
||||||
private String searchConfiguration = null;
|
private String searchConfiguration = null;
|
||||||
private String filter;
|
private String filter;
|
||||||
|
private List<String> externalSources;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scope of the input sets, this restricts hidden metadata fields from
|
* The scope of the input sets, this restricts hidden metadata fields from
|
||||||
@@ -226,6 +227,15 @@ public class DCInput {
|
|||||||
relationshipType = fieldMap.get("relationship-type");
|
relationshipType = fieldMap.get("relationship-type");
|
||||||
searchConfiguration = fieldMap.get("search-configuration");
|
searchConfiguration = fieldMap.get("search-configuration");
|
||||||
filter = fieldMap.get("filter");
|
filter = fieldMap.get("filter");
|
||||||
|
externalSources = new ArrayList<>();
|
||||||
|
String externalSourcesDef = fieldMap.get("externalsources");
|
||||||
|
if (StringUtils.isNotBlank(externalSourcesDef)) {
|
||||||
|
String[] sources = StringUtils.split(externalSourcesDef, ",");
|
||||||
|
for (String source: sources) {
|
||||||
|
externalSources.add(StringUtils.trim(source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -522,6 +532,10 @@ public class DCInput {
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getExternalSources() {
|
||||||
|
return externalSources;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isQualdropValue() {
|
public boolean isQualdropValue() {
|
||||||
if ("qualdrop_value".equals(getInputType())) {
|
if ("qualdrop_value".equals(getInputType())) {
|
||||||
return true;
|
return true;
|
||||||
|
@@ -10,6 +10,7 @@ package org.dspace.content;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
@@ -19,12 +20,14 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.authorize.service.AuthorizeService;
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.content.dao.RelationshipDAO;
|
import org.dspace.content.dao.RelationshipDAO;
|
||||||
|
import org.dspace.content.service.EntityTypeService;
|
||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
import org.dspace.content.service.RelationshipService;
|
import org.dspace.content.service.RelationshipService;
|
||||||
import org.dspace.content.service.RelationshipTypeService;
|
import org.dspace.content.service.RelationshipTypeService;
|
||||||
import org.dspace.content.virtual.VirtualMetadataPopulator;
|
import org.dspace.content.virtual.VirtualMetadataPopulator;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
public class RelationshipServiceImpl implements RelationshipService {
|
public class RelationshipServiceImpl implements RelationshipService {
|
||||||
@@ -43,6 +46,12 @@ public class RelationshipServiceImpl implements RelationshipService {
|
|||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
protected RelationshipTypeService relationshipTypeService;
|
protected RelationshipTypeService relationshipTypeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EntityTypeService entityTypeService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RelationshipMetadataService relationshipMetadataService;
|
private RelationshipMetadataService relationshipMetadataService;
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -90,6 +99,7 @@ public class RelationshipServiceImpl implements RelationshipService {
|
|||||||
Relationship relationshipToReturn = relationshipDAO.create(context, relationship);
|
Relationship relationshipToReturn = relationshipDAO.create(context, relationship);
|
||||||
updatePlaceInRelationship(context, relationshipToReturn);
|
updatePlaceInRelationship(context, relationshipToReturn);
|
||||||
update(context, relationshipToReturn);
|
update(context, relationshipToReturn);
|
||||||
|
updateItemsInRelationship(context, relationship);
|
||||||
return relationshipToReturn;
|
return relationshipToReturn;
|
||||||
} else {
|
} else {
|
||||||
throw new AuthorizeException(
|
throw new AuthorizeException(
|
||||||
@@ -234,6 +244,10 @@ public class RelationshipServiceImpl implements RelationshipService {
|
|||||||
Integer maxCardinality,
|
Integer maxCardinality,
|
||||||
RelationshipType relationshipType,
|
RelationshipType relationshipType,
|
||||||
boolean isLeft) throws SQLException {
|
boolean isLeft) throws SQLException {
|
||||||
|
if (maxCardinality == null) {
|
||||||
|
//no need to check the relationships
|
||||||
|
return true;
|
||||||
|
}
|
||||||
List<Relationship> rightRelationships = findByItemAndRelationshipType(context, itemToProcess, relationshipType,
|
List<Relationship> rightRelationships = findByItemAndRelationshipType(context, itemToProcess, relationshipType,
|
||||||
isLeft);
|
isLeft);
|
||||||
if (maxCardinality != null && rightRelationships.size() >= maxCardinality) {
|
if (maxCardinality != null && rightRelationships.size() >= maxCardinality) {
|
||||||
@@ -243,7 +257,8 @@ public class RelationshipServiceImpl implements RelationshipService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifyEntityTypes(Item itemToProcess, EntityType entityTypeToProcess) {
|
private boolean verifyEntityTypes(Item itemToProcess, EntityType entityTypeToProcess) {
|
||||||
List<MetadataValue> list = itemService.getMetadata(itemToProcess, "relationship", "type", null, Item.ANY);
|
List<MetadataValue> list = itemService.getMetadata(itemToProcess, "relationship", "type",
|
||||||
|
null, Item.ANY, false);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -362,6 +377,7 @@ public class RelationshipServiceImpl implements RelationshipService {
|
|||||||
authorizeService.authorizeActionBoolean(context, relationship.getRightItem(), Constants.WRITE)) {
|
authorizeService.authorizeActionBoolean(context, relationship.getRightItem(), Constants.WRITE)) {
|
||||||
relationshipDAO.delete(context, relationship);
|
relationshipDAO.delete(context, relationship);
|
||||||
updatePlaceInRelationship(context, relationship);
|
updatePlaceInRelationship(context, relationship);
|
||||||
|
updateItemsInRelationship(context, relationship);
|
||||||
} else {
|
} else {
|
||||||
throw new AuthorizeException(
|
throw new AuthorizeException(
|
||||||
"You do not have write rights on this relationship's items");
|
"You do not have write rights on this relationship's items");
|
||||||
@@ -369,6 +385,128 @@ public class RelationshipServiceImpl implements RelationshipService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to ensure discovery is updated for the 2 items
|
||||||
|
* This method is used when creating, modifying or deleting a relationship
|
||||||
|
* The virtual metadata of the 2 items may need to be updated, so they should be re-indexed
|
||||||
|
*
|
||||||
|
* @param context The relevant DSpace context
|
||||||
|
* @param relationship The relationship which has been created, updated or deleted
|
||||||
|
* @throws SQLException If something goes wrong
|
||||||
|
*/
|
||||||
|
private void updateItemsInRelationship(Context context, Relationship relationship) throws SQLException {
|
||||||
|
// Since this call is performed after creating, updating or deleting the relationships, the permissions have
|
||||||
|
// already been verified. The following updateItem calls can however call the
|
||||||
|
// ItemService.update() functions which would fail if the user doesn't have permission on both items.
|
||||||
|
// Since we allow this edits to happen under these circumstances, we need to turn off the
|
||||||
|
// authorization system here so that this failure doesn't happen when the items need to be update
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
try {
|
||||||
|
// Set a limit on the total amount of items to update at once during a relationship change
|
||||||
|
int max = configurationService.getIntProperty("relationship.update.relateditems.max", 20);
|
||||||
|
// Set a limit on the total depth of relationships to traverse during a relationship change
|
||||||
|
int maxDepth = configurationService.getIntProperty("relationship.update.relateditems.maxdepth", 5);
|
||||||
|
// This is the list containing all items which will have changes to their virtual metadata
|
||||||
|
List<Item> itemsToUpdate = new LinkedList<>();
|
||||||
|
itemsToUpdate.add(relationship.getLeftItem());
|
||||||
|
itemsToUpdate.add(relationship.getRightItem());
|
||||||
|
|
||||||
|
if (containsVirtualMetadata(relationship.getRelationshipType().getLeftwardType())) {
|
||||||
|
findModifiedDiscoveryItemsForCurrentItem(context, relationship.getLeftItem(),
|
||||||
|
itemsToUpdate, max, 0, maxDepth);
|
||||||
|
}
|
||||||
|
if (containsVirtualMetadata(relationship.getRelationshipType().getRightwardType())) {
|
||||||
|
findModifiedDiscoveryItemsForCurrentItem(context, relationship.getRightItem(),
|
||||||
|
itemsToUpdate, max, 0, maxDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Item item : itemsToUpdate) {
|
||||||
|
updateItem(context, item);
|
||||||
|
}
|
||||||
|
} catch (AuthorizeException e) {
|
||||||
|
log.error("Authorization Exception while authorization has been disabled", e);
|
||||||
|
} finally {
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for items whose metadata should be updated in discovery and adds them to itemsToUpdate
|
||||||
|
* It starts from the given item, excludes items already in itemsToUpdate (they're already handled),
|
||||||
|
* and can be limited in amount of items or depth to update
|
||||||
|
*/
|
||||||
|
private void findModifiedDiscoveryItemsForCurrentItem(Context context, Item item, List<Item> itemsToUpdate,
|
||||||
|
int max, int currentDepth, int maxDepth)
|
||||||
|
throws SQLException {
|
||||||
|
if (itemsToUpdate.size() >= max) {
|
||||||
|
log.debug("skipping findModifiedDiscoveryItemsForCurrentItem for item "
|
||||||
|
+ item.getID() + " due to " + itemsToUpdate.size() + " items to be updated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentDepth == maxDepth) {
|
||||||
|
log.debug("skipping findModifiedDiscoveryItemsForCurrentItem for item "
|
||||||
|
+ item.getID() + " due to " + currentDepth + " depth");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String entityTypeStringFromMetadata = relationshipMetadataService.getEntityTypeStringFromMetadata(item);
|
||||||
|
EntityType actualEntityType = entityTypeService.findByEntityType(context, entityTypeStringFromMetadata);
|
||||||
|
// Get all types of relations for the current item
|
||||||
|
List<RelationshipType> relationshipTypes = relationshipTypeService.findByEntityType(context, actualEntityType);
|
||||||
|
for (RelationshipType relationshipType : relationshipTypes) {
|
||||||
|
//are we searching for items where the current item is on the left
|
||||||
|
boolean isLeft = relationshipType.getLeftType().equals(actualEntityType);
|
||||||
|
|
||||||
|
// Verify whether there's virtual metadata configured for this type of relation
|
||||||
|
// If it's not present, we don't need to update the virtual metadata in discovery
|
||||||
|
String typeToSearchInVirtualMetadata;
|
||||||
|
if (isLeft) {
|
||||||
|
typeToSearchInVirtualMetadata = relationshipType.getRightwardType();
|
||||||
|
} else {
|
||||||
|
typeToSearchInVirtualMetadata = relationshipType.getLeftwardType();
|
||||||
|
}
|
||||||
|
if (containsVirtualMetadata(typeToSearchInVirtualMetadata)) {
|
||||||
|
// we have a relationship type where the items attached to the current item will inherit
|
||||||
|
// virtual metadata from the current item
|
||||||
|
// retrieving the actual relationships so the related items can be updated
|
||||||
|
List<Relationship> list = findByItemAndRelationshipType(context, item, relationshipType, isLeft);
|
||||||
|
for (Relationship foundRelationship : list) {
|
||||||
|
Item nextItem;
|
||||||
|
if (isLeft) {
|
||||||
|
// current item on the left, next item is on the right
|
||||||
|
nextItem = foundRelationship.getRightItem();
|
||||||
|
} else {
|
||||||
|
nextItem = foundRelationship.getLeftItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify it hasn't been processed yet
|
||||||
|
if (!itemsToUpdate.contains(nextItem)) {
|
||||||
|
itemsToUpdate.add(nextItem);
|
||||||
|
// continue the process for the next item, it may also inherit item from the current item
|
||||||
|
findModifiedDiscoveryItemsForCurrentItem(context, nextItem,
|
||||||
|
itemsToUpdate, max, currentDepth + 1, maxDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("skipping " + relationshipType.getID()
|
||||||
|
+ " in findModifiedDiscoveryItemsForCurrentItem for item "
|
||||||
|
+ item.getID() + " because no relevant virtual metadata was found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies whether there is virtual metadata generated for the given relationship
|
||||||
|
* If no such virtual metadata exists, there's no need to update the items in discovery
|
||||||
|
* @param typeToSearchInVirtualMetadata a leftWardType or rightWardType of a relationship type
|
||||||
|
* This can be e.g. isAuthorOfPublication
|
||||||
|
* @return true if there is virtual metadata for this relationship
|
||||||
|
*/
|
||||||
|
private boolean containsVirtualMetadata(String typeToSearchInVirtualMetadata) {
|
||||||
|
return virtualMetadataPopulator.getMap().containsKey(typeToSearchInVirtualMetadata)
|
||||||
|
&& virtualMetadataPopulator.getMap().get(typeToSearchInVirtualMetadata).size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts virtual metadata from RelationshipMetadataValue objects to actual item metadata.
|
* Converts virtual metadata from RelationshipMetadataValue objects to actual item metadata.
|
||||||
*
|
*
|
||||||
|
@@ -18,9 +18,7 @@ import org.dspace.content.Item;
|
|||||||
import org.dspace.content.Relationship;
|
import org.dspace.content.Relationship;
|
||||||
import org.dspace.content.RelationshipType;
|
import org.dspace.content.RelationshipType;
|
||||||
import org.dspace.content.service.EntityService;
|
import org.dspace.content.service.EntityService;
|
||||||
import org.dspace.content.service.EntityTypeService;
|
|
||||||
import org.dspace.content.service.RelationshipService;
|
import org.dspace.content.service.RelationshipService;
|
||||||
import org.dspace.content.service.RelationshipTypeService;
|
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
@@ -36,15 +34,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
*/
|
*/
|
||||||
public class Related implements VirtualMetadataConfiguration {
|
public class Related implements VirtualMetadataConfiguration {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RelationshipTypeService relationshipTypeService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RelationshipService relationshipService;
|
private RelationshipService relationshipService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EntityTypeService entityTypeService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EntityService entityService;
|
private EntityService entityService;
|
||||||
|
|
||||||
@@ -172,12 +164,12 @@ public class Related implements VirtualMetadataConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Relationship relationship : relationships) {
|
for (Relationship relationship : relationships) {
|
||||||
if (relationship.getRelationshipType().getLeftType() == entityType) {
|
if (relationship.getRelationshipType().getLeftType().equals(entityType)) {
|
||||||
if (place == null || relationship.getLeftPlace() == place) {
|
if (place == null || relationship.getLeftPlace() == place) {
|
||||||
Item otherItem = relationship.getRightItem();
|
Item otherItem = relationship.getRightItem();
|
||||||
return virtualMetadataConfiguration.getValues(context, otherItem);
|
return virtualMetadataConfiguration.getValues(context, otherItem);
|
||||||
}
|
}
|
||||||
} else if (relationship.getRelationshipType().getRightType() == entityType) {
|
} else if (relationship.getRelationshipType().getRightType().equals(entityType)) {
|
||||||
if (place == null || relationship.getRightPlace() == place) {
|
if (place == null || relationship.getRightPlace() == place) {
|
||||||
Item otherItem = relationship.getLeftItem();
|
Item otherItem = relationship.getLeftItem();
|
||||||
return virtualMetadataConfiguration.getValues(context, otherItem);
|
return virtualMetadataConfiguration.getValues(context, otherItem);
|
||||||
|
@@ -44,7 +44,7 @@ public class DiscoveryConfigurationService {
|
|||||||
public DiscoveryConfiguration getDiscoveryConfiguration(IndexableObject dso) {
|
public DiscoveryConfiguration getDiscoveryConfiguration(IndexableObject dso) {
|
||||||
String name;
|
String name;
|
||||||
if (dso == null) {
|
if (dso == null) {
|
||||||
name = "site";
|
name = "default";
|
||||||
} else if (dso instanceof IndexableDSpaceObject) {
|
} else if (dso instanceof IndexableDSpaceObject) {
|
||||||
name = ((IndexableDSpaceObject) dso).getIndexedObject().getHandle();
|
name = ((IndexableDSpaceObject) dso).getIndexedObject().getHandle();
|
||||||
} else {
|
} else {
|
||||||
|
@@ -63,6 +63,7 @@
|
|||||||
<dc-qualifier>author</dc-qualifier>
|
<dc-qualifier>author</dc-qualifier>
|
||||||
<input-type>name</input-type>
|
<input-type>name</input-type>
|
||||||
</linked-metadata-field>
|
</linked-metadata-field>
|
||||||
|
<externalsources>orcid,my_staff_db</externalsources>
|
||||||
<required></required>
|
<required></required>
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
@@ -242,6 +243,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
|||||||
<filter>creativework.publisher:somepublishername</filter>
|
<filter>creativework.publisher:somepublishername</filter>
|
||||||
<label>Journal</label>
|
<label>Journal</label>
|
||||||
<hint>Select the journal related to this volume.</hint>
|
<hint>Select the journal related to this volume.</hint>
|
||||||
|
<externalsources></externalsources>
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
|
@@ -17,10 +17,13 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.dspace.authorize.service.AuthorizeService;
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.content.dao.RelationshipDAO;
|
import org.dspace.content.dao.RelationshipDAO;
|
||||||
|
import org.dspace.content.service.EntityTypeService;
|
||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.content.service.RelationshipTypeService;
|
||||||
import org.dspace.content.virtual.VirtualMetadataPopulator;
|
import org.dspace.content.virtual.VirtualMetadataPopulator;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -56,6 +59,18 @@ public class RelationshipServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private VirtualMetadataPopulator virtualMetadataPopulator;
|
private VirtualMetadataPopulator virtualMetadataPopulator;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RelationshipTypeService relationshipTypeService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RelationshipMetadataService relationshipMetadataService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private EntityTypeService entityTypeService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
relationshipsList = new ArrayList<>();
|
relationshipsList = new ArrayList<>();
|
||||||
@@ -212,8 +227,8 @@ public class RelationshipServiceImplTest {
|
|||||||
when(metsList.get(0).getValue()).thenReturn("Entitylabel");
|
when(metsList.get(0).getValue()).thenReturn("Entitylabel");
|
||||||
when(relationshipService
|
when(relationshipService
|
||||||
.findByItemAndRelationshipType(context, leftItem, testRel, true)).thenReturn(leftTypelist);
|
.findByItemAndRelationshipType(context, leftItem, testRel, true)).thenReturn(leftTypelist);
|
||||||
when(itemService.getMetadata(leftItem, "relationship", "type", null, Item.ANY)).thenReturn(metsList);
|
when(itemService.getMetadata(leftItem, "relationship", "type", null, Item.ANY, false)).thenReturn(metsList);
|
||||||
when(itemService.getMetadata(rightItem, "relationship", "type", null, Item.ANY)).thenReturn(metsList);
|
when(itemService.getMetadata(rightItem, "relationship", "type", null, Item.ANY, false)).thenReturn(metsList);
|
||||||
when(relationshipDAO.create(any(), any())).thenReturn(relationship);
|
when(relationshipDAO.create(any(), any())).thenReturn(relationship);
|
||||||
|
|
||||||
// The reported Relationship should match our defined relationship
|
// The reported Relationship should match our defined relationship
|
||||||
@@ -290,8 +305,8 @@ public class RelationshipServiceImplTest {
|
|||||||
relationship = getRelationship(leftItem, rightItem, testRel, 0,0);
|
relationship = getRelationship(leftItem, rightItem, testRel, 0,0);
|
||||||
|
|
||||||
// Mock the state of objects utilized in update() to meet the success criteria of the invocation
|
// Mock the state of objects utilized in update() to meet the success criteria of the invocation
|
||||||
when(itemService.getMetadata(leftItem, "relationship", "type", null, Item.ANY)).thenReturn(metsList);
|
when(itemService.getMetadata(leftItem, "relationship", "type", null, Item.ANY, false)).thenReturn(metsList);
|
||||||
when(itemService.getMetadata(rightItem, "relationship", "type", null, Item.ANY)).thenReturn(metsList);
|
when(itemService.getMetadata(rightItem, "relationship", "type", null, Item.ANY, false)).thenReturn(metsList);
|
||||||
when(authorizeService.authorizeActionBoolean(context, relationship.getLeftItem(),
|
when(authorizeService.authorizeActionBoolean(context, relationship.getLeftItem(),
|
||||||
Constants.WRITE)).thenReturn(true);
|
Constants.WRITE)).thenReturn(true);
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dspace.app.rest.model.ScopeEnum;
|
import org.dspace.app.rest.model.ScopeEnum;
|
||||||
import org.dspace.app.rest.model.SubmissionFormFieldRest;
|
import org.dspace.app.rest.model.SubmissionFormFieldRest;
|
||||||
@@ -174,6 +175,9 @@ public class SubmissionFormConverter implements DSpaceConverter<DCInputSet, Subm
|
|||||||
selectableRelationship.setFilter(dcinput.getFilter());
|
selectableRelationship.setFilter(dcinput.getFilter());
|
||||||
selectableRelationship.setSearchConfiguration(dcinput.getSearchConfiguration());
|
selectableRelationship.setSearchConfiguration(dcinput.getSearchConfiguration());
|
||||||
selectableRelationship.setNameVariants(String.valueOf(dcinput.areNameVariantsAllowed()));
|
selectableRelationship.setNameVariants(String.valueOf(dcinput.areNameVariantsAllowed()));
|
||||||
|
if (CollectionUtils.isNotEmpty(dcinput.getExternalSources())) {
|
||||||
|
selectableRelationship.setExternalSources(dcinput.getExternalSources());
|
||||||
|
}
|
||||||
return selectableRelationship;
|
return selectableRelationship;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,6 +12,8 @@ import java.util.LinkedList;
|
|||||||
import org.dspace.app.rest.ExternalSourcesRestController;
|
import org.dspace.app.rest.ExternalSourcesRestController;
|
||||||
import org.dspace.app.rest.link.HalLinkFactory;
|
import org.dspace.app.rest.link.HalLinkFactory;
|
||||||
import org.dspace.app.rest.model.hateoas.ExternalSourceResource;
|
import org.dspace.app.rest.model.hateoas.ExternalSourceResource;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.hateoas.Link;
|
import org.springframework.hateoas.Link;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -21,14 +23,18 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ExternalSourceHalLinkFactory extends
|
public class ExternalSourceHalLinkFactory extends
|
||||||
HalLinkFactory<ExternalSourceResource, ExternalSourcesRestController> {
|
HalLinkFactory<ExternalSourceResource, ExternalSourcesRestController> {
|
||||||
|
@Autowired
|
||||||
|
ConfigurationService configurationService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addLinks(ExternalSourceResource halResource, Pageable pageable, LinkedList<Link> list)
|
protected void addLinks(ExternalSourceResource halResource, Pageable pageable, LinkedList<Link> list)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
list.add(buildLink("entries", getMethodOn()
|
String dspaceServerUrl = configurationService.getProperty("dspace.server.url");
|
||||||
.getExternalSourceEntries(halResource.getContent().getName(), "", null, null, null)));
|
list.add(
|
||||||
|
buildLink("entries", dspaceServerUrl + "/api/integration/externalsources/" +
|
||||||
|
halResource.getContent().getName() + "/entries"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,6 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.model.query;
|
package org.dspace.app.rest.model.query;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -95,4 +98,19 @@ public enum RestSearchOperator {
|
|||||||
public String getDspaceOperator() {
|
public String getDspaceOperator() {
|
||||||
return dspaceOperator;
|
return dspaceOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of dspace operators of this enum's values, plus "query" which is also allowed, but will be
|
||||||
|
* transformed in {@link org.dspace.app.rest.converter.query.SearchQueryConverter} to any of the others
|
||||||
|
*
|
||||||
|
* @return List of dspace operators of this enum's values, plus "query"
|
||||||
|
*/
|
||||||
|
public static List<String> getListOfAllowedSearchOperatorStrings() {
|
||||||
|
List<String> allowedSearchOperatorStrings = new ArrayList<>();
|
||||||
|
for (RestSearchOperator restSearchOperator: Arrays.asList(RestSearchOperator.values())) {
|
||||||
|
allowedSearchOperatorStrings.add(restSearchOperator.getDspaceOperator());
|
||||||
|
}
|
||||||
|
allowedSearchOperatorStrings.add("query");
|
||||||
|
return allowedSearchOperatorStrings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.model.submit;
|
package org.dspace.app.rest.model.submit;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SelectableRelationship REST Resource. It is not addressable directly, only
|
* The SelectableRelationship REST Resource. It is not addressable directly, only
|
||||||
* used as inline object in the InputForm resource.
|
* used as inline object in the InputForm resource.
|
||||||
@@ -24,6 +26,7 @@ public class SelectableRelationship {
|
|||||||
private String filter;
|
private String filter;
|
||||||
private String searchConfiguration;
|
private String searchConfiguration;
|
||||||
private String nameVariants;
|
private String nameVariants;
|
||||||
|
private List<String> externalSources;
|
||||||
|
|
||||||
public void setRelationshipType(String relationshipType) {
|
public void setRelationshipType(String relationshipType) {
|
||||||
this.relationshipType = relationshipType;
|
this.relationshipType = relationshipType;
|
||||||
@@ -56,4 +59,12 @@ public class SelectableRelationship {
|
|||||||
public String getNameVariants() {
|
public String getNameVariants() {
|
||||||
return nameVariants;
|
return nameVariants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getExternalSources() {
|
||||||
|
return externalSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExternalSources(List<String> externalSources) {
|
||||||
|
this.externalSources = externalSources;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,8 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
|
import org.dspace.app.rest.model.query.RestSearchOperator;
|
||||||
import org.dspace.app.rest.parameter.SearchFilter;
|
import org.dspace.app.rest.parameter.SearchFilter;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
@@ -29,13 +31,15 @@ public class SearchFilterResolver implements HandlerMethodArgumentResolver {
|
|||||||
public static final String SEARCH_FILTER_PREFIX = "f.";
|
public static final String SEARCH_FILTER_PREFIX = "f.";
|
||||||
public static final String FILTER_OPERATOR_SEPARATOR = ",";
|
public static final String FILTER_OPERATOR_SEPARATOR = ",";
|
||||||
|
|
||||||
|
public static final List<String> ALLOWED_SEARCH_OPERATORS =
|
||||||
|
RestSearchOperator.getListOfAllowedSearchOperatorStrings();
|
||||||
|
|
||||||
public boolean supportsParameter(final MethodParameter parameter) {
|
public boolean supportsParameter(final MethodParameter parameter) {
|
||||||
return parameter.getParameterType().equals(SearchFilter.class) || isSearchFilterList(parameter);
|
return parameter.getParameterType().equals(SearchFilter.class) || isSearchFilterList(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer,
|
public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer,
|
||||||
final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory)
|
final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) {
|
||||||
throws Exception {
|
|
||||||
List<SearchFilter> result = new LinkedList<>();
|
List<SearchFilter> result = new LinkedList<>();
|
||||||
|
|
||||||
Iterator<String> parameterNames = webRequest.getParameterNames();
|
Iterator<String> parameterNames = webRequest.getParameterNames();
|
||||||
@@ -48,7 +52,7 @@ public class SearchFilterResolver implements HandlerMethodArgumentResolver {
|
|||||||
for (String value : webRequest.getParameterValues(parameterName)) {
|
for (String value : webRequest.getParameterValues(parameterName)) {
|
||||||
String filterValue = StringUtils.substringBeforeLast(value, FILTER_OPERATOR_SEPARATOR);
|
String filterValue = StringUtils.substringBeforeLast(value, FILTER_OPERATOR_SEPARATOR);
|
||||||
String filterOperator = StringUtils.substringAfterLast(value, FILTER_OPERATOR_SEPARATOR);
|
String filterOperator = StringUtils.substringAfterLast(value, FILTER_OPERATOR_SEPARATOR);
|
||||||
|
this.checkIfValidOperator(filterOperator);
|
||||||
result.add(new SearchFilter(filterName, filterOperator, filterValue));
|
result.add(new SearchFilter(filterName, filterOperator, filterValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +65,19 @@ public class SearchFilterResolver implements HandlerMethodArgumentResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkIfValidOperator(String filterOperator) {
|
||||||
|
if (StringUtils.isNotBlank(filterOperator)) {
|
||||||
|
if (!ALLOWED_SEARCH_OPERATORS.contains(filterOperator.trim())) {
|
||||||
|
throw new UnprocessableEntityException(
|
||||||
|
"The operator can't be \"" + filterOperator + "\", must be the of one of: " +
|
||||||
|
String.join(", ", ALLOWED_SEARCH_OPERATORS));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new UnprocessableEntityException(
|
||||||
|
"The operator can't be empty, must be the one of: " + String.join(", ", ALLOWED_SEARCH_OPERATORS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isSearchFilterList(final MethodParameter parameter) {
|
private boolean isSearchFilterList(final MethodParameter parameter) {
|
||||||
return parameter.getParameterType().equals(List.class)
|
return parameter.getParameterType().equals(List.class)
|
||||||
&& parameter.getGenericParameterType() instanceof ParameterizedType
|
&& parameter.getGenericParameterType() instanceof ParameterizedType
|
||||||
|
@@ -124,15 +124,6 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
|
|||||||
Relationship relationship = relationshipService.create(context, leftItem, rightItem,
|
Relationship relationship = relationshipService.create(context, leftItem, rightItem,
|
||||||
relationshipType, -1, -1,
|
relationshipType, -1, -1,
|
||||||
leftwardValue, rightwardValue);
|
leftwardValue, rightwardValue);
|
||||||
// The above if check deals with the case that a Relationship can be created if the user has write
|
|
||||||
// rights on one of the two items. The following updateItem calls can however call the
|
|
||||||
// ItemService.update() functions which would fail if the user doesn't have permission on both items.
|
|
||||||
// Since we allow this creation to happen under these circumstances, we need to turn off the
|
|
||||||
// authorization system here so that this failure doesn't happen when the items need to be update
|
|
||||||
context.turnOffAuthorisationSystem();
|
|
||||||
relationshipService.updateItem(context, relationship.getLeftItem());
|
|
||||||
relationshipService.updateItem(context, relationship.getRightItem());
|
|
||||||
context.restoreAuthSystemState();
|
|
||||||
return converter.toRest(relationship, utils.obtainProjection());
|
return converter.toRest(relationship, utils.obtainProjection());
|
||||||
} else {
|
} else {
|
||||||
throw new AccessDeniedException("You do not have write rights on this relationship's items");
|
throw new AccessDeniedException("You do not have write rights on this relationship's items");
|
||||||
|
@@ -2370,7 +2370,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsWithQueryOperatorContains() throws Exception {
|
public void discoverSearchObjectsWithQueryOperatorContains_query() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
@@ -2445,7 +2445,83 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsWithQueryOperatorNotContains() throws Exception {
|
public void discoverSearchObjectsWithQueryOperatorContains() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
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();
|
||||||
|
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
|
||||||
|
//2. Three public items that are readable by Anonymous with different subjects
|
||||||
|
Item publicItem1 = ItemBuilder.createItem(context, col1)
|
||||||
|
.withTitle("Test")
|
||||||
|
.withIssueDate("2010-10-17")
|
||||||
|
.withAuthor("Smith, Donald")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem2 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Test 2")
|
||||||
|
.withIssueDate("1990-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("Testing, Works")
|
||||||
|
.withSubject("TestingForMore").withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem3 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Public item 2")
|
||||||
|
.withIssueDate("2010-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("test,test")
|
||||||
|
.withAuthor("test2, test2").withAuthor("Maybe, Maybe")
|
||||||
|
.withSubject("AnotherTest").withSubject("TestingForMore")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
UUID scope = col2.getID();
|
||||||
|
//** WHEN **
|
||||||
|
//An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
//With the given search filter
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "test,contains"))
|
||||||
|
//** THEN **
|
||||||
|
//The status has to be 200 OK
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
//The type has to be 'discover'
|
||||||
|
.andExpect(jsonPath("$.type", is("discover")))
|
||||||
|
//The page object needs to look like this
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||||
|
PageMatcher.pageEntry(0, 20)
|
||||||
|
)))
|
||||||
|
//The search results have to contain the items that match the searchFilter
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects", Matchers.containsInAnyOrder(
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Test"),
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Test 2")
|
||||||
|
)))
|
||||||
|
//These facets have to show up in the embedded.facets section as well with the given hasMore property
|
||||||
|
// because we don't exceed their default limit for a hasMore true (the default is 10)
|
||||||
|
.andExpect(jsonPath("$._embedded.facets", Matchers.containsInAnyOrder(
|
||||||
|
FacetEntryMatcher.authorFacet(false),
|
||||||
|
FacetEntryMatcher.entityTypeFacet(false),
|
||||||
|
FacetEntryMatcher.subjectFacet(false),
|
||||||
|
FacetEntryMatcher.dateIssuedFacet(false),
|
||||||
|
FacetEntryMatcher.hasContentInOriginalBundleFacet(false)
|
||||||
|
)))
|
||||||
|
//There always needs to be a self link available
|
||||||
|
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")))
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithQueryOperatorNotContains_query() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
@@ -2518,6 +2594,81 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithQueryOperatorNotContains() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
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();
|
||||||
|
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
|
||||||
|
//2. Three public items that are readable by Anonymous with different subjects
|
||||||
|
Item publicItem1 = ItemBuilder.createItem(context, col1)
|
||||||
|
.withTitle("Test")
|
||||||
|
.withIssueDate("2010-10-17")
|
||||||
|
.withAuthor("Smith, Donald")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem2 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Test 2")
|
||||||
|
.withIssueDate("1990-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("Testing, Works")
|
||||||
|
.withSubject("TestingForMore").withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem3 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Public item 2")
|
||||||
|
.withIssueDate("2010-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("test,test")
|
||||||
|
.withAuthor("test2, test2").withAuthor("Maybe, Maybe")
|
||||||
|
.withSubject("AnotherTest").withSubject("TestingForMore")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
UUID scope = col2.getID();
|
||||||
|
//** WHEN **
|
||||||
|
//An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
//With the given search filter
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "test,notcontains"))
|
||||||
|
//** THEN **
|
||||||
|
//The status has to be 200 OK
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
//The type has to be 'discover'
|
||||||
|
.andExpect(jsonPath("$.type", is("discover")))
|
||||||
|
//The page object needs to look like this
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||||
|
PageMatcher.pageEntry(0, 20)
|
||||||
|
)))
|
||||||
|
//The search results have to contain the items that match the searchFilter
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects", Matchers.hasItem(
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Public item 2")
|
||||||
|
)))
|
||||||
|
//These facets have to show up in the embedded.facets section as well with the given hasMore property
|
||||||
|
// because we don't exceed their default limit for a hasMore true (the default is 10)
|
||||||
|
.andExpect(jsonPath("$._embedded.facets", Matchers.containsInAnyOrder(
|
||||||
|
FacetEntryMatcher.authorFacet(false),
|
||||||
|
FacetEntryMatcher.entityTypeFacet(false),
|
||||||
|
FacetEntryMatcher.subjectFacet(false),
|
||||||
|
FacetEntryMatcher.dateIssuedFacet(false),
|
||||||
|
FacetEntryMatcher.hasContentInOriginalBundleFacet(false)
|
||||||
|
)))
|
||||||
|
//There always needs to be a self link available
|
||||||
|
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")))
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsTestForMinMaxValues() throws Exception {
|
public void discoverSearchObjectsTestForMinMaxValues() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
@@ -2673,7 +2824,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsWithQueryOperatorEquals() throws Exception {
|
public void discoverSearchObjectsWithQueryOperatorEquals_query() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
@@ -2747,7 +2898,82 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsWithQueryOperatorNotEquals() throws Exception {
|
public void discoverSearchObjectsWithQueryOperatorEquals() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
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();
|
||||||
|
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
|
||||||
|
//2. Three public items that are readable by Anonymous with different subjects
|
||||||
|
Item publicItem1 = ItemBuilder.createItem(context, col1)
|
||||||
|
.withTitle("Test")
|
||||||
|
.withIssueDate("2010-10-17")
|
||||||
|
.withAuthor("Smith, Donald")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem2 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Test 2")
|
||||||
|
.withIssueDate("1990-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("Testing, Works")
|
||||||
|
.withSubject("TestingForMore").withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem3 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Public item 2")
|
||||||
|
.withIssueDate("2010-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("test,test")
|
||||||
|
.withAuthor("test2, test2").withAuthor("Maybe, Maybe")
|
||||||
|
.withSubject("AnotherTest").withSubject("TestingForMore")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
UUID scope = col2.getID();
|
||||||
|
//** WHEN **
|
||||||
|
//An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
//With the given search filter
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "Test,equals"))
|
||||||
|
//** THEN **
|
||||||
|
//The status has to be 200 OK
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
//The type has to be 'discover'
|
||||||
|
.andExpect(jsonPath("$.type", is("discover")))
|
||||||
|
//The page object needs to look like this
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||||
|
PageMatcher.pageEntry(0, 20)
|
||||||
|
)))
|
||||||
|
//The search results have to contain the items that match the searchFilter
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects", Matchers.containsInAnyOrder(
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Test")
|
||||||
|
)))
|
||||||
|
//These facets have to show up in the embedded.facets section as well with the given hasMore property
|
||||||
|
// because we don't exceed their default limit for a hasMore true (the default is 10)
|
||||||
|
.andExpect(jsonPath("$._embedded.facets", Matchers.containsInAnyOrder(
|
||||||
|
FacetEntryMatcher.authorFacet(false),
|
||||||
|
FacetEntryMatcher.entityTypeFacet(false),
|
||||||
|
FacetEntryMatcher.subjectFacet(false),
|
||||||
|
FacetEntryMatcher.dateIssuedFacet(false),
|
||||||
|
FacetEntryMatcher.hasContentInOriginalBundleFacet(false)
|
||||||
|
)))
|
||||||
|
//There always needs to be a self link available
|
||||||
|
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")))
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithQueryOperatorNotEquals_query() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
@@ -2822,7 +3048,83 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsWithQueryOperatorNotAuthority() throws Exception {
|
public void discoverSearchObjectsWithQueryOperatorNotEquals() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
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();
|
||||||
|
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
|
||||||
|
//2. Three public items that are readable by Anonymous with different subjects
|
||||||
|
Item publicItem1 = ItemBuilder.createItem(context, col1)
|
||||||
|
.withTitle("Test")
|
||||||
|
.withIssueDate("2010-10-17")
|
||||||
|
.withAuthor("Smith, Donald")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem2 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Test 2")
|
||||||
|
.withIssueDate("1990-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("Testing, Works")
|
||||||
|
.withSubject("TestingForMore").withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem3 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Public item 2")
|
||||||
|
.withIssueDate("2010-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("test,test")
|
||||||
|
.withAuthor("test2, test2").withAuthor("Maybe, Maybe")
|
||||||
|
.withSubject("AnotherTest").withSubject("TestingForMore")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
UUID scope = col2.getID();
|
||||||
|
//** WHEN **
|
||||||
|
//An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
//With the given search filter
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "Test,notequals"))
|
||||||
|
//** THEN **
|
||||||
|
//The status has to be 200 OK
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
//The type has to be 'discover'
|
||||||
|
.andExpect(jsonPath("$.type", is("discover")))
|
||||||
|
//The page object needs to look like this
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||||
|
PageMatcher.pageEntry(0, 20)
|
||||||
|
)))
|
||||||
|
//The search results have to contain the items that match the searchFilter
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects", Matchers.hasItems(
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Test 2"),
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Public item 2")
|
||||||
|
)))
|
||||||
|
//These facets have to show up in the embedded.facets section as well with the given hasMore property
|
||||||
|
// because we don't exceed their default limit for a hasMore true (the default is 10)
|
||||||
|
.andExpect(jsonPath("$._embedded.facets", Matchers.containsInAnyOrder(
|
||||||
|
FacetEntryMatcher.authorFacet(false),
|
||||||
|
FacetEntryMatcher.entityTypeFacet(false),
|
||||||
|
FacetEntryMatcher.subjectFacet(false),
|
||||||
|
FacetEntryMatcher.dateIssuedFacet(false),
|
||||||
|
FacetEntryMatcher.hasContentInOriginalBundleFacet(false)
|
||||||
|
)))
|
||||||
|
//There always needs to be a self link available
|
||||||
|
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")))
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithQueryOperatorNotAuthority_query() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
@@ -2895,6 +3197,108 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithQueryOperatorNotAuthority() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
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();
|
||||||
|
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
|
||||||
|
//2. Three public items that are readable by Anonymous with different subjects
|
||||||
|
Item publicItem1 = ItemBuilder.createItem(context, col1)
|
||||||
|
.withTitle("Test")
|
||||||
|
.withIssueDate("2010-10-17")
|
||||||
|
.withAuthor("Smith, Donald")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem2 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Test 2")
|
||||||
|
.withIssueDate("1990-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("Testing, Works")
|
||||||
|
.withSubject("TestingForMore").withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Item publicItem3 = ItemBuilder.createItem(context, col2)
|
||||||
|
.withTitle("Public item 2")
|
||||||
|
.withIssueDate("2010-02-13")
|
||||||
|
.withAuthor("Smith, Maria").withAuthor("Doe, Jane").withAuthor("test,test")
|
||||||
|
.withAuthor("test2, test2").withAuthor("Maybe, Maybe")
|
||||||
|
.withSubject("AnotherTest").withSubject("TestingForMore")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
UUID scope = col2.getID();
|
||||||
|
//** WHEN **
|
||||||
|
//An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
//With the given search filter
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "test,notauthority"))
|
||||||
|
//** THEN **
|
||||||
|
//The status has to be 200 OK
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
//The type has to be 'discover'
|
||||||
|
.andExpect(jsonPath("$.type", is("discover")))
|
||||||
|
//The page object needs to look like this
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||||
|
PageMatcher.pageEntry(0, 20)
|
||||||
|
)))
|
||||||
|
//The search results have to contain the items that match the searchFilter
|
||||||
|
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects", Matchers.hasItem(
|
||||||
|
SearchResultMatcher.matchOnItemName("item", "items", "Public item 2")
|
||||||
|
)))
|
||||||
|
//These facets have to show up in the embedded.facets section as well with the given hasMore property
|
||||||
|
// because we don't exceed their default limit for a hasMore true (the default is 10)
|
||||||
|
.andExpect(jsonPath("$._embedded.facets", Matchers.containsInAnyOrder(
|
||||||
|
FacetEntryMatcher.authorFacet(false),
|
||||||
|
FacetEntryMatcher.entityTypeFacet(false),
|
||||||
|
FacetEntryMatcher.subjectFacet(false),
|
||||||
|
FacetEntryMatcher.dateIssuedFacet(false),
|
||||||
|
FacetEntryMatcher.hasContentInOriginalBundleFacet(false)
|
||||||
|
)))
|
||||||
|
//There always needs to be a self link available
|
||||||
|
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")))
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithMissingQueryOperator() throws Exception {
|
||||||
|
//** WHEN **
|
||||||
|
// An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
// With the given search filter where there is the filter operator missing in the value (must be of form
|
||||||
|
// <:filter-value>,<:filter-operator>)
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "test"))
|
||||||
|
//** THEN **
|
||||||
|
//Will result in 422 status because of missing filter operator
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void discoverSearchObjectsWithNotValidQueryOperator() throws Exception {
|
||||||
|
//** WHEN **
|
||||||
|
// An anonymous user browses this endpoint to find the the objects in the system
|
||||||
|
// With the given search filter where there is a non-valid filter operator given (must be of form
|
||||||
|
// <:filter-value>,<:filter-operator> where the filter operator is one of: “contains”, “notcontains”, "equals"
|
||||||
|
// “notequals”, “authority”, “notauthority”, "query”); see enum RestSearchOperator
|
||||||
|
getClient().perform(get("/api/discover/search/objects")
|
||||||
|
.param("f.title", "test,operator"))
|
||||||
|
//** THEN **
|
||||||
|
//Will result in 422 status because of non-valid filter operator
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoverSearchObjectsTestWithDateIssuedQueryTest() throws Exception {
|
public void discoverSearchObjectsTestWithDateIssuedQueryTest() throws Exception {
|
||||||
//We turn off the authorization system in order to create the structure as defined below
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
@@ -4478,7 +4882,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
.perform(get("/api/discover/search/objects")
|
.perform(get("/api/discover/search/objects")
|
||||||
.param("configuration", "administrativeView")
|
.param("configuration", "administrativeView")
|
||||||
.param("query", "Test")
|
.param("query", "Test")
|
||||||
.param("f.withdrawn", "true")
|
.param("f.withdrawn", "true,contains")
|
||||||
)
|
)
|
||||||
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -4497,7 +4901,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
.perform(get("/api/discover/search/objects")
|
.perform(get("/api/discover/search/objects")
|
||||||
.param("configuration", "administrativeView")
|
.param("configuration", "administrativeView")
|
||||||
.param("query", "Test")
|
.param("query", "Test")
|
||||||
.param("f.withdrawn", "false")
|
.param("f.withdrawn", "false,contains")
|
||||||
)
|
)
|
||||||
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -4517,7 +4921,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
.perform(get("/api/discover/search/objects")
|
.perform(get("/api/discover/search/objects")
|
||||||
.param("configuration", "administrativeView")
|
.param("configuration", "administrativeView")
|
||||||
.param("query", "Test")
|
.param("query", "Test")
|
||||||
.param("f.discoverable", "true")
|
.param("f.discoverable", "true,contains")
|
||||||
)
|
)
|
||||||
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -4537,7 +4941,7 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
.perform(get("/api/discover/search/objects")
|
.perform(get("/api/discover/search/objects")
|
||||||
.param("configuration", "administrativeView")
|
.param("configuration", "administrativeView")
|
||||||
.param("query", "Test")
|
.param("query", "Test")
|
||||||
.param("f.discoverable", "false")
|
.param("f.discoverable", "false,contains")
|
||||||
)
|
)
|
||||||
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@@ -9,6 +9,8 @@ package org.dspace.app.rest;
|
|||||||
|
|
||||||
import static com.jayway.jsonpath.JsonPath.read;
|
import static com.jayway.jsonpath.JsonPath.read;
|
||||||
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
@@ -18,6 +20,7 @@ import static org.hamcrest.Matchers.is;
|
|||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
@@ -33,6 +36,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.solr.client.solrj.SolrQuery;
|
||||||
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
import org.dspace.app.rest.matcher.PageMatcher;
|
import org.dspace.app.rest.matcher.PageMatcher;
|
||||||
import org.dspace.app.rest.matcher.RelationshipMatcher;
|
import org.dspace.app.rest.matcher.RelationshipMatcher;
|
||||||
import org.dspace.app.rest.model.RelationshipRest;
|
import org.dspace.app.rest.model.RelationshipRest;
|
||||||
@@ -41,13 +46,16 @@ import org.dspace.authorize.service.AuthorizeService;
|
|||||||
import org.dspace.builder.CollectionBuilder;
|
import org.dspace.builder.CollectionBuilder;
|
||||||
import org.dspace.builder.CommunityBuilder;
|
import org.dspace.builder.CommunityBuilder;
|
||||||
import org.dspace.builder.EPersonBuilder;
|
import org.dspace.builder.EPersonBuilder;
|
||||||
|
import org.dspace.builder.EntityTypeBuilder;
|
||||||
import org.dspace.builder.ItemBuilder;
|
import org.dspace.builder.ItemBuilder;
|
||||||
import org.dspace.builder.MetadataFieldBuilder;
|
import org.dspace.builder.MetadataFieldBuilder;
|
||||||
import org.dspace.builder.RelationshipBuilder;
|
import org.dspace.builder.RelationshipBuilder;
|
||||||
|
import org.dspace.builder.RelationshipTypeBuilder;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.EntityType;
|
import org.dspace.content.EntityType;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.MetadataField;
|
||||||
import org.dspace.content.MetadataSchema;
|
import org.dspace.content.MetadataSchema;
|
||||||
import org.dspace.content.MetadataValue;
|
import org.dspace.content.MetadataValue;
|
||||||
import org.dspace.content.Relationship;
|
import org.dspace.content.Relationship;
|
||||||
@@ -59,6 +67,7 @@ import org.dspace.content.service.MetadataSchemaService;
|
|||||||
import org.dspace.content.service.RelationshipTypeService;
|
import org.dspace.content.service.RelationshipTypeService;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.I18nUtil;
|
import org.dspace.core.I18nUtil;
|
||||||
|
import org.dspace.discovery.MockSolrSearchCore;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -86,6 +95,8 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MetadataSchemaService metadataSchemaService;
|
private MetadataSchemaService metadataSchemaService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockSolrSearchCore mockSolrSearchCore;
|
||||||
private Community parentCommunity;
|
private Community parentCommunity;
|
||||||
private Community child1;
|
private Community child1;
|
||||||
|
|
||||||
@@ -2674,6 +2685,151 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVirtualMdInRESTAndSolrDoc() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
// Create entity types if needed
|
||||||
|
EntityType journalEntityType = entityTypeService.findByEntityType(context, "Journal");
|
||||||
|
if (journalEntityType == null) {
|
||||||
|
journalEntityType = EntityTypeBuilder.createEntityTypeBuilder(context, "Journal").build();
|
||||||
|
}
|
||||||
|
EntityType journalVolumeEntityType = entityTypeService.findByEntityType(context, "JournalVolume");
|
||||||
|
if (journalVolumeEntityType == null) {
|
||||||
|
journalVolumeEntityType = EntityTypeBuilder.createEntityTypeBuilder(context, "JournalVolume").build();
|
||||||
|
}
|
||||||
|
EntityType journalIssueEntityType = entityTypeService.findByEntityType(context, "JournalIssue");
|
||||||
|
if (journalIssueEntityType == null) {
|
||||||
|
journalIssueEntityType = EntityTypeBuilder.createEntityTypeBuilder(context, "JournalIssue").build();
|
||||||
|
}
|
||||||
|
EntityType publicationEntityType = entityTypeService.findByEntityType(context, "Publication");
|
||||||
|
if (publicationEntityType == null) {
|
||||||
|
publicationEntityType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create relationship types if needed
|
||||||
|
RelationshipType isPublicationOfJournalIssue = relationshipTypeService
|
||||||
|
.findbyTypesAndTypeName(context, journalIssueEntityType, publicationEntityType,
|
||||||
|
"isPublicationOfJournalIssue", "isJournalIssueOfPublication");
|
||||||
|
if (isPublicationOfJournalIssue == null) {
|
||||||
|
isPublicationOfJournalIssue = RelationshipTypeBuilder.createRelationshipTypeBuilder(context,
|
||||||
|
journalIssueEntityType, publicationEntityType, "isPublicationOfJournalIssue",
|
||||||
|
"isJournalIssueOfPublication", null, null, null, null).build();
|
||||||
|
}
|
||||||
|
RelationshipType isIssueOfJournalVolume = relationshipTypeService
|
||||||
|
.findbyTypesAndTypeName(context, journalVolumeEntityType, journalIssueEntityType,
|
||||||
|
"isIssueOfJournalVolume", "isJournalVolumeOfIssue");
|
||||||
|
if (isIssueOfJournalVolume == null) {
|
||||||
|
isIssueOfJournalVolume = RelationshipTypeBuilder.createRelationshipTypeBuilder(context,
|
||||||
|
journalVolumeEntityType, journalIssueEntityType, "isIssueOfJournalVolume",
|
||||||
|
"isJournalVolumeOfIssue", null, null, null, null).build();
|
||||||
|
} else {
|
||||||
|
// Otherwise error in destroy methods when removing Journal Issue-Journal Volume relationship
|
||||||
|
// since the rightMinCardinality constraint would be violated upon deletion
|
||||||
|
isIssueOfJournalVolume.setRightMinCardinality(0);
|
||||||
|
}
|
||||||
|
RelationshipType isVolumeOfJournal = relationshipTypeService
|
||||||
|
.findbyTypesAndTypeName(context, journalEntityType, journalVolumeEntityType,
|
||||||
|
"isVolumeOfJournal", "isJournalOfVolume");
|
||||||
|
if (isVolumeOfJournal == null) {
|
||||||
|
isVolumeOfJournal = RelationshipTypeBuilder.createRelationshipTypeBuilder(context,
|
||||||
|
journalEntityType, journalVolumeEntityType, "isVolumeOfJournal", "isJournalOfVolume",
|
||||||
|
null, null, null, null).build();
|
||||||
|
} else {
|
||||||
|
// Otherwise error in destroy methods when removing Journal Volume - Journal relationship
|
||||||
|
// since the rightMinCardinality constraint would be violated upon deletion
|
||||||
|
isVolumeOfJournal.setRightMinCardinality(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create virtual metadata fields if needed
|
||||||
|
MetadataSchema journalSchema = metadataSchemaService.find(context, "journal");
|
||||||
|
if (journalSchema == null) {
|
||||||
|
journalSchema = metadataSchemaService.create(context, "journal", "journal");
|
||||||
|
}
|
||||||
|
String journalTitleVirtualMdField = "journal.title";
|
||||||
|
MetadataField journalTitleField = metadataFieldService.findByString(context, journalTitleVirtualMdField, '.');
|
||||||
|
if (journalTitleField == null) {
|
||||||
|
metadataFieldService.create(context, journalSchema, "title", null, "Journal Title");
|
||||||
|
}
|
||||||
|
|
||||||
|
String journalTitle = "Journal Title Test";
|
||||||
|
|
||||||
|
// Create entity items
|
||||||
|
Item journal =
|
||||||
|
ItemBuilder.createItem(context, col1).withRelationshipType("Journal").withTitle(journalTitle).build();
|
||||||
|
Item journalVolume =
|
||||||
|
ItemBuilder.createItem(context, col1).withRelationshipType("JournalVolume").withTitle("JournalVolume")
|
||||||
|
.build();
|
||||||
|
Item journalIssue =
|
||||||
|
ItemBuilder.createItem(context, col1).withRelationshipType("JournalIssue").withTitle("JournalIssue")
|
||||||
|
.build();
|
||||||
|
Item publication =
|
||||||
|
ItemBuilder.createItem(context, col1).withRelationshipType("Publication").withTitle("Publication").build();
|
||||||
|
|
||||||
|
// Link Publication-Journal Issue
|
||||||
|
RelationshipBuilder.createRelationshipBuilder(context, journalIssue, publication, isPublicationOfJournalIssue)
|
||||||
|
.build();
|
||||||
|
// Link Journal Issue-Journal Volume
|
||||||
|
RelationshipBuilder.createRelationshipBuilder(context, journalVolume, journalIssue, isIssueOfJournalVolume)
|
||||||
|
.build();
|
||||||
|
mockSolrSearchCore.getSolr().commit(false, false);
|
||||||
|
|
||||||
|
// Verify Publication item via REST does not contain virtual md journal.title
|
||||||
|
getClient().perform(get("/api/core/items/" + publication.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.metadata." + journalTitleVirtualMdField).doesNotExist());
|
||||||
|
|
||||||
|
// Verify Publication item via Solr does not contain virtual md journal.title
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setQuery("search.resourceid:" + publication.getID());
|
||||||
|
QueryResponse queryResponse = mockSolrSearchCore.getSolr().query(solrQuery);
|
||||||
|
assertThat(queryResponse.getResults().size(), equalTo(1));
|
||||||
|
assertNull(queryResponse.getResults().get(0).getFieldValues(journalTitleVirtualMdField));
|
||||||
|
|
||||||
|
// Link Journal Volume - Journal
|
||||||
|
RelationshipBuilder.createRelationshipBuilder(context, journal, journalVolume, isVolumeOfJournal).build();
|
||||||
|
mockSolrSearchCore.getSolr().commit(false, false);
|
||||||
|
|
||||||
|
// Verify Publication item via REST does contain virtual md journal.title
|
||||||
|
getClient().perform(get("/api/core/items/" + publication.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.metadata", allOf(
|
||||||
|
matchMetadata(journalTitleVirtualMdField, journalTitle))));
|
||||||
|
|
||||||
|
// Verify Publication item via Solr contains virtual md journal.title
|
||||||
|
queryResponse = mockSolrSearchCore.getSolr().query(solrQuery);
|
||||||
|
assertThat(queryResponse.getResults().size(), equalTo(1));
|
||||||
|
assertEquals(journalTitle,
|
||||||
|
((List) queryResponse.getResults().get(0).getFieldValues(journalTitleVirtualMdField)).get(0));
|
||||||
|
|
||||||
|
// Verify Journal Volume item via REST also contains virtual md journal.title
|
||||||
|
getClient().perform(get("/api/core/items/" + journalVolume.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.metadata", allOf(
|
||||||
|
matchMetadata(journalTitleVirtualMdField, journalTitle))));
|
||||||
|
|
||||||
|
// Verify Journal Volume item via Solr also contains virtual md journal.title
|
||||||
|
solrQuery.setQuery("search.resourceid:" + journalVolume.getID());
|
||||||
|
queryResponse = mockSolrSearchCore.getSolr().query(solrQuery);
|
||||||
|
assertThat(queryResponse.getResults().size(), equalTo(1));
|
||||||
|
assertEquals(journalTitle,
|
||||||
|
((List) queryResponse.getResults().get(0).getFieldValues(journalTitleVirtualMdField)).get(0));
|
||||||
|
|
||||||
|
// Verify Journal Issue item via REST also contains virtual md journal.title
|
||||||
|
getClient().perform(get("/api/core/items/" + journalIssue.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.metadata", allOf(
|
||||||
|
matchMetadata(journalTitleVirtualMdField, journalTitle))));
|
||||||
|
|
||||||
|
// Verify Journal Issue item via Solr also contains virtual md journal.title
|
||||||
|
solrQuery.setQuery("search.resourceid:" + journalIssue.getID());
|
||||||
|
queryResponse = mockSolrSearchCore.getSolr().query(solrQuery);
|
||||||
|
assertThat(queryResponse.getResults().size(), equalTo(1));
|
||||||
|
assertEquals(journalTitle,
|
||||||
|
((List) queryResponse.getResults().get(0).getFieldValues(journalTitleVirtualMdField)).get(0));
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findOneTestWrongUUID() throws Exception {
|
public void findOneTestWrongUUID() throws Exception {
|
||||||
getClient().perform(get("/api/core/relationships/" + 1000))
|
getClient().perform(get("/api/core/relationships/" + 1000))
|
||||||
|
@@ -11,6 +11,7 @@ import static org.hamcrest.Matchers.contains;
|
|||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
@@ -591,6 +592,47 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
|
|||||||
resetLocalesConfiguration();
|
resetLocalesConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleExternalSourcesTest() 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 the external sources of the first field in the first row
|
||||||
|
.andExpect(jsonPath("$.rows[0].fields[0].selectableRelationship.externalSources",
|
||||||
|
contains(is("orcid"), is("my_staff_db"))))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noExternalSourcesTest() throws Exception {
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/config/submissionforms/journalVolumeStep"))
|
||||||
|
//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 "journalVolumeStep" input forms
|
||||||
|
.andExpect(jsonPath("$.id", is("journalVolumeStep")))
|
||||||
|
.andExpect(jsonPath("$.name", is("journalVolumeStep")))
|
||||||
|
.andExpect(jsonPath("$.type", is("submissionform")))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers
|
||||||
|
.startsWith(REST_SERVER_URL + "config/submissionforms/journalVolumeStep")))
|
||||||
|
// check the external sources of the first field in the first row
|
||||||
|
.andExpect(jsonPath("$.rows[0].fields[0].selectableRelationship.externalSources", nullValue()))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
private void resetLocalesConfiguration() throws DCInputsReaderException {
|
private void resetLocalesConfiguration() throws DCInputsReaderException {
|
||||||
configurationService.setProperty("default.locale","en");
|
configurationService.setProperty("default.locale","en");
|
||||||
configurationService.setProperty("webui.supported.locales",null);
|
configurationService.setProperty("webui.supported.locales",null);
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
package org.dspace.app.rest.matcher;
|
package org.dspace.app.rest.matcher;
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.dspace.app.rest.test.AbstractControllerIntegrationTest.REST_SERVER_URL;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
@@ -22,7 +23,9 @@ public class ExternalSourceMatcher {
|
|||||||
return allOf(
|
return allOf(
|
||||||
hasJsonPath("$.id", is(id)),
|
hasJsonPath("$.id", is(id)),
|
||||||
hasJsonPath("$.name", is(name)),
|
hasJsonPath("$.name", is(name)),
|
||||||
hasJsonPath("$.hierarchical", is(hierarchical))
|
hasJsonPath("$.hierarchical", is(hierarchical)),
|
||||||
|
hasJsonPath("$._links.entries.href", is(REST_SERVER_URL +
|
||||||
|
"integration/externalsources/" + name + "/entries"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
dspace/config/modules/relationship.cfg
Normal file
16
dspace/config/modules/relationship.cfg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#---------------------------------------------------------------#
|
||||||
|
#-----------------RELATIONSHIP CONFIGURATIONS-------------------#
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
# Configuration properties used by the RelationshipService #
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
|
||||||
|
# The maximum number of items to be updated when adjusting a relationship.
|
||||||
|
# This includes the relationship’s left and right item.
|
||||||
|
# If the max is below 2, the relationship’s left and right item will still be processed. Defaults to 20
|
||||||
|
# relationship.update.relateditems.max = 20
|
||||||
|
|
||||||
|
# The maximum depth of relationships to traverse.
|
||||||
|
# A value of 5 means that a maximum of 5 levels (relationships) deep will be scanned for updates on both the left side
|
||||||
|
# and the right side. Indirectly related items requiring more than 5 items will be skipped. Defaults to 5
|
||||||
|
# relationship.update.relateditems.maxdepth = 5
|
||||||
|
|
@@ -7,19 +7,19 @@
|
|||||||
<!ELEMENT form (row)+ >
|
<!ELEMENT form (row)+ >
|
||||||
<!ELEMENT row (field|relation-field)+ >
|
<!ELEMENT row (field|relation-field)+ >
|
||||||
<!ATTLIST form name NMTOKEN #REQUIRED>
|
<!ATTLIST form name NMTOKEN #REQUIRED>
|
||||||
|
|
||||||
<!ELEMENT field (dc-schema, dc-element, dc-qualifier?, language?, repeatable?, label, style?, type-bind?, input-type, hint, required?, regex?, vocabulary?, visibility?, readonly?) >
|
<!ELEMENT field (dc-schema, dc-element, dc-qualifier?, language?, repeatable?, label, style?, type-bind?, input-type, hint, required?, regex?, vocabulary?, visibility?, readonly?) >
|
||||||
<!ELEMENT dc-schema (#PCDATA) >
|
<!ELEMENT dc-schema (#PCDATA) >
|
||||||
<!ELEMENT dc-element (#PCDATA) >
|
<!ELEMENT dc-element (#PCDATA) >
|
||||||
<!ELEMENT dc-qualifier (#PCDATA) >
|
<!ELEMENT dc-qualifier (#PCDATA) >
|
||||||
<!ELEMENT language (#PCDATA)>
|
<!ELEMENT language (#PCDATA)>
|
||||||
<!ELEMENT type-bind (#PCDATA) >
|
<!ELEMENT type-bind (#PCDATA) >
|
||||||
|
|
||||||
<!ELEMENT repeatable (#PCDATA) >
|
<!ELEMENT repeatable (#PCDATA) >
|
||||||
<!ELEMENT label (#PCDATA) >
|
<!ELEMENT label (#PCDATA) >
|
||||||
<!ELEMENT style (#PCDATA) >
|
<!ELEMENT style (#PCDATA) >
|
||||||
<!ELEMENT input-type (#PCDATA)>
|
<!ELEMENT input-type (#PCDATA)>
|
||||||
|
|
||||||
<!ELEMENT hint (#PCDATA) >
|
<!ELEMENT hint (#PCDATA) >
|
||||||
<!ELEMENT required (#PCDATA)>
|
<!ELEMENT required (#PCDATA)>
|
||||||
<!ELEMENT name-variants (#PCDATA)>
|
<!ELEMENT name-variants (#PCDATA)>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<!ATTLIST value-pairs value-pairs-name CDATA #REQUIRED
|
<!ATTLIST value-pairs value-pairs-name CDATA #REQUIRED
|
||||||
dc-term CDATA #REQUIRED
|
dc-term CDATA #REQUIRED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ELEMENT pair (displayed-value,stored-value) >
|
<!ELEMENT pair (displayed-value,stored-value) >
|
||||||
<!ELEMENT displayed-value (#PCDATA)>
|
<!ELEMENT displayed-value (#PCDATA)>
|
||||||
<!ELEMENT stored-value (#PCDATA)>
|
<!ELEMENT stored-value (#PCDATA)>
|
||||||
@@ -49,16 +49,17 @@
|
|||||||
|
|
||||||
<!ELEMENT vocabulary (#PCDATA) >
|
<!ELEMENT vocabulary (#PCDATA) >
|
||||||
|
|
||||||
<!ATTLIST vocabulary closed (true|false) "false">
|
<!ATTLIST vocabulary closed (true|false) "false">
|
||||||
|
|
||||||
<!ELEMENT visibility (#PCDATA) >
|
<!ELEMENT visibility (#PCDATA) >
|
||||||
|
|
||||||
<!ATTLIST language value-pairs-name CDATA #IMPLIED>
|
<!ATTLIST language value-pairs-name CDATA #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT readonly (#PCDATA) >
|
<!ELEMENT readonly (#PCDATA) >
|
||||||
|
|
||||||
<!ELEMENT relationship-type (#PCDATA) >
|
<!ELEMENT relationship-type (#PCDATA) >
|
||||||
<!ELEMENT search-configuration (#PCDATA) >
|
<!ELEMENT search-configuration (#PCDATA) >
|
||||||
<!ELEMENT filter (#PCDATA) >
|
<!ELEMENT filter (#PCDATA) >
|
||||||
<!ELEMENT relation-field (relationship-type, search-configuration, filter?, repeatable?, name-variants?, label, type-bind?, hint, linked-metadata-field?, required?, visibility?)>
|
<!ELEMENT externalsources (#PCDATA) >
|
||||||
|
<!ELEMENT relation-field (relationship-type, search-configuration, filter?, repeatable?, name-variants?, label, type-bind?, hint, linked-metadata-field?, externalsources?, required?, regex?, visibility?)>
|
||||||
<!ELEMENT linked-metadata-field (dc-schema, dc-element, dc-qualifier?, input-type)>
|
<!ELEMENT linked-metadata-field (dc-schema, dc-element, dc-qualifier?, input-type)>
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
<label>Title</label>
|
<label>Title</label>
|
||||||
<input-type>onebox</input-type>
|
<input-type>onebox</input-type>
|
||||||
<hint>Enter the name of the file.</hint>
|
<hint>Enter the name of the file.</hint>
|
||||||
<required>You must enter a main title for this item.</required>
|
<required>You must enter a name for this file</required>
|
||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
@@ -54,14 +54,19 @@
|
|||||||
<search-configuration>person</search-configuration>
|
<search-configuration>person</search-configuration>
|
||||||
<repeatable>true</repeatable>
|
<repeatable>true</repeatable>
|
||||||
<label>Author</label>
|
<label>Author</label>
|
||||||
<hint>Add an author</hint>
|
<hint>Enter the author's name (Family name, Given names).</hint>
|
||||||
<linked-metadata-field>
|
<linked-metadata-field>
|
||||||
<dc-schema>dc</dc-schema>
|
<dc-schema>dc</dc-schema>
|
||||||
<dc-element>contributor</dc-element>
|
<dc-element>contributor</dc-element>
|
||||||
<dc-qualifier>author</dc-qualifier>
|
<dc-qualifier>author</dc-qualifier>
|
||||||
<input-type>name</input-type>
|
<input-type>onebox</input-type>
|
||||||
</linked-metadata-field>
|
</linked-metadata-field>
|
||||||
<required>At least one author (plain text or relationship) is required</required>
|
<externalsources>orcid</externalsources>
|
||||||
|
<required></required>
|
||||||
|
<!-- You may choose to validate author names via a Regular Expression if it's appropriate for
|
||||||
|
your institution. The below regex requires a comma to be present in the author field.
|
||||||
|
However, this is disabled by default to support organizations as authors, etc. -->
|
||||||
|
<!--<regex>\w+(,)+\w+</regex>-->
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
@@ -74,7 +79,6 @@
|
|||||||
<input-type>onebox</input-type>
|
<input-type>onebox</input-type>
|
||||||
<hint>Enter the main title of the item.</hint>
|
<hint>Enter the main title of the item.</hint>
|
||||||
<required>You must enter a main title for this item.</required>
|
<required>You must enter a main title for this item.</required>
|
||||||
<!-- <language value-pairs-name="common_iso_languages">true</language> -->
|
|
||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
@@ -99,8 +103,7 @@
|
|||||||
<style>col-sm-4</style>
|
<style>col-sm-4</style>
|
||||||
<input-type>date</input-type>
|
<input-type>date</input-type>
|
||||||
<hint>Please give the date of previous publication or public distribution.
|
<hint>Please give the date of previous publication or public distribution.
|
||||||
You can leave out the day and/or month if they aren't
|
You can leave out the day and/or month if they aren't applicable.
|
||||||
applicable.
|
|
||||||
</hint>
|
</hint>
|
||||||
<required>You must enter at least the year.</required>
|
<required>You must enter at least the year.</required>
|
||||||
</field>
|
</field>
|
||||||
@@ -241,6 +244,15 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="peopleStep">
|
<form name="peopleStep">
|
||||||
|
<row>
|
||||||
|
<relation-field>
|
||||||
|
<relationship-type>isPublicationOfAuthor</relationship-type>
|
||||||
|
<search-configuration>publication</search-configuration>
|
||||||
|
<label>Publication</label>
|
||||||
|
<hint>import a publicaton</hint>
|
||||||
|
<externalsources>pubmed</externalsources>
|
||||||
|
</relation-field>
|
||||||
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
<dc-schema>person</dc-schema>
|
<dc-schema>person</dc-schema>
|
||||||
@@ -494,6 +506,7 @@
|
|||||||
<filter>creativework.publisher:somepublishername</filter>
|
<filter>creativework.publisher:somepublishername</filter>
|
||||||
<label>Journal</label>
|
<label>Journal</label>
|
||||||
<hint>Select the journal related to this volume.</hint>
|
<hint>Select the journal related to this volume.</hint>
|
||||||
|
<externalsources>sherpaJournal</externalsources>
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
@@ -625,6 +638,7 @@
|
|||||||
<dc-qualifier>author</dc-qualifier>
|
<dc-qualifier>author</dc-qualifier>
|
||||||
<input-type>name</input-type>
|
<input-type>name</input-type>
|
||||||
</linked-metadata-field>
|
</linked-metadata-field>
|
||||||
|
<externalsources>orcid</externalsources>
|
||||||
<required>At least one author (plain text or relationship) is required</required>
|
<required>At least one author (plain text or relationship) is required</required>
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
@@ -852,7 +866,7 @@
|
|||||||
</row>
|
</row>
|
||||||
|
|
||||||
<!-- Additional Subject with specific taxonomy (like mesh, fos, ...) -->
|
<!-- Additional Subject with specific taxonomy (like mesh, fos, ...) -->
|
||||||
|
|
||||||
<!-- example for FOS -->
|
<!-- example for FOS -->
|
||||||
<!-- <row>
|
<!-- <row>
|
||||||
<field>
|
<field>
|
||||||
@@ -893,6 +907,7 @@
|
|||||||
<dc-element>relation</dc-element>
|
<dc-element>relation</dc-element>
|
||||||
<input-type>onebox</input-type>
|
<input-type>onebox</input-type>
|
||||||
</linked-metadata-field>
|
</linked-metadata-field>
|
||||||
|
<externalsources></externalsources>
|
||||||
<required></required>
|
<required></required>
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
@@ -1043,6 +1058,7 @@
|
|||||||
<dc-qualifier>name</dc-qualifier>
|
<dc-qualifier>name</dc-qualifier>
|
||||||
<input-type>onebox</input-type>
|
<input-type>onebox</input-type>
|
||||||
</linked-metadata-field>
|
</linked-metadata-field>
|
||||||
|
<externalsources></externalsources>
|
||||||
<required>One funding agency is required</required>
|
<required>One funding agency is required</required>
|
||||||
</relation-field>
|
</relation-field>
|
||||||
</row>
|
</row>
|
||||||
@@ -1053,14 +1069,14 @@
|
|||||||
<label>Name of the Funding Stream</label>
|
<label>Name of the Funding Stream</label>
|
||||||
<input-type>onebox</input-type>
|
<input-type>onebox</input-type>
|
||||||
<hint>Enter the name of the funding stream of the project</hint>
|
<hint>Enter the name of the funding stream of the project</hint>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
<field>
|
||||||
<dc-schema>dc</dc-schema>
|
<dc-schema>dc</dc-schema>
|
||||||
<dc-element>identifier</dc-element>
|
<dc-element>identifier</dc-element>
|
||||||
<label>Award number</label>
|
<label>Award number</label>
|
||||||
<input-type>onebox</input-type>
|
<input-type>onebox</input-type>
|
||||||
<hint>Enter the identifier of the project</hint>
|
<hint>Enter the identifier of the project</hint>
|
||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@@ -1341,9 +1357,9 @@
|
|||||||
|
|
||||||
</value-pairs>
|
</value-pairs>
|
||||||
|
|
||||||
<!-- OpenAIRE document types
|
<!-- OpenAIRE document types
|
||||||
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationtype.html
|
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationtype.html
|
||||||
Based on COAR Vocabularies -> Controlled Vocabulary for Resource Type Genres (Version 2.0):
|
Based on COAR Vocabularies -> Controlled Vocabulary for Resource Type Genres (Version 2.0):
|
||||||
http://vocabularies.coar-repositories.org/documentation/resource_types/
|
http://vocabularies.coar-repositories.org/documentation/resource_types/
|
||||||
-->
|
-->
|
||||||
<value-pairs value-pairs-name="openaire_types" dc-term="type">
|
<value-pairs value-pairs-name="openaire_types" dc-term="type">
|
||||||
|
Reference in New Issue
Block a user