diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java index b3211799cc..2edd72b0f7 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -695,6 +695,11 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It log.info(LogManager.getHeader(context, "delete_item", "item_id=" + item.getID())); + // Remove relationships + for (Relationship relationship : relationshipService.findByItem(context, item)) { + relationshipService.delete(context, relationship, false, false); + } + // Remove bundles removeAllBundles(context, item); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java index a0b67fd5ba..e8d5f956d8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java @@ -10,9 +10,11 @@ package org.dspace.app.rest.repository; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; @@ -32,10 +34,14 @@ import org.dspace.app.rest.repository.patch.ItemPatch; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.Relationship; +import org.dspace.content.RelationshipType; import org.dspace.content.WorkspaceItem; import org.dspace.content.service.CollectionService; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; +import org.dspace.content.service.RelationshipService; +import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.WorkspaceItemService; import org.dspace.core.Context; import org.dspace.util.UUIDUtils; @@ -78,6 +84,12 @@ public class ItemRestRepository extends DSpaceObjectRestRepository 0) { + List relationshipIds = + Arrays.stream(copyVirtual).filter(StringUtils::isNumeric).collect(Collectors.toList()) + .stream().map(Integer::parseInt).collect(Collectors.toList()); + for (Integer relationshipId : relationshipIds) { + RelationshipType relationshipType = relationshipTypeService.find(context, relationshipId); + for (Relationship relationship : relationshipService + .findByItemAndRelationshipType(context, item, relationshipType)) { + + deleteRelationshipCopyVirtualMetadata(item, relationship); + } + } + } + } + + private void deleteRelationshipCopyVirtualMetadata(Item itemToDelete, Relationship relationshipToDelete) + throws SQLException, AuthorizeException { + + boolean copyToLeft = relationshipToDelete.getRightItem().equals(itemToDelete); + boolean copyToRight = relationshipToDelete.getLeftItem().equals(itemToDelete); + + if (copyToLeft && copyToRight) { + copyToLeft = false; + copyToRight = false; + } + + relationshipService.delete(obtainContext(), relationshipToDelete, copyToLeft, copyToRight); + } + @Override @PreAuthorize("hasAuthority('ADMIN')") protected ItemRest createAndReturn(Context context) throws AuthorizeException, SQLException { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java index c77dff659b..a325557e3f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipDeleteRestRepositoryIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -17,6 +18,7 @@ import java.util.List; import org.dspace.app.rest.builder.CollectionBuilder; import org.dspace.app.rest.builder.CommunityBuilder; +import org.dspace.app.rest.builder.GroupBuilder; import org.dspace.app.rest.builder.ItemBuilder; import org.dspace.app.rest.builder.RelationshipBuilder; import org.dspace.app.rest.test.AbstractEntityIntegrationTest; @@ -29,6 +31,10 @@ import org.dspace.content.RelationshipType; import org.dspace.content.service.EntityTypeService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipTypeService; +import org.dspace.core.I18nUtil; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.service.EPersonService; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -44,12 +50,21 @@ public class RelationshipDeleteRestRepositoryIT extends AbstractEntityIntegratio @Autowired private RelationshipTypeService relationshipTypeService; + @Autowired + private EPersonService ePersonService; + private Item leftItem; private Item rightItem; private Collection collection; private RelationshipType relationshipType; private Relationship relationship; private String adminAuthToken; + private EPerson collectionAdmin; + private Item personItem; + private Item projectItem; + private Item publicationItem; + private RelationshipType personProjectRelationshipType; + private RelationshipType publicationPersonRelationshipType; @Before @Override @@ -58,15 +73,47 @@ public class RelationshipDeleteRestRepositoryIT extends AbstractEntityIntegratio adminAuthToken = getAuthToken(admin.getEmail(), password); context.turnOffAuthorisationSystem(); + + collectionAdmin = ePersonService.findByEmail(context, "collectionAdminTest@email.com"); + if (collectionAdmin == null) { + // This EPerson creation should only happen once (i.e. for first test run) + collectionAdmin = ePersonService.create(context); + collectionAdmin.setFirstName(context, "first"); + collectionAdmin.setLastName(context, "last"); + collectionAdmin.setEmail("collectionAdminTest@email.com"); + collectionAdmin.setCanLogIn(true); + collectionAdmin.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); + ePersonService.setPassword(collectionAdmin, password); + // actually save the eperson to unit testing DB + ePersonService.update(context, collectionAdmin); + } + Community community = CommunityBuilder.createCommunity(context) .withName("Parent community") .build(); collection = CollectionBuilder.createCollection(context, community) .withName("Collection") + .withAdminGroup(collectionAdmin) .build(); context.restoreAuthSystemState(); } + @After + @Override + public void destroy() throws Exception { + try { + context.turnOffAuthorisationSystem(); + collectionAdmin = ePersonService.findByEmail(context, "collectionAdminTest@email.com"); + if (collectionAdmin != null) { + ePersonService.delete(context, collectionAdmin); + context.commit(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + super.destroy(); + } + private void initPublicationAuthor() throws Exception { context.turnOffAuthorisationSystem(); @@ -115,6 +162,63 @@ public class RelationshipDeleteRestRepositoryIT extends AbstractEntityIntegratio context.restoreAuthSystemState(); } + private void initPersonProjectPublication() throws Exception { + context.turnOffAuthorisationSystem(); + personItem = ItemBuilder.createItem(context, collection) + .withTitle("Person 1") + .withPersonIdentifierFirstName("Donald") + .withPersonIdentifierLastName("Smith") + .withRelationshipType("Person") + .build(); + projectItem = ItemBuilder.createItem(context, collection) + .withTitle("Project 1") + .withRelationshipType("Project") + .build(); + publicationItem = ItemBuilder.createItem(context, collection) + .withTitle("Publication 1") + .withRelationshipType("Publication") + .build(); + personProjectRelationshipType = relationshipTypeService.findbyTypesAndLabels(context, + entityTypeService.findByEntityType(context, "Person"), + entityTypeService.findByEntityType(context, "Project"), + "isProjectOfPerson", + "isPersonOfProject"); + publicationPersonRelationshipType = relationshipTypeService.findbyTypesAndLabels(context, + entityTypeService.findByEntityType(context, "Publication"), + entityTypeService.findByEntityType(context, "Person"), + "isAuthorOfPublication", + "isPublicationOfAuthor"); + RelationshipBuilder + .createRelationshipBuilder(context, personItem, projectItem, personProjectRelationshipType) + .withLeftPlace(0) + .build(); + RelationshipBuilder + .createRelationshipBuilder(context, publicationItem, personItem, publicationPersonRelationshipType) + .withLeftPlace(0) + .build(); + context.restoreAuthSystemState(); + + publicationItem = itemService.find(context, publicationItem.getID()); + List publicationAuthorList = + itemService.getMetadata(publicationItem, "dc", "contributor", "author", Item.ANY); + assertThat(publicationAuthorList.size(), equalTo(1)); + assertThat(publicationAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertThat(publicationAuthorList.get(0).getAuthority(), startsWith("virtual::")); + List publicationRelationships = itemService.getMetadata(publicationItem, + "relation", "isAuthorOfPublication", Item.ANY, Item.ANY); + assertThat(publicationRelationships.size(), equalTo(1)); + + projectItem = itemService.find(context, projectItem.getID()); + List projectAuthorList = + itemService.getMetadata(projectItem, "project", "contributor", "author", Item.ANY); + assertThat(projectAuthorList.size(), equalTo(1)); + assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertThat(projectAuthorList.get(0).getAuthority(), startsWith("virtual::")); + List projectRelationships = itemService.getMetadata(projectItem, + "relation", "isPersonOfProject", Item.ANY, Item.ANY); + assertThat(projectRelationships.size(), equalTo(1)); + } + @Test public void testDeleteAuthorRelationshipCopyToLeftItem() throws Exception { initPublicationAuthor(); @@ -236,4 +340,145 @@ public class RelationshipDeleteRestRepositoryIT extends AbstractEntityIntegratio assertThat(issueList.size(), equalTo(1)); assertThat(issueList.get(0).getValue(), equalTo("2")); } + + @Test + public void deleteItemCopyVirtualMetadataAll() throws Exception { + initPersonProjectPublication(); + + getClient(adminAuthToken).perform( + delete("/api/core/items/" + personItem.getID() + "?copyVirtualMetadata=all")) + .andExpect(status().isNoContent()); + + publicationItem = itemService.find(context, publicationItem.getID()); + List publicationAuthorList = itemService.getMetadata(publicationItem, + "dc", "contributor", "author", Item.ANY); + assertThat(publicationAuthorList.size(), equalTo(1)); + assertThat(publicationAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertNull(publicationAuthorList.get(0).getAuthority()); + List publicationRelationships = itemService.getMetadata(publicationItem, + "relation", "isAuthorOfPublication", Item.ANY, Item.ANY); + assertThat(publicationRelationships.size(), equalTo(0)); + + projectItem = itemService.find(context, projectItem.getID()); + List projectAuthorList = itemService.getMetadata(projectItem, + "project", "contributor", "author", Item.ANY); + assertThat(projectAuthorList.size(), equalTo(1)); + assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertNull(projectAuthorList.get(0).getAuthority()); + List projectRelationships = itemService.getMetadata(projectItem, + "relation", "isPersonOfProject", Item.ANY, Item.ANY); + assertThat(projectRelationships.size(), equalTo(0)); + } + + @Test + public void deleteItemCopyVirtualMetadataOneType() throws Exception { + initPersonProjectPublication(); + + getClient(adminAuthToken).perform( + delete("/api/core/items/" + personItem.getID() + "?copyVirtualMetadata=" + + publicationPersonRelationshipType.getID())) + .andExpect(status().isNoContent()); + + publicationItem = itemService.find(context, publicationItem.getID()); + List publicationAuthorList = itemService.getMetadata(publicationItem, + "dc", "contributor", "author", Item.ANY); + assertThat(publicationAuthorList.size(), equalTo(1)); + assertThat(publicationAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertNull(publicationAuthorList.get(0).getAuthority()); + List publicationRelationships = itemService.getMetadata(publicationItem, + "relation", "isAuthorOfPublication", Item.ANY, Item.ANY); + assertThat(publicationRelationships.size(), equalTo(0)); + + projectItem = itemService.find(context, projectItem.getID()); + List projectAuthorList = itemService.getMetadata(projectItem, + "project", "contributor", "author", Item.ANY); + assertThat(projectAuthorList.size(), equalTo(0)); + List projectRelationships = itemService.getMetadata(projectItem, + "relation", "isPersonOfProject", Item.ANY, Item.ANY); + assertThat(projectRelationships.size(), equalTo(0)); + } + + @Test + public void deleteItemCopyVirtualMetadataTwoTypes() throws Exception { + initPersonProjectPublication(); + + getClient(adminAuthToken).perform( + delete("/api/core/items/" + personItem.getID() + + "?copyVirtualMetadata=" + publicationPersonRelationshipType.getID() + + "©VirtualMetadata=" + personProjectRelationshipType.getID())) + .andExpect(status().isNoContent()); + + publicationItem = itemService.find(context, publicationItem.getID()); + List publicationAuthorList = itemService.getMetadata(publicationItem, + "dc", "contributor", "author", Item.ANY); + assertThat(publicationAuthorList.size(), equalTo(1)); + assertThat(publicationAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertNull(publicationAuthorList.get(0).getAuthority()); + List publicationRelationships = itemService.getMetadata(publicationItem, + "relation", "isAuthorOfPublication", Item.ANY, Item.ANY); + assertThat(publicationRelationships.size(), equalTo(0)); + + projectItem = itemService.find(context, projectItem.getID()); + List projectAuthorList = itemService.getMetadata(projectItem, + "project", "contributor", "author", Item.ANY); + assertThat(projectAuthorList.size(), equalTo(1)); + assertThat(projectAuthorList.get(0).getValue(), equalTo("Smith, Donald")); + assertNull(projectAuthorList.get(0).getAuthority()); + List projectRelationships = itemService.getMetadata(projectItem, + "relation", "isPersonOfProject", Item.ANY, Item.ANY); + assertThat(projectRelationships.size(), equalTo(0)); + } + + @Test + public void deleteItemCopyVirtualMetadataNone() throws Exception { + initPersonProjectPublication(); + + getClient(adminAuthToken).perform( + delete("/api/core/items/" + personItem.getID())) + .andExpect(status().isNoContent()); + + publicationItem = itemService.find(context, publicationItem.getID()); + List publicationAuthorList = itemService.getMetadata(publicationItem, + "dc", "contributor", "author", Item.ANY); + assertThat(publicationAuthorList.size(), equalTo(0)); + List publicationRelationships = itemService.getMetadata(publicationItem, + "relation", "isAuthorOfPublication", Item.ANY, Item.ANY); + assertThat(publicationRelationships.size(), equalTo(0)); + + projectItem = itemService.find(context, projectItem.getID()); + List projectAuthorList = itemService.getMetadata(projectItem, + "project", "contributor", "author", Item.ANY); + assertThat(projectAuthorList.size(), equalTo(0)); + List projectRelationships = itemService.getMetadata(projectItem, + "relation", "isPersonOfProject", Item.ANY, Item.ANY); + assertThat(projectRelationships.size(), equalTo(0)); + } + + @Test + public void deleteItemCopyVirtualMetadataAllNoPermissions() throws Exception { + initPersonProjectPublication(); + + getClient(getAuthToken(eperson.getEmail(), password)).perform( + delete("/api/core/items/" + personItem.getID())) + .andExpect(status().isForbidden()); + } + + @Test + public void deleteItemCopyVirtualMetadataAllInsufficientPermissions() throws Exception { + initPersonProjectPublication(); + + getClient(getAuthToken(collectionAdmin.getEmail(), password)).perform( + delete("/api/core/items/" + personItem.getID())) + .andExpect(status().isForbidden()); + } + + @Test + public void deleteItemCopyVirtualMetadataTypeInsufficientPermissions() throws Exception { + initPersonProjectPublication(); + + getClient(getAuthToken(collectionAdmin.getEmail(), password)).perform( + delete("/api/core/items/" + personItem.getID() + + "?copyVirtualMetadata=" + publicationPersonRelationshipType.getID())) + .andExpect(status().isForbidden()); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/AbstractDSpaceObjectBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/AbstractDSpaceObjectBuilder.java index b61cb041f2..e1f983f388 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/AbstractDSpaceObjectBuilder.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/AbstractDSpaceObjectBuilder.java @@ -226,7 +226,12 @@ public abstract class AbstractDSpaceObjectBuilder if (attachedDso != null) { getService().delete(c, attachedDso); } - c.complete(); + try { + c.complete(); + } + catch (Exception e) { + throw new RuntimeException(e); + } } indexingService.commit(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java index 8cbc087d41..4d806a01f7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java @@ -98,6 +98,23 @@ public class CollectionBuilder extends AbstractDSpaceObjectBuilder { return this; } + /** + * Create an admin group for the collection with the specified members + * + * @param members epersons to add to the admin group + * @return this builder + * @throws SQLException + * @throws AuthorizeException + */ + public CollectionBuilder withAdminGroup(EPerson... members) throws SQLException, AuthorizeException { + Group g = collectionService.createAdministrators(context, collection); + for (EPerson e : members) { + groupService.addMember(context, g, e); + } + groupService.update(context, g); + return this; + } + @Override public Collection build() { try { @@ -114,6 +131,7 @@ public class CollectionBuilder extends AbstractDSpaceObjectBuilder { @Override public void cleanup() throws Exception { deleteWorkflowGroups(collection); + //collectionService.removeAdministrators(context, collection); delete(collection); } diff --git a/dspace/config/registries/schema-project-types.xml b/dspace/config/registries/schema-project-types.xml index 7c78f74c19..61833cf519 100644 --- a/dspace/config/registries/schema-project-types.xml +++ b/dspace/config/registries/schema-project-types.xml @@ -17,4 +17,18 @@ - project.identifier.expectedcompletion > NOT FOUND --> + + project + contributor + author + Virtual metadata field that holds the full name of the author + + + + project + contributor + other + Virtual metadata field that holds the value of organization.legalName + +