diff --git a/dspace-api/src/main/java/org/dspace/content/RelationshipServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/RelationshipServiceImpl.java index 1fdfde6c7462..beb9e02ce5cb 100644 --- a/dspace-api/src/main/java/org/dspace/content/RelationshipServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/RelationshipServiceImpl.java @@ -864,6 +864,11 @@ private boolean containsVirtualMetadata(String typeToSearchInVirtualMetadata) { private void copyMetadataValues(Context context, Relationship relationship, boolean copyToLeftItem, boolean copyToRightItem) throws SQLException, AuthorizeException { + + // Fetch configuration: should we copy relation.* metadata or not? + boolean copyRelationMetadata = + configurationService.getBooleanProperty("relation.metadata.copy.on.delete", true); + if (copyToLeftItem) { String entityTypeString = itemService.getEntityTypeLabel(relationship.getLeftItem()); List relationshipMetadataValues = @@ -878,14 +883,19 @@ private void copyMetadataValues(Context context, Relationship relationship, bool // relationship values which are not useForPlace, while the relationhip type has useForPlace // E.g. when using addAndShiftRightMetadata on relation.isAuthorOfPublication, it will break the order // from dc.contributor.author - itemService.addMetadata(context, relationship.getLeftItem(), - relationshipMetadataValue.getMetadataField(). - getMetadataSchema().getName(), - relationshipMetadataValue.getMetadataField().getElement(), - relationshipMetadataValue.getMetadataField().getQualifier(), - relationshipMetadataValue.getLanguage(), - relationshipMetadataValue.getValue(), null, -1, - relationshipMetadataValue.getPlace()); + + MetadataField metadataField = relationshipMetadataValue.getMetadataField(); + // Only add metadata if it's not relation.* or if copying relation.* metadata is allowed + if (copyRelationMetadata || !"relation".equals(metadataField.getMetadataSchema().getName())) { + itemService.addMetadata(context, relationship.getLeftItem(), + relationshipMetadataValue.getMetadataField(). + getMetadataSchema().getName(), + relationshipMetadataValue.getMetadataField().getElement(), + relationshipMetadataValue.getMetadataField().getQualifier(), + relationshipMetadataValue.getLanguage(), + relationshipMetadataValue.getValue(), null, -1, + relationshipMetadataValue.getPlace()); + } } //This will ensure the new values no longer overlap, but won't break the order itemService.update(context, relationship.getLeftItem()); @@ -896,14 +906,19 @@ private void copyMetadataValues(Context context, Relationship relationship, bool relationshipMetadataService.findRelationshipMetadataValueForItemRelationship(context, relationship.getRightItem(), entityTypeString, relationship, true); for (RelationshipMetadataValue relationshipMetadataValue : relationshipMetadataValues) { - itemService.addMetadata(context, relationship.getRightItem(), - relationshipMetadataValue.getMetadataField(). - getMetadataSchema().getName(), - relationshipMetadataValue.getMetadataField().getElement(), - relationshipMetadataValue.getMetadataField().getQualifier(), - relationshipMetadataValue.getLanguage(), - relationshipMetadataValue.getValue(), null, -1, - relationshipMetadataValue.getPlace()); + + MetadataField metadataField = relationshipMetadataValue.getMetadataField(); + // Only add metadata if it's not relation.* or if copying relation.* metadata is allowed + if (copyRelationMetadata || !"relation".equals(metadataField.getMetadataSchema().getName())) { + itemService.addMetadata(context, relationship.getRightItem(), + relationshipMetadataValue.getMetadataField(). + getMetadataSchema().getName(), + relationshipMetadataValue.getMetadataField().getElement(), + relationshipMetadataValue.getMetadataField().getQualifier(), + relationshipMetadataValue.getLanguage(), + relationshipMetadataValue.getValue(), null, -1, + relationshipMetadataValue.getPlace()); + } } itemService.update(context, relationship.getRightItem()); } diff --git a/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplMetadataCopyTest.java b/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplMetadataCopyTest.java new file mode 100644 index 000000000000..18defaa457fc --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplMetadataCopyTest.java @@ -0,0 +1,154 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EntityTypeBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.RelationshipBuilder; +import org.dspace.builder.RelationshipTypeBuilder; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.RelationshipService; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.Before; +import org.junit.Test; + +public class RelationshipServiceImplMetadataCopyTest extends AbstractIntegrationTestWithDatabase { + + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); + private ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + + private Item leftItem; + private Item rightItem; + + private Relationship leftRightRelationship; + + @Before + public void setUp() throws Exception { + super.setUp(); + + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context).build(); + + Collection journalVolumeCollection = CollectionBuilder.createCollection(context, community) + .withEntityType("JournalVolume") + .build(); + Collection journalIssueCollection = CollectionBuilder.createCollection(context, community) + .withEntityType("JournalIssue") + .build(); + + EntityType journalVolumeType = EntityTypeBuilder.createEntityTypeBuilder(context, "JournalVolume").build(); + EntityType journalIssueType = EntityTypeBuilder.createEntityTypeBuilder(context, "JournalIssue").build(); + + RelationshipType issueJournalVolumeType = + RelationshipTypeBuilder + .createRelationshipTypeBuilder(context, journalIssueType, journalVolumeType, + "isJournalVolumeOfIssue", "isIssueOfJournalVolume", + null, null, null, null).build(); + + leftItem = ItemBuilder.createItem(context, journalIssueCollection) + .withPublicationIssueNumber("2").build(); + rightItem = ItemBuilder.createItem(context, journalVolumeCollection) + .withPublicationVolumeNumber("30").build(); + + leftRightRelationship = + RelationshipBuilder.createRelationshipBuilder(context, leftItem, rightItem, issueJournalVolumeType).build(); + + context.restoreAuthSystemState(); + } + + @Test + public void testRelationMetadataNotCopiedWhenConfigDisabled() throws Exception { + context.turnOffAuthorisationSystem(); + + // Set the configuration to disable relation metadata copying + configurationService.setProperty("relation.metadata.copy.on.delete", "false"); + + // Assert the relationship exists before removal + List relationships = relationshipService.findByItem(context, leftItem); + assertEquals(1, relationships.size()); + + // Verify the left item's relation.isJournalVolumeOfIssue metadata before removal + List volumeList = + itemService.getMetadata(leftItem, "relation", "isJournalVolumeOfIssue", null, Item.ANY); + assertThat(volumeList.get(0).getValue(), equalTo(rightItem.getID().toString())); + + // Verify the right item's relation.isIssueOfJournalVolume metadata before removal + List issueList = + itemService.getMetadata(rightItem, "relation", "isIssueOfJournalVolume", null, Item.ANY); + assertThat(issueList.get(0).getValue(), equalTo(leftItem.getID().toString())); + + // Remove the relationship + relationshipService.delete(context, leftRightRelationship); + + // Assert the relationship is removed + relationships = relationshipService.findByItem(context, leftItem); + assertEquals(0, relationships.size()); + + // Verify the left item's relation.isJournalVolumeOfIssue metadata should NOT be copied + volumeList = itemService.getMetadata(leftItem, "relation", "isJournalVolumeOfIssue", null, Item.ANY); + assertThat(volumeList.size(), equalTo(0)); // Expect no metadata from rightItem + + // Verify the right item's relation.isIssueOfJournalVolume metadata should NOT be copied + issueList = itemService.getMetadata(rightItem, "relation", "isIssueOfJournalVolume", null, Item.ANY); + assertThat(issueList.size(), equalTo(0)); // Expect no metadata from leftItem + + context.restoreAuthSystemState(); + } + + @Test + public void testRelationMetadataCopiedWhenConfigEnabled() throws Exception { + context.turnOffAuthorisationSystem(); + + // Set the configuration to disable relation metadata copying + configurationService.setProperty("relation.metadata.copy.on.delete", "true"); + + // Assert the relationship exists before removal + List relationships = relationshipService.findByItem(context, leftItem); + assertEquals(1, relationships.size()); + + // Verify the left item's relation.isJournalVolumeOfIssue metadata before removal + List volumeList = + itemService.getMetadata(leftItem, "relation", "isJournalVolumeOfIssue", null, Item.ANY); + assertThat(volumeList.get(0).getValue(), equalTo(rightItem.getID().toString())); + + // Verify the right item's relation.isIssueOfJournalVolume metadata before removal + List issueList = + itemService.getMetadata(rightItem, "relation", "isIssueOfJournalVolume", null, Item.ANY); + assertThat(issueList.get(0).getValue(), equalTo(leftItem.getID().toString())); + + // Remove the relationship + relationshipService.delete(context, leftRightRelationship); + + // Assert the relationship is removed + relationships = relationshipService.findByItem(context, leftItem); + assertEquals(0, relationships.size()); + + // Verify the left item's relation.isJournalVolumeOfIssue metadata copied after removal + volumeList = itemService.getMetadata(leftItem, "relation", "isJournalVolumeOfIssue", null, Item.ANY); + assertThat(volumeList.size(), equalTo(0)); + + // Verify the right item's relation.isIssueOfJournalVolume metadata copied after removal + issueList = itemService.getMetadata(rightItem, "relation", "isIssueOfJournalVolume", null, Item.ANY); + assertThat(issueList.size(), equalTo(0)); + + context.restoreAuthSystemState(); + } +} diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 8e532310c11b..1405a4fe9246 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1671,3 +1671,7 @@ include = ${module_dir}/usage-statistics.cfg include = ${module_dir}/versioning.cfg include = ${module_dir}/workflow.cfg include = ${module_dir}/external-providers.cfg + +# Controls whether relation.* virtual metadata should be copied when deleting a relationship +# Default is true, meaning relation.* metadata will be copied +relation.metadata.copy.on.delete = true