From 65654797a76a8b95930ec09af9b3b205ccba193b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:21:49 +0000 Subject: [PATCH 1/3] Initial plan From e385bb213dc997d153651eabf27128f7f1103ac5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:01:08 +0000 Subject: [PATCH 2/3] Fix refbox format interpolation to include dc.publisher field Co-authored-by: kosarko <1842385+kosarko@users.noreply.github.com> --- .../app/rest/ClarinRefBoxController.java | 25 +++++++++++++------ .../app/rest/ClarinRefBoxControllerIT.java | 18 +++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java index 3e889361609a..37b0e60ae2e0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java @@ -352,11 +352,6 @@ private String buildDisplayText(Context context, Item item) { // 1. Authors List authors = itemService.getMetadata(item, "dc", "contributor", "author", DEFAULT_LANGUAGE) .stream().map(MetadataValue::getValue).collect(Collectors.toList()); - // If there are no authors, try to get the publisher metadata - if (authors.isEmpty()) { - authors = itemService.getMetadata(item, "dc", "publisher", null, DEFAULT_LANGUAGE) - .stream().map(MetadataValue::getValue).collect(Collectors.toList()); - } String authorText = formatAuthors(authors); // 2. Year @@ -370,16 +365,26 @@ private String buildDisplayText(Context context, Item item) { // 3. Title String title = itemService.getMetadataFirstValue(item, "dc", "title", null, DEFAULT_LANGUAGE); - // 4. Repository name + // 4. Publisher + String publisher = itemService.getMetadataFirstValue(item, "dc", "publisher", null, DEFAULT_LANGUAGE); + + // 5. Repository name String repository = configurationService.getProperty("dspace.name"); - // 5. Identifier URI (prefer DOI) + // 6. Identifier URI (prefer DOI) String identifier = itemService.getMetadataFirstValue(item, "dc", "identifier", "doi", DEFAULT_LANGUAGE); if (identifier == null) { identifier = itemService.getMetadataFirstValue(item, "dc", "identifier", "uri", DEFAULT_LANGUAGE); } - // 6. Format + // 7. Format as: {authors}, {year}, {title}, {publisher}, {repository}, {identifier} + // If no authors, use publisher as author fallback for backwards compatibility + if (authorText == null || authorText.isEmpty()) { + if (publisher != null && !publisher.isEmpty()) { + authorText = publisher; + } + } + // Using html tags to format the output because this display text will be rendered in the UI StringBuilder sb = new StringBuilder(); if (authorText != null && !authorText.isEmpty()) { @@ -392,6 +397,10 @@ private String buildDisplayText(Context context, Item item) { sb.append(year); } sb.append(", \n ").append(title != null ? title : "").append(""); + if (publisher != null && !publisher.isEmpty() && + (authorText == null || authorText.isEmpty() || !authorText.equals(publisher))) { + sb.append(", ").append(publisher); + } if (repository != null && !repository.isEmpty()) { sb.append(", ").append(repository); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java index ce87f39bff7d..0bddeb17acca 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java @@ -257,6 +257,24 @@ public void testFeaturedServiceWithMalformedLink() throws Exception { .andExpect(jsonPath("$.featuredServices.featuredService").isEmpty()); } + @Test + public void testRefboxInfoWithBothAuthorAndPublisher() throws Exception { + context.turnOffAuthorisationSystem(); + Item itemWithBoth = ItemBuilder.createItem(context, collection) + .withTitle("Test Item") + .withAuthor("Test Author") + .withMetadata("dc", "publisher", null, "Test Publisher") + .withIssueDate("2023-01-01") + .build(); + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/refbox?handle=" + itemWithBoth.getHandle())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers.containsString("Test Author"))) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers.containsString("Test Publisher"))); + } + @Test public void testDisplayTextWithOneAuthor() throws Exception { context.turnOffAuthorisationSystem(); From 8a7b732196ef07072a040035fe6b0bd9e4f087d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:55:17 +0000 Subject: [PATCH 3/3] Implement custom format template interpolation for refbox display text - Add support for local.refbox.format metadata field with variable interpolation - Support variables: {title}, {authors}, {pid}, {repository}, {year}, {publisher} - Maintain backward compatibility when no custom format is present - Add comprehensive test cases for template and default behavior Co-authored-by: kosarko <1842385+kosarko@users.noreply.github.com> --- .../app/rest/ClarinRefBoxController.java | 52 ++++++++++++++++ .../app/rest/ClarinRefBoxControllerIT.java | 60 +++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java index 37b0e60ae2e0..4e850896b127 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ClarinRefBoxController.java @@ -349,6 +349,58 @@ public ResponseEntity getRefboxInfo( * Build the display text for the RefBox based on the Item Metadata. */ private String buildDisplayText(Context context, Item item) { + // Check for custom format template + String formatTemplate = itemService.getMetadataFirstValue(item, "local", "refbox", "format", DEFAULT_LANGUAGE); + + if (formatTemplate != null && !formatTemplate.isEmpty()) { + return buildDisplayTextFromTemplate(context, item, formatTemplate); + } else { + return buildDisplayTextDefault(context, item); + } + } + + /** + * Build display text using a custom format template with variable interpolation. + * Supported variables: {title}, {authors}, {pid}, {repository}, {year}, {publisher} + */ + private String buildDisplayTextFromTemplate(Context context, Item item, String template) { + // Extract all metadata values + List authors = itemService.getMetadata(item, "dc", "contributor", "author", DEFAULT_LANGUAGE) + .stream().map(MetadataValue::getValue).collect(Collectors.toList()); + String authorText = formatAuthors(authors); + + String year = ""; + String issued = itemService.getMetadataFirstValue(item, "dc", "date", "issued", DEFAULT_LANGUAGE); + if (issued != null && !issued.isEmpty()) { + year = issued.split("-")[0]; + } + + String title = itemService.getMetadataFirstValue(item, "dc", "title", null, DEFAULT_LANGUAGE); + String publisher = itemService.getMetadataFirstValue(item, "dc", "publisher", null, DEFAULT_LANGUAGE); + String repository = configurationService.getProperty("dspace.name"); + + // Get identifier (prefer DOI, fallback to URI) + String pid = itemService.getMetadataFirstValue(item, "dc", "identifier", "doi", DEFAULT_LANGUAGE); + if (pid == null) { + pid = itemService.getMetadataFirstValue(item, "dc", "identifier", "uri", DEFAULT_LANGUAGE); + } + + // Perform template interpolation + String result = template; + result = result.replace("{title}", title != null ? title : ""); + result = result.replace("{authors}", authorText != null ? authorText : ""); + result = result.replace("{pid}", pid != null ? pid : ""); + result = result.replace("{repository}", repository != null ? repository : ""); + result = result.replace("{year}", year != null ? year : ""); + result = result.replace("{publisher}", publisher != null ? publisher : ""); + + return result; + } + + /** + * Build display text using the default hardcoded format. + */ + private String buildDisplayTextDefault(Context context, Item item) { // 1. Authors List authors = itemService.getMetadata(item, "dc", "contributor", "author", DEFAULT_LANGUAGE) .stream().map(MetadataValue::getValue).collect(Collectors.toList()); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java index 0bddeb17acca..1a196a6e26c3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinRefBoxControllerIT.java @@ -275,6 +275,66 @@ public void testRefboxInfoWithBothAuthorAndPublisher() throws Exception { .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers.containsString("Test Publisher"))); } + @Test + public void testRefboxInfoWithCustomFormatTemplate() throws Exception { + context.turnOffAuthorisationSystem(); + Item itemWithTemplate = ItemBuilder.createItem(context, collection) + .withTitle("Custom Title") + .withAuthor("Custom Author") + .withMetadata("dc", "publisher", null, "Custom Publisher") + .withMetadata("local", "refbox", "format", + "{authors} ({year}). {title}. {publisher}. {repository}. {pid}") + .withIssueDate("2024-01-01") + .build(); + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/refbox?handle=" + itemWithTemplate.getHandle())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers + .containsString("Custom Author (2024). Custom Title. Custom Publisher."))); + } + + @Test + public void testRefboxInfoWithCustomFormatTemplatePartialVariables() throws Exception { + context.turnOffAuthorisationSystem(); + Item itemWithPartialTemplate = ItemBuilder.createItem(context, collection) + .withTitle("Partial Title") + .withAuthor("Partial Author") + .withMetadata("local", "refbox", "format", "{authors}: {title} [{year}]") + .withIssueDate("2023-12-31") + .build(); + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/refbox?handle=" + itemWithPartialTemplate.getHandle())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers + .containsString("Partial Author: Partial Title [2023]"))); + } + + @Test + public void testRefboxInfoWithoutCustomFormatUsesDefault() throws Exception { + context.turnOffAuthorisationSystem(); + Item itemWithoutTemplate = ItemBuilder.createItem(context, collection) + .withTitle("Default Title") + .withAuthor("Default Author") + .withMetadata("dc", "publisher", null, "Default Publisher") + .withIssueDate("2024-02-01") + .build(); + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/refbox?handle=" + itemWithoutTemplate.getHandle())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers + .containsString("Default Author"))) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers + .containsString("Default Publisher"))) + .andExpect(jsonPath("$.displayText").value(org.hamcrest.Matchers + .containsString("Default Title"))); + } + @Test public void testDisplayTextWithOneAuthor() throws Exception { context.turnOffAuthorisationSystem();