From e4a9714f464219a947bddf1fa940f9e60e9b04e7 Mon Sep 17 00:00:00 2001 From: pkiraly Date: Tue, 29 Jun 2021 18:50:29 +0200 Subject: [PATCH 01/10] #7978: linking ORCID from the metadata tab. --- src/main/webapp/metadataFragment.xhtml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/metadataFragment.xhtml b/src/main/webapp/metadataFragment.xhtml index 74e1f531234..94760b30abe 100755 --- a/src/main/webapp/metadataFragment.xhtml +++ b/src/main/webapp/metadataFragment.xhtml @@ -70,10 +70,25 @@

- + + + + + + - + + + + + + + +
From ce8882c07bc89274fae337aa9dc10fcf8f469dab Mon Sep 17 00:00:00 2001 From: pkiraly Date: Tue, 13 Jul 2021 22:01:50 +0200 Subject: [PATCH 02/10] #7978: linking ORCID, ISNI, VIAF, ResearcherId and ScopusID from the metadata tab. --- .../dataverse/DatasetFieldCompoundValue.java | 68 +++++++++++++++++-- src/main/webapp/metadataFragment.xhtml | 13 +--- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java index 407a1d57bd3..3875f16a0c2 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java @@ -14,7 +14,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.ResourceBundle; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -25,7 +24,11 @@ import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; +import javax.persistence.Transient; + import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; /** * @@ -68,6 +71,22 @@ public static DatasetFieldCompoundValue createNewEmptyDatasetFieldCompoundValue( @OrderBy("datasetFieldType ASC") private List childDatasetFields = new ArrayList<>(); + private static final Map> linkComponents = Map.of("author", new ImmutablePair<>("authorIdentifierScheme", "authorIdentifier")); + @Transient + private Map linkMap = new LinkedHashMap<>(); + @Transient + private String linkType = null; + @Transient + private String linkValue = null; + @Transient + private Map linkTemplates = Map.of( + "ORCID", "https://orcid.org/%s", + "ISNI", "https://isni.org/isni/%s", + "VIAF", "http://viaf.org/viaf/%s/", + "ResearcherID", "https://publons.com/researcher/%s/", + "ScopusID", "https://www.scopus.com/authid/detail.uri?authorId=%s" + ); + public Long getId() { return id; } @@ -133,15 +152,29 @@ public DatasetFieldCompoundValue copy(DatasetField parent) { return compoundValue; } - public Map getDisplayValueMap() { + public Map getDisplayValueMap() { // todo - this currently only supports child datasetfields with single values // need to determine how we would want to handle multiple Map fieldMap = new LinkedHashMap<>(); + linkMap = new LinkedHashMap<>(); boolean fixTrailingComma = false; + Pair linkComponents = getLinkComponents(); + linkType = null; + linkValue = null; for (DatasetField childDatasetField : childDatasetFields) { fixTrailingComma = false; // skip the value if it is empty or N/A if (!StringUtils.isBlank(childDatasetField.getValue()) && !DatasetField.NA_VALUE.equals(childDatasetField.getValue())) { + if (linkComponents != null) { + if (fieldNameEquals(childDatasetField, linkComponents.getKey())) { + linkType = childDatasetField.getValue(); + } else if (fieldNameEquals(childDatasetField, linkComponents.getValue())) { + linkValue = childDatasetField.getValue(); + if (StringUtils.isNotBlank(linkType) && StringUtils.isNotBlank(linkValue)) + linkMap.put(childDatasetField, true); + } + } + String format = childDatasetField.getDatasetFieldType().getDisplayFormat(); if (StringUtils.isBlank(format)) { format = "#VALUE"; @@ -161,8 +194,8 @@ public Map getDisplayValueMap() { //todo: this should be handled in more generic way for any other text that can then be internationalized // if we need to use replaceAll for regexp, then make sure to use: java.util.regex.Matcher.quoteReplacement() .replace("#EMAIL", BundleUtil.getStringFromBundle("dataset.email.hiddenMessage")) - .replace("#VALUE", sanitizedValue ); - fieldMap.put(childDatasetField,displayValue); + .replace("#VALUE", sanitizedValue); + fieldMap.put(childDatasetField, displayValue); } } @@ -172,7 +205,31 @@ public Map getDisplayValueMap() { return fieldMap; } - + + public String getLink() { + if (linkTemplates.containsKey(linkType)) + return String.format(linkTemplates.get(linkType), linkValue); + return null; + } + + public boolean isLink(DatasetField datasetField) { + return linkMap.containsKey(datasetField) && linkMap.get(datasetField) == true && getLink() != null; + } + + private boolean fieldNameEquals(DatasetField datasetField, String linkTypeComponent) { + return datasetField.getDatasetFieldType().getName().equals(linkTypeComponent); + } + + public boolean isLinkableField() { + return linkComponents.containsKey(parentDatasetField.getDatasetFieldType().getName()); + } + + public Pair getLinkComponents() { + if (!isLinkableField()) + return null; + return linkComponents.get(parentDatasetField.getDatasetFieldType().getName()); + } + private Map removeLastComma(Map mapIn) { Iterator> itr = mapIn.entrySet().iterator(); @@ -192,6 +249,5 @@ private Map removeLastComma(Map mapI } return mapIn; - } } diff --git a/src/main/webapp/metadataFragment.xhtml b/src/main/webapp/metadataFragment.xhtml index 94760b30abe..4941d334882 100755 --- a/src/main/webapp/metadataFragment.xhtml +++ b/src/main/webapp/metadataFragment.xhtml @@ -70,22 +70,15 @@

- - - - - - - + + - + From 8b9b5ea51cea6f9880b2009559076c883f24259e Mon Sep 17 00:00:00 2001 From: pkiraly Date: Wed, 14 Jul 2021 13:19:02 +0200 Subject: [PATCH 03/10] #7978: linking LCNA and GND identifiers from the metadata tab. --- .../dataverse/DatasetFieldCompoundValue.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java index 3875f16a0c2..4707eac25e5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java @@ -71,21 +71,26 @@ public static DatasetFieldCompoundValue createNewEmptyDatasetFieldCompoundValue( @OrderBy("datasetFieldType ASC") private List childDatasetFields = new ArrayList<>(); - private static final Map> linkComponents = Map.of("author", new ImmutablePair<>("authorIdentifierScheme", "authorIdentifier")); - @Transient - private Map linkMap = new LinkedHashMap<>(); - @Transient - private String linkType = null; - @Transient - private String linkValue = null; - @Transient - private Map linkTemplates = Map.of( + // configurations for link creation + private static final Map> linkComponents = Map.of( + "author", new ImmutablePair<>("authorIdentifierScheme", "authorIdentifier") + ); + private static final Map linkSchemeTemplates = Map.of( "ORCID", "https://orcid.org/%s", "ISNI", "https://isni.org/isni/%s", + "LCNA", "http://id.loc.gov/authorities/names/%s", "VIAF", "http://viaf.org/viaf/%s/", + "GND", "https://d-nb.info/gnd/%s", + // DAI "ResearcherID", "https://publons.com/researcher/%s/", "ScopusID", "https://www.scopus.com/authid/detail.uri?authorId=%s" ); + @Transient + private Map linkMap = new LinkedHashMap<>(); + @Transient + private String linkScheme = null; + @Transient + private String linkValue = null; public Long getId() { return id; @@ -159,7 +164,7 @@ public Map getDisplayValueMap() { linkMap = new LinkedHashMap<>(); boolean fixTrailingComma = false; Pair linkComponents = getLinkComponents(); - linkType = null; + linkScheme = null; linkValue = null; for (DatasetField childDatasetField : childDatasetFields) { fixTrailingComma = false; @@ -167,10 +172,10 @@ public Map getDisplayValueMap() { if (!StringUtils.isBlank(childDatasetField.getValue()) && !DatasetField.NA_VALUE.equals(childDatasetField.getValue())) { if (linkComponents != null) { if (fieldNameEquals(childDatasetField, linkComponents.getKey())) { - linkType = childDatasetField.getValue(); + linkScheme = childDatasetField.getValue(); } else if (fieldNameEquals(childDatasetField, linkComponents.getValue())) { linkValue = childDatasetField.getValue(); - if (StringUtils.isNotBlank(linkType) && StringUtils.isNotBlank(linkValue)) + if (StringUtils.isNotBlank(linkScheme) && StringUtils.isNotBlank(linkValue)) linkMap.put(childDatasetField, true); } } @@ -207,8 +212,8 @@ public Map getDisplayValueMap() { } public String getLink() { - if (linkTemplates.containsKey(linkType)) - return String.format(linkTemplates.get(linkType), linkValue); + if (linkSchemeTemplates.containsKey(linkScheme)) + return String.format(linkSchemeTemplates.get(linkScheme), linkValue); return null; } From 4d4167a121d4806f4fb2d23ff066a124cd9af8fb Mon Sep 17 00:00:00 2001 From: pkiraly Date: Wed, 14 Jul 2021 13:41:56 +0200 Subject: [PATCH 04/10] #7978: linking LCNA and GND identifiers from the metadata tab. --- .../edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java index 4707eac25e5..4505d99c0f4 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java @@ -85,6 +85,8 @@ public static DatasetFieldCompoundValue createNewEmptyDatasetFieldCompoundValue( "ResearcherID", "https://publons.com/researcher/%s/", "ScopusID", "https://www.scopus.com/authid/detail.uri?authorId=%s" ); + + // field for handling links. Annotation '@Transient' prevents these fields to be saved in DB @Transient private Map linkMap = new LinkedHashMap<>(); @Transient From 3dbd1d37f7c0122b4aacf3130a0fe294c9210367 Mon Sep 17 00:00:00 2001 From: pkiraly Date: Tue, 24 Aug 2021 16:40:33 +0200 Subject: [PATCH 05/10] #7978: Modifying DatasetAuthor - creating a map for the identifier regex pattern and the link pattern. --- .../harvard/iq/dataverse/DatasetAuthor.java | 74 ++++++++----------- .../dataverse/DatasetFieldCompoundValue.java | 14 +--- .../harvard/iq/dataverse/LinkTemplate.java | 21 ++++++ .../iq/dataverse/DatasetAuthorTest.java | 4 +- .../DatasetFieldValueValidatorTest.java | 10 +-- 5 files changed, 62 insertions(+), 61 deletions(-) create mode 100644 src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java b/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java index ce8405a0164..0cb41eb510b 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java @@ -7,14 +7,13 @@ package edu.harvard.iq.dataverse; import java.util.Comparator; +import java.util.Map; import java.util.regex.Pattern; - /** * * @author skraffmiller */ - public class DatasetAuthor { public static Comparator DisplayOrder = new Comparator(){ @@ -89,17 +88,25 @@ public boolean isEmpty() { ); } - /** - * https://support.orcid.org/hc/en-us/articles/360006897674-Structure-of-the-ORCID-Identifier - */ - final public static String REGEX_ORCID = "^\\d{4}-\\d{4}-\\d{4}-(\\d{4}|\\d{3}X)$"; - final public static String REGEX_ISNI = "^\\d*$"; - final public static String REGEX_LCNA = "^[a-z]+\\d+$"; - final public static String REGEX_VIAF = "^\\d*$"; - /** - * GND regex from https://www.wikidata.org/wiki/Property:P227 - */ - final public static String REGEX_GND = "^1[01]?\\d{7}[0-9X]|[47]\\d{6}-\\d|[1-9]\\d{0,7}-[0-9X]|3\\d{7}[0-9X]$"; + public static final Map linkSchemeTemplates = Map.of( + // https://support.orcid.org/hc/en-us/articles/360006897674-Structure-of-the-ORCID-Identifier + "ORCID", + new LinkTemplate("https://orcid.org/%s", "^\\d{4}-\\d{4}-\\d{4}-(\\d{4}|\\d{3}X)$"), + "ISNI", + new LinkTemplate("http://www.isni.org/isni/%s", "^\\d*$"), + "LCNA", + new LinkTemplate("http://id.loc.gov/authorities/names/%s", "^[a-z]+\\d+$"), + "VIAF", + new LinkTemplate("https://viaf.org/viaf/%s", "^\\d*$"), + // GND regex from https://www.wikidata.org/wiki/Property:P227 + "GND", + new LinkTemplate("https://d-nb.info/gnd/%s", "^1[01]?\\d{7}[0-9X]|[47]\\d{6}-\\d|[1-9]\\d{0,7}-[0-9X]|3\\d{7}[0-9X]$"), + // DAI is missing from this list + "ResearcherID", + new LinkTemplate("https://publons.com/researcher/%s/", "^[A-Z\\d][A-Z\\d-]+[A-Z\\d]$"), + "ScopusID", + new LinkTemplate("https://www.scopus.com/authid/detail.uri?authorId=%s", "^\\d*$") + ); /** * Each author identification type has its own valid pattern/syntax. @@ -109,39 +116,22 @@ public static Pattern getValidPattern(String regex) { } public String getIdentifierAsUrl() { + if (idType != null && !idType.isEmpty() && idValue != null && !idValue.isEmpty()) { + return getIdentifierAsUrl(idType, idValue); + } + return null; + } + + public static String getIdentifierAsUrl(String idType, String idValue) { if (idType != null && !idType.isEmpty() && idValue != null && !idValue.isEmpty()) { DatasetFieldValueValidator datasetFieldValueValidator = new DatasetFieldValueValidator(); - switch (idType) { - case "ORCID": - if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, getValidPattern(REGEX_ORCID))) { - return "https://orcid.org/" + idValue; - } - break; - case "ISNI": - if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, getValidPattern(REGEX_ISNI))) { - return "http://www.isni.org/isni/" + idValue; - } - break; - case "LCNA": - if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, getValidPattern(REGEX_LCNA))) { - return "http://id.loc.gov/authorities/names/" + idValue; - } - break; - case "VIAF": - if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, getValidPattern(REGEX_VIAF))) { - return "https://viaf.org/viaf/" + idValue; - } - break; - case "GND": - if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, getValidPattern(REGEX_GND))) { - return "https://d-nb.info/gnd/" + idValue; - } - break; - default: - break; + if (linkSchemeTemplates.containsKey(idType)) { + LinkTemplate template = linkSchemeTemplates.get(idType); + if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, template.getPattern())) { + return String.format(template.getTemplate(), idValue); + } } } return null; } - } diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java index 4505d99c0f4..72a5fe15b9a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java @@ -75,16 +75,6 @@ public static DatasetFieldCompoundValue createNewEmptyDatasetFieldCompoundValue( private static final Map> linkComponents = Map.of( "author", new ImmutablePair<>("authorIdentifierScheme", "authorIdentifier") ); - private static final Map linkSchemeTemplates = Map.of( - "ORCID", "https://orcid.org/%s", - "ISNI", "https://isni.org/isni/%s", - "LCNA", "http://id.loc.gov/authorities/names/%s", - "VIAF", "http://viaf.org/viaf/%s/", - "GND", "https://d-nb.info/gnd/%s", - // DAI - "ResearcherID", "https://publons.com/researcher/%s/", - "ScopusID", "https://www.scopus.com/authid/detail.uri?authorId=%s" - ); // field for handling links. Annotation '@Transient' prevents these fields to be saved in DB @Transient @@ -214,9 +204,7 @@ public Map getDisplayValueMap() { } public String getLink() { - if (linkSchemeTemplates.containsKey(linkScheme)) - return String.format(linkSchemeTemplates.get(linkScheme), linkValue); - return null; + return DatasetAuthor.getIdentifierAsUrl(linkScheme, linkValue); } public boolean isLink(DatasetField datasetField) { diff --git a/src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java b/src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java new file mode 100644 index 00000000000..6f9ff2a58fe --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java @@ -0,0 +1,21 @@ +package edu.harvard.iq.dataverse; + +import java.util.regex.Pattern; + +public class LinkTemplate { + private String template; + private Pattern pattern; + + public LinkTemplate(String template, String regex) { + this.template = template; + this.pattern = Pattern.compile(regex); + } + + public String getTemplate() { + return template; + } + + public Pattern getPattern() { + return pattern; + } +} diff --git a/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java b/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java index 5b943448fca..0d349ee6824 100644 --- a/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java @@ -31,6 +31,9 @@ public static Collection parameters() { { "LCNA", "n82058243", "http://id.loc.gov/authorities/names/n82058243" }, { "VIAF", "172389567", "https://viaf.org/viaf/172389567" }, { "GND", "4079154-3", "https://d-nb.info/gnd/4079154-3" }, + { "ResearcherID", "4079154", "https://publons.com/researcher/4079154/" }, + { "ResearcherID", "AAW-9289-2021", "https://publons.com/researcher/AAW-9289-2021/" }, + { "ScopusID", "4079154", "https://www.scopus.com/authid/detail.uri?authorId=4079154" }, { null, null, null, }, }); } @@ -44,5 +47,4 @@ public void getIdentifierAsUrl() { } assertEquals(expectedIdentifierAsUrl, datasetAuthor.getIdentifierAsUrl()); } - } diff --git a/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java b/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java index dedafe7722e..d11d5ca747b 100644 --- a/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java @@ -141,7 +141,7 @@ public void testIsValid() { @Test public void testIsValidAuthorIdentifierOrcid() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.getValidPattern(DatasetAuthor.REGEX_ORCID); + Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("ORCID").getPattern(); assertTrue(validator.isValidAuthorIdentifier("0000-0002-1825-0097", pattern)); // An "X" at the end of an ORCID is less common but still valid. assertTrue(validator.isValidAuthorIdentifier("0000-0002-1694-233X", pattern)); @@ -154,7 +154,7 @@ public void testIsValidAuthorIdentifierOrcid() { @Test public void testIsValidAuthorIdentifierIsni() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.getValidPattern(DatasetAuthor.REGEX_ISNI); + Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("ISNI").getPattern(); assertTrue(validator.isValidAuthorIdentifier("0000000121032683", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); } @@ -162,7 +162,7 @@ public void testIsValidAuthorIdentifierIsni() { @Test public void testIsValidAuthorIdentifierLcna() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.getValidPattern(DatasetAuthor.REGEX_LCNA); + Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("LCNA").getPattern(); assertTrue(validator.isValidAuthorIdentifier("n82058243", pattern)); assertTrue(validator.isValidAuthorIdentifier("foobar123", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); @@ -171,7 +171,7 @@ public void testIsValidAuthorIdentifierLcna() { @Test public void testIsValidAuthorIdentifierViaf() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.getValidPattern(DatasetAuthor.REGEX_VIAF); + Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("VIAF").getPattern(); assertTrue(validator.isValidAuthorIdentifier("172389567", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); } @@ -179,7 +179,7 @@ public void testIsValidAuthorIdentifierViaf() { @Test public void testIsValidAuthorIdentifierGnd() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.getValidPattern(DatasetAuthor.REGEX_GND); + Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("GND").getPattern(); assertTrue(validator.isValidAuthorIdentifier("4079154-3", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); } From ec466d890bd3a7b5d78209719cbec9077d1be656 Mon Sep 17 00:00:00 2001 From: pkiraly Date: Wed, 25 Aug 2021 10:34:40 +0200 Subject: [PATCH 06/10] #7978: adding a release note. --- .../7978-linking-ORCID-profile-from-metadata-tab.md | 3 +++ src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md diff --git a/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md b/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md new file mode 100644 index 00000000000..92eea36eea3 --- /dev/null +++ b/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md @@ -0,0 +1,3 @@ +### Displaying author's identifier as link + +In the dataset page's metadatatab the author's identifier is displayed as a clickable link, which points to the profile page in the external service (ORCID, VIAF etc.), given that the identifier scheme provides a resolvable landing page. \ No newline at end of file diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java b/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java index 0cb41eb510b..28d2a12141d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java @@ -101,7 +101,7 @@ public boolean isEmpty() { // GND regex from https://www.wikidata.org/wiki/Property:P227 "GND", new LinkTemplate("https://d-nb.info/gnd/%s", "^1[01]?\\d{7}[0-9X]|[47]\\d{6}-\\d|[1-9]\\d{0,7}-[0-9X]|3\\d{7}[0-9X]$"), - // DAI is missing from this list + // note: DAI is missing from this list, because it doesn't have resolvable URL "ResearcherID", new LinkTemplate("https://publons.com/researcher/%s/", "^[A-Z\\d][A-Z\\d-]+[A-Z\\d]$"), "ScopusID", From a8c14451b34bb2fbb98a605d69a47931b94d482e Mon Sep 17 00:00:00 2001 From: pkiraly Date: Wed, 25 Aug 2021 15:06:52 +0200 Subject: [PATCH 07/10] #7978: adding test parameters. --- .../java/edu/harvard/iq/dataverse/DatasetAuthorTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java b/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java index 0d349ee6824..fddb95eda9e 100644 --- a/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/DatasetAuthorTest.java @@ -31,9 +31,10 @@ public static Collection parameters() { { "LCNA", "n82058243", "http://id.loc.gov/authorities/names/n82058243" }, { "VIAF", "172389567", "https://viaf.org/viaf/172389567" }, { "GND", "4079154-3", "https://d-nb.info/gnd/4079154-3" }, - { "ResearcherID", "4079154", "https://publons.com/researcher/4079154/" }, + { "ResearcherID", "634082", "https://publons.com/researcher/634082/" }, { "ResearcherID", "AAW-9289-2021", "https://publons.com/researcher/AAW-9289-2021/" }, - { "ScopusID", "4079154", "https://www.scopus.com/authid/detail.uri?authorId=4079154" }, + { "ResearcherID", "J-9733-2013", "https://publons.com/researcher/J-9733-2013/" }, + { "ScopusID", "6602344670", "https://www.scopus.com/authid/detail.uri?authorId=6602344670" }, { null, null, null, }, }); } From 6e571a159a68abadd2f4dbd2311780908475e477 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 25 Aug 2021 09:52:53 -0400 Subject: [PATCH 08/10] typo #7978 --- .../7978-linking-ORCID-profile-from-metadata-tab.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md b/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md index 92eea36eea3..f19c5dd696b 100644 --- a/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md +++ b/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md @@ -1,3 +1,3 @@ ### Displaying author's identifier as link -In the dataset page's metadatatab the author's identifier is displayed as a clickable link, which points to the profile page in the external service (ORCID, VIAF etc.), given that the identifier scheme provides a resolvable landing page. \ No newline at end of file +In the dataset page's metadata tab the author's identifier is displayed as a clickable link, which points to the profile page in the external service (ORCID, VIAF etc.), given that the identifier scheme provides a resolvable landing page. From 2af4252c228f110fc18d7fcb51b2c035e1175c9e Mon Sep 17 00:00:00 2001 From: pkiraly Date: Fri, 8 Oct 2021 13:26:33 +0200 Subject: [PATCH 09/10] #7978: fixing conflicts and refactoring the solution making an enumeration of linked external identifier services. --- .../harvard/iq/dataverse/DatasetAuthor.java | 41 ++----------- .../dataverse/DatasetFieldCompoundValue.java | 2 +- .../iq/dataverse/ExternalIdentifier.java | 58 +++++++++++++++++++ .../harvard/iq/dataverse/LinkTemplate.java | 21 ------- src/main/webapp/metadataFragment.xhtml | 16 +++-- .../DatasetFieldValueValidatorTest.java | 10 ++-- .../iq/dataverse/ExternalIdentifierTest.java | 53 +++++++++++++++++ 7 files changed, 130 insertions(+), 71 deletions(-) create mode 100644 src/main/java/edu/harvard/iq/dataverse/ExternalIdentifier.java delete mode 100644 src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java create mode 100644 src/test/java/edu/harvard/iq/dataverse/ExternalIdentifierTest.java diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java b/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java index 28d2a12141d..d33d709107f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetAuthor.java @@ -7,8 +7,6 @@ package edu.harvard.iq.dataverse; import java.util.Comparator; -import java.util.Map; -import java.util.regex.Pattern; /** * @@ -88,33 +86,6 @@ public boolean isEmpty() { ); } - public static final Map linkSchemeTemplates = Map.of( - // https://support.orcid.org/hc/en-us/articles/360006897674-Structure-of-the-ORCID-Identifier - "ORCID", - new LinkTemplate("https://orcid.org/%s", "^\\d{4}-\\d{4}-\\d{4}-(\\d{4}|\\d{3}X)$"), - "ISNI", - new LinkTemplate("http://www.isni.org/isni/%s", "^\\d*$"), - "LCNA", - new LinkTemplate("http://id.loc.gov/authorities/names/%s", "^[a-z]+\\d+$"), - "VIAF", - new LinkTemplate("https://viaf.org/viaf/%s", "^\\d*$"), - // GND regex from https://www.wikidata.org/wiki/Property:P227 - "GND", - new LinkTemplate("https://d-nb.info/gnd/%s", "^1[01]?\\d{7}[0-9X]|[47]\\d{6}-\\d|[1-9]\\d{0,7}-[0-9X]|3\\d{7}[0-9X]$"), - // note: DAI is missing from this list, because it doesn't have resolvable URL - "ResearcherID", - new LinkTemplate("https://publons.com/researcher/%s/", "^[A-Z\\d][A-Z\\d-]+[A-Z\\d]$"), - "ScopusID", - new LinkTemplate("https://www.scopus.com/authid/detail.uri?authorId=%s", "^\\d*$") - ); - - /** - * Each author identification type has its own valid pattern/syntax. - */ - public static Pattern getValidPattern(String regex) { - return Pattern.compile(regex); - } - public String getIdentifierAsUrl() { if (idType != null && !idType.isEmpty() && idValue != null && !idValue.isEmpty()) { return getIdentifierAsUrl(idType, idValue); @@ -124,12 +95,12 @@ public String getIdentifierAsUrl() { public static String getIdentifierAsUrl(String idType, String idValue) { if (idType != null && !idType.isEmpty() && idValue != null && !idValue.isEmpty()) { - DatasetFieldValueValidator datasetFieldValueValidator = new DatasetFieldValueValidator(); - if (linkSchemeTemplates.containsKey(idType)) { - LinkTemplate template = linkSchemeTemplates.get(idType); - if (datasetFieldValueValidator.isValidAuthorIdentifier(idValue, template.getPattern())) { - return String.format(template.getTemplate(), idValue); - } + try { + ExternalIdentifier externalIdentifier = ExternalIdentifier.valueOf(idType); + if (externalIdentifier.isValidIdentifier(idValue)) + return externalIdentifier.format(idValue); + } catch (Exception e) { + // non registered identifier } } return null; diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java index 72a5fe15b9a..5d83f1e4f8d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java @@ -153,7 +153,7 @@ public Map getDisplayValueMap() { // todo - this currently only supports child datasetfields with single values // need to determine how we would want to handle multiple Map fieldMap = new LinkedHashMap<>(); - linkMap = new LinkedHashMap<>(); + linkMap.clear(); boolean fixTrailingComma = false; Pair linkComponents = getLinkComponents(); linkScheme = null; diff --git a/src/main/java/edu/harvard/iq/dataverse/ExternalIdentifier.java b/src/main/java/edu/harvard/iq/dataverse/ExternalIdentifier.java new file mode 100644 index 00000000000..0b7285c017e --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/ExternalIdentifier.java @@ -0,0 +1,58 @@ +package edu.harvard.iq.dataverse; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public enum ExternalIdentifier { + ORCID("ORCID", "https://orcid.org/%s", "^\\d{4}-\\d{4}-\\d{4}-(\\d{4}|\\d{3}X)$"), + ISNI("ISNI", "http://www.isni.org/isni/%s", "^\\d*$"), + LCNA("LCNA", "http://id.loc.gov/authorities/names/%s", "^[a-z]+\\d+$"), + VIAF("VIAF", "https://viaf.org/viaf/%s", "^\\d*$"), + // GND regex from https://www.wikidata.org/wiki/Property:P227 + GND("GND", "https://d-nb.info/gnd/%s", "^1[01]?\\d{7}[0-9X]|[47]\\d{6}-\\d|[1-9]\\d{0,7}-[0-9X]|3\\d{7}[0-9X]$"), + // note: DAI is missing from this list, because it doesn't have resolvable URL + ResearcherID("ResearcherID", "https://publons.com/researcher/%s/", "^[A-Z\\d][A-Z\\d-]+[A-Z\\d]$"), + ScopusID("ScopusID", "https://www.scopus.com/authid/detail.uri?authorId=%s", "^\\d*$"); + + private String name; + private String template; + private Pattern pattern; + private Matcher matcher; + + ExternalIdentifier(String name, String template, String regex) { + this.template = template; + this.pattern = Pattern.compile(regex); + this.matcher = pattern.matcher(""); + } + + public ExternalIdentifier of(String name) { + System.err.println(name); + for (ExternalIdentifier identifier : values()) { + System.err.println(" vs " + identifier.name); + if (identifier.name.toLowerCase().equals(name.toLowerCase())) { + return identifier; + } + } + return null; + } + + public boolean isValidIdentifier(String userInput) { + return matcher.reset(userInput).matches(); + } + + public String getName() { + return name; + } + + public String getTemplate() { + return template; + } + + public Pattern getPattern() { + return pattern; + } + + public String format(String idValue) { + return String.format(template, idValue); + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java b/src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java deleted file mode 100644 index 6f9ff2a58fe..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/LinkTemplate.java +++ /dev/null @@ -1,21 +0,0 @@ -package edu.harvard.iq.dataverse; - -import java.util.regex.Pattern; - -public class LinkTemplate { - private String template; - private Pattern pattern; - - public LinkTemplate(String template, String regex) { - this.template = template; - this.pattern = Pattern.compile(regex); - } - - public String getTemplate() { - return template; - } - - public Pattern getPattern() { - return pattern; - } -} diff --git a/src/main/webapp/metadataFragment.xhtml b/src/main/webapp/metadataFragment.xhtml index 0fcda310687..f1b99e9f366 100755 --- a/src/main/webapp/metadataFragment.xhtml +++ b/src/main/webapp/metadataFragment.xhtml @@ -64,7 +64,7 @@ - +

- + - - -
+ +
+
- + - +
diff --git a/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java b/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java index d11d5ca747b..804b573c7b7 100644 --- a/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/DatasetFieldValueValidatorTest.java @@ -141,7 +141,7 @@ public void testIsValid() { @Test public void testIsValidAuthorIdentifierOrcid() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("ORCID").getPattern(); + Pattern pattern = ExternalIdentifier.valueOf("ORCID").getPattern(); assertTrue(validator.isValidAuthorIdentifier("0000-0002-1825-0097", pattern)); // An "X" at the end of an ORCID is less common but still valid. assertTrue(validator.isValidAuthorIdentifier("0000-0002-1694-233X", pattern)); @@ -154,7 +154,7 @@ public void testIsValidAuthorIdentifierOrcid() { @Test public void testIsValidAuthorIdentifierIsni() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("ISNI").getPattern(); + Pattern pattern = ExternalIdentifier.valueOf("ISNI").getPattern(); assertTrue(validator.isValidAuthorIdentifier("0000000121032683", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); } @@ -162,7 +162,7 @@ public void testIsValidAuthorIdentifierIsni() { @Test public void testIsValidAuthorIdentifierLcna() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("LCNA").getPattern(); + Pattern pattern = ExternalIdentifier.valueOf("LCNA").getPattern(); assertTrue(validator.isValidAuthorIdentifier("n82058243", pattern)); assertTrue(validator.isValidAuthorIdentifier("foobar123", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); @@ -171,7 +171,7 @@ public void testIsValidAuthorIdentifierLcna() { @Test public void testIsValidAuthorIdentifierViaf() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("VIAF").getPattern(); + Pattern pattern = ExternalIdentifier.valueOf("VIAF").getPattern(); assertTrue(validator.isValidAuthorIdentifier("172389567", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); } @@ -179,7 +179,7 @@ public void testIsValidAuthorIdentifierViaf() { @Test public void testIsValidAuthorIdentifierGnd() { DatasetFieldValueValidator validator = new DatasetFieldValueValidator(); - Pattern pattern = DatasetAuthor.linkSchemeTemplates.get("GND").getPattern(); + Pattern pattern = ExternalIdentifier.valueOf("GND").getPattern(); assertTrue(validator.isValidAuthorIdentifier("4079154-3", pattern)); assertFalse(validator.isValidAuthorIdentifier("junk", pattern)); } diff --git a/src/test/java/edu/harvard/iq/dataverse/ExternalIdentifierTest.java b/src/test/java/edu/harvard/iq/dataverse/ExternalIdentifierTest.java new file mode 100644 index 00000000000..c14d2e4086e --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/ExternalIdentifierTest.java @@ -0,0 +1,53 @@ +package edu.harvard.iq.dataverse; + +import org.junit.Test; + +import java.util.regex.Pattern; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ExternalIdentifierTest { + + @Test + public void testIsValidAuthorIdentifierOrcid() { + ExternalIdentifier identifier = ExternalIdentifier.valueOf("ORCID"); + assertTrue(identifier.isValidIdentifier("0000-0002-1825-0097")); + // An "X" at the end of an ORCID is less common but still valid. + assertTrue(identifier.isValidIdentifier("0000-0002-1694-233X")); + assertFalse(identifier.isValidIdentifier("0000 0002 1825 0097")); + assertFalse(identifier.isValidIdentifier(" 0000-0002-1825-0097")); + assertFalse(identifier.isValidIdentifier("0000-0002-1825-0097 ")); + assertFalse(identifier.isValidIdentifier("junk")); + } + + @Test + public void testIsValidAuthorIdentifierIsni() { + ExternalIdentifier identifier = ExternalIdentifier.valueOf("ISNI"); + assertTrue(identifier.isValidIdentifier("0000000121032683")); + assertFalse(identifier.isValidIdentifier("junk")); + } + + @Test + public void testIsValidAuthorIdentifierLcna() { + ExternalIdentifier identifier = ExternalIdentifier.valueOf("LCNA"); + assertTrue(identifier.isValidIdentifier("n82058243")); + assertTrue(identifier.isValidIdentifier("foobar123")); + assertFalse(identifier.isValidIdentifier("junk")); + } + + @Test + public void testIsValidAuthorIdentifierViaf() { + ExternalIdentifier identifier = ExternalIdentifier.valueOf("VIAF"); + assertTrue(identifier.isValidIdentifier("172389567")); + assertFalse(identifier.isValidIdentifier("junk")); + } + + @Test + public void testIsValidAuthorIdentifierGnd() { + ExternalIdentifier identifier = ExternalIdentifier.valueOf("GND"); + assertTrue(identifier.isValidIdentifier("4079154-3")); + assertFalse(identifier.isValidIdentifier("junk")); + } + +} From 1e66657ed8d2f1b23d493bd58acb6b905a854f4e Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 17 Nov 2021 11:41:44 -0500 Subject: [PATCH 10/10] mention no link if identifier is not valid #7978 --- .../7978-linking-ORCID-profile-from-metadata-tab.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md b/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md index f19c5dd696b..62c8b308073 100644 --- a/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md +++ b/doc/release-notes/7978-linking-ORCID-profile-from-metadata-tab.md @@ -1,3 +1,3 @@ ### Displaying author's identifier as link -In the dataset page's metadata tab the author's identifier is displayed as a clickable link, which points to the profile page in the external service (ORCID, VIAF etc.), given that the identifier scheme provides a resolvable landing page. +In the dataset page's metadata tab the author's identifier is displayed as a clickable link, which points to the profile page in the external service (ORCID, VIAF etc.), given that the identifier scheme provides a resolvable landing page. If the identifier does not match the expected scheme, a link is not shown.