diff --git a/src/main/java/edu/harvard/iq/dataverse/MailServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/MailServiceBean.java index c67a0293847..dcb66b1bb5b 100644 --- a/src/main/java/edu/harvard/iq/dataverse/MailServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/MailServiceBean.java @@ -720,9 +720,9 @@ public String getMessageTextBasedOnNotification(UserNotification userNotificatio Object[] paramArrayDatasetMentioned = { userNotification.getUser().getName(), BrandingUtil.getInstallationBrandName(), - citingResource.getString("@type"), + citingResource.getString("@type", "External Resource"), citingResource.getString("@id"), - citingResource.getString("name"), + citingResource.getString("name", citingResource.getString("@id")), citingResource.getString("relationship"), systemConfig.getDataverseSiteUrl(), dataset.getGlobalId().toString(), diff --git a/src/main/java/edu/harvard/iq/dataverse/api/LDNInbox.java b/src/main/java/edu/harvard/iq/dataverse/api/LDNInbox.java index 6a9c608dc13..b9bddb02cfe 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/LDNInbox.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/LDNInbox.java @@ -13,25 +13,24 @@ import edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip.IpAddress; import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.pidproviders.PidProvider; -import edu.harvard.iq.dataverse.pidproviders.doi.AbstractDOIProvider; -import edu.harvard.iq.dataverse.pidproviders.handle.HandlePidProvider; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import edu.harvard.iq.dataverse.util.json.JSONLDUtil; import edu.harvard.iq.dataverse.util.json.JsonLDNamespace; import edu.harvard.iq.dataverse.util.json.JsonLDTerm; +import edu.harvard.iq.dataverse.util.json.JsonUtil; import java.util.Date; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.io.StringWriter; +import java.net.URI; import java.sql.Timestamp; import java.util.logging.Logger; import jakarta.ejb.EJB; import jakarta.json.Json; import jakarta.json.JsonObject; -import jakarta.json.JsonValue; +import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonWriter; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.BadRequestException; @@ -43,6 +42,12 @@ import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + @Path("inbox") public class LDNInbox extends AbstractApiBean { @@ -72,12 +77,11 @@ public class LDNInbox extends AbstractApiBean { @Path("/") @Consumes("application/ld+json, application/json-ld") public Response acceptMessage(String body) { + try { IpAddress origin = new DataverseRequest(null, httpRequest).getSourceAddress(); String whitelist = settingsService.get(SettingsServiceBean.Key.LDNMessageHosts.toString(), ""); // Only do something if we listen to this host if (whitelist.equals("*") || whitelist.contains(origin.toString())) { - String citingPID = null; - String citingType = null; boolean sent = false; JsonObject jsonld = null; @@ -101,98 +105,138 @@ public Response acceptMessage(String body) { if (jsonld == null) { throw new BadRequestException("Could not parse message to find acceptable citation link to a dataset."); } - String relationship = "isRelatedTo"; + //To Do - lower level for PR + logger.info(JsonUtil.prettyPrint(jsonld)); + //String relationship = "isRelatedTo"; String name = null; + String itemType = null; JsonLDNamespace activityStreams = JsonLDNamespace.defineNamespace("as", "https://www.w3.org/ns/activitystreams#"); - JsonLDNamespace ietf = JsonLDNamespace.defineNamespace("ietf", "http://www.iana.org/assignments/relation/"); + //JsonLDNamespace ietf = JsonLDNamespace.defineNamespace("ietf", "http://www.iana.org/assignments/relation/"); String objectKey = new JsonLDTerm(activityStreams, "object").getUrl(); if (jsonld.containsKey(objectKey)) { JsonObject msgObject = jsonld.getJsonObject(objectKey); - - citingPID = msgObject.getJsonObject(new JsonLDTerm(ietf, "cite-as").getUrl()).getString("@id"); - logger.fine("Citing PID: " + citingPID); - if (msgObject.containsKey("@type")) { - citingType = msgObject.getString("@type"); - if (citingType.startsWith(JsonLDNamespace.schema.getUrl())) { - citingType = citingType.replace(JsonLDNamespace.schema.getUrl(), ""); - } - if (msgObject.containsKey(JsonLDTerm.schemaOrg("name").getUrl())) { - name = msgObject.getString(JsonLDTerm.schemaOrg("name").getUrl()); + if (new JsonLDTerm(activityStreams, "Relationship").getUrl().equals(msgObject.getString("@type"))) { + // We have a relationship message - need to get the subject and object and + // relationship type + String subjectId = msgObject.getJsonObject(new JsonLDTerm(activityStreams, "subject").getUrl()).getString("@id"); + String objectId = msgObject.getJsonObject(new JsonLDTerm(activityStreams, "object").getUrl()).getString("@id"); + // Best-effort to get name by following redirects and looing for a 'name' field in the returned json + try (CloseableHttpClient client = HttpClients.createDefault()) { + logger.info("Getting " + subjectId); + HttpGet get = new HttpGet(new URI(subjectId)); + get.addHeader("Accept", "application/json"); + + int statusCode=0; + do { + CloseableHttpResponse response = client.execute(get); + statusCode = response.getStatusLine().getStatusCode(); + switch (statusCode) { + case 302: + case 303: + String location=response.getFirstHeader("location").getValue(); + logger.info("Redirecting to: " + location); + get = new HttpGet(location); + get.addHeader("Accept", "application/json"); + + break; + case 200: + String responseString = EntityUtils.toString(response.getEntity(), "UTF-8"); + logger.info("Received: " + responseString); + JsonObject job = JsonUtil.getJsonObject(responseString); + name = job.getString("name", null); + itemType = job.getString("type", null); + break; + default: + logger.info("Received " + statusCode + " when accessing " + subjectId); + } + } while(statusCode == 302); + } catch (Exception e) { + logger.info("Unable to get name from " + subjectId); + logger.info(e.getLocalizedMessage()); } - logger.fine("Citing Type: " + citingType); - String contextKey = new JsonLDTerm(activityStreams, "context").getUrl(); - - if (jsonld.containsKey(contextKey)) { - JsonObject context = jsonld.getJsonObject(contextKey); - for (Map.Entry entry : context.entrySet()) { - - relationship = entry.getKey().replace("_:", ""); - // Assuming only one for now - should check for array and loop - JsonObject citedResource = (JsonObject) entry.getValue(); - String pid = citedResource.getJsonObject(new JsonLDTerm(ietf, "cite-as").getUrl()) - .getString("@id"); - if (citedResource.getString("@type").equals(JsonLDTerm.schemaOrg("Dataset").getUrl())) { - logger.fine("Raw PID: " + pid); - if (pid.startsWith(AbstractDOIProvider.DOI_RESOLVER_URL)) { - pid = pid.replace(AbstractDOIProvider.DOI_RESOLVER_URL, AbstractDOIProvider.DOI_PROTOCOL + ":"); - } else if (pid.startsWith(HandlePidProvider.HDL_RESOLVER_URL)) { - pid = pid.replace(HandlePidProvider.HDL_RESOLVER_URL, HandlePidProvider.HDL_PROTOCOL + ":"); + String relationshipId = msgObject + .getJsonObject(new JsonLDTerm(activityStreams, "relationship").getUrl()).getString("@id"); + if (subjectId != null && objectId != null && relationshipId != null) { + // Strip the URL part from a relationship ID/URL assuming a usable label exists + // after a # char. Default is to use the whole URI. + int index = relationshipId.indexOf("#"); + logger.info("Found # at " + index + " in " + relationshipId); + String relationship = relationshipId.substring(index + 1); + // Parse the URI as a PID and see if this Dataverse instance has this dataset + Optional id = PidProvider.parse(objectId); + if (id.isPresent()) { + //ToDo - avoid reparsing GlobalId by making a findByGlobalId(GlobalId) method? + Dataset dataset = datasetSvc.findByGlobalId(objectId); + if (dataset != null) { + JsonObjectBuilder citingResourceBuilder = Json.createObjectBuilder().add("@id", subjectId) + .add("relationship", relationship); + if(name!=null && !name.isBlank()) { + citingResourceBuilder.add("name",name); + } + if(itemType!=null && !itemType.isBlank()) { + citingResourceBuilder.add("@type",itemType.substring(0,1).toUpperCase() + itemType.substring(1)); } - logger.fine("Protocol PID: " + pid); - Optional id = PidProvider.parse(pid); - Dataset dataset = datasetSvc.findByGlobalId(pid); - if (dataset != null) { - JsonObject citingResource = Json.createObjectBuilder().add("@id", citingPID) - .add("@type", citingType).add("relationship", relationship) - .add("name", name).build(); - StringWriter sw = new StringWriter(128); - try (JsonWriter jw = Json.createWriter(sw)) { - jw.write(citingResource); - } - String jsonstring = sw.toString(); - Set ras = roleService.rolesAssignments(dataset); - - roleService.rolesAssignments(dataset).stream() - .filter(ra -> ra.getRole().permissions() - .contains(Permission.PublishDataset)) - .flatMap( - ra -> roleAssigneeService - .getExplicitUsers(roleAssigneeService - .getRoleAssignee(ra.getAssigneeIdentifier())) - .stream()) - .distinct() // prevent double-send - .forEach(au -> { - - if (au.isSuperuser()) { - userNotificationService.sendNotification(au, - new Timestamp(new Date().getTime()), - UserNotification.Type.DATASETMENTIONED, dataset.getId(), - null, null, true, jsonstring); - - } - }); - sent = true; + JsonObject citingResource = citingResourceBuilder.build(); + StringWriter sw = new StringWriter(128); + try (JsonWriter jw = Json.createWriter(sw)) { + jw.write(citingResource); } + String jsonstring = sw.toString(); + logger.info("Storing: " + jsonstring); + //Set ras = roleService.rolesAssignments(dataset); + + roleService.rolesAssignments(dataset).stream() + .filter(ra -> ra.getRole().permissions().contains(Permission.PublishDataset)) + .flatMap(ra -> roleAssigneeService + .getExplicitUsers( + roleAssigneeService.getRoleAssignee(ra.getAssigneeIdentifier())) + .stream()) + .distinct() // prevent double-send + .forEach(au -> { + + if (au.isSuperuser()) { + userNotificationService.sendNotification(au, + new Timestamp(new Date().getTime()), + UserNotification.Type.DATASETMENTIONED, dataset.getId(), null, + null, true, jsonstring); + + } + }); + sent = true; } + } else { + logger.info("Didn't find dataset"); + // We don't have a dataset corresponding to the object of the relationship - do + // nothing } + + } else { + // Can't get subject, relationship, object from message + logger.info("Can't find the subject, relationship or object in the message - ignoring"); + } + } else { + + // This isn't a Relationship announcement message - ignore + logger.info("This is not a relationship announcement - ignoring message of type " + + msgObject.getJsonString("@type")); } } if (!sent) { - if (citingPID == null || citingType == null) { - throw new BadRequestException( - "Could not parse message to find acceptable citation link to a dataset."); - } else { - throw new ServiceUnavailableException( - "Unable to process message. Please contact the administrators."); - } + throw new ServiceUnavailableException("Unable to process message. Please contact the administrators."); } } else { logger.info("Ignoring message from IP address: " + origin.toString()); throw new ForbiddenException("Inbox does not acept messages from this address"); } + } catch (Throwable t) { + logger.severe(t.getLocalizedMessage()); + t.printStackTrace(); + + throw t; + } return ok("Message Received"); } } diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/JSONLDUtil.java b/src/main/java/edu/harvard/iq/dataverse/util/json/JSONLDUtil.java index 380cef6aa9d..242847d2e25 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/JSONLDUtil.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/JSONLDUtil.java @@ -552,7 +552,7 @@ public static JsonObject decontextualizeJsonLD(String jsonLDString) { logger.fine("Decontextualized object: " + jsonld); return jsonld; } catch (JsonLdError e) { - System.out.println(e.getMessage()); + logger.warning(e.getMessage()); return null; } } diff --git a/src/main/java/edu/harvard/iq/dataverse/workflow/internalspi/LDNAnnounceDatasetVersionStep.java b/src/main/java/edu/harvard/iq/dataverse/workflow/internalspi/LDNAnnounceDatasetVersionStep.java index 124eea801d9..abe9f26f98d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/workflow/internalspi/LDNAnnounceDatasetVersionStep.java +++ b/src/main/java/edu/harvard/iq/dataverse/workflow/internalspi/LDNAnnounceDatasetVersionStep.java @@ -18,8 +18,11 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -32,6 +35,7 @@ import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonString; import jakarta.json.JsonValue; import org.apache.http.client.methods.CloseableHttpResponse; @@ -56,10 +60,6 @@ public class LDNAnnounceDatasetVersionStep implements WorkflowStep { private static final String LDN_TARGET = ":LDNTarget"; private static final String RELATED_PUBLICATION = "publication"; - JsonLDTerm publicationIDType = null; - JsonLDTerm publicationIDNumber = null; - JsonLDTerm publicationURL = null; - public LDNAnnounceDatasetVersionStep(Map paramSet) { new HashMap<>(paramSet); } @@ -74,35 +74,54 @@ public WorkflowStepResult run(WorkflowContext context) { CloseableHttpClient client = HttpClients.createDefault(); // build method - + HttpPost announcement; try { - announcement = buildAnnouncement(false, context, target); - } catch (URISyntaxException e) { - return new Failure("LDNAnnounceDatasetVersion workflow step failed: unable to parse inbox in :LDNTarget setting."); - } - if(announcement==null) { - logger.info(context.getDataset().getGlobalId().asString() + "does not have metadata required to send LDN message. Nothing sent."); - return OK; - } - // execute - try (CloseableHttpResponse response = client.execute(announcement)) { - int code = response.getStatusLine().getStatusCode(); - if (code >= 200 && code < 300) { - // HTTP OK range - return OK; - } else { - String responseBody = new String(response.getEntity().getContent().readAllBytes(), - StandardCharsets.UTF_8); - ; - return new Failure("Error communicating with " + inboxUrl + ". Server response: " + responseBody - + " (" + response + ")."); + // First check that we have what is required + Dataset d = context.getDataset(); + DatasetVersion dv = d.getReleasedVersion(); + List dvf = dv.getDatasetFields(); + Map fields = new HashMap(); + List reqFields = Arrays + .asList(((String) context.getSettings().getOrDefault(REQUIRED_FIELDS, "")).split(",\\s*")); + for (DatasetField df : dvf) { + if (!df.isEmpty() && reqFields.contains(df.getDatasetFieldType().getName())) { + fields.put(df.getDatasetFieldType().getName(), df); + } + } + // Loop through and send a message for each supported relationship + boolean success = false; + for (JsonObject rel : getObjects(context, fields).getValuesAs(JsonObject.class)) { + announcement = buildAnnouncement(d, rel, target); + // execute + try (CloseableHttpResponse response = client.execute(announcement)) { + int code = response.getStatusLine().getStatusCode(); + if (code >= 200 && code < 300) { + // HTTP OK range + success = true; + logger.fine("Successfully sent message for " + rel.toString()); + } else { + String responseBody = new String(response.getEntity().getContent().readAllBytes(), + StandardCharsets.UTF_8); + ; + return new Failure((success ? "Partial failure" : "") + "Error communicating with " + + inboxUrl + " for relationship " + rel.toString() + ". Server response: " + + responseBody + " (" + response + ")."); + } + + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error communicating with remote server: " + ex.getMessage(), ex); + return new Failure((success ? "Partial failure" : "") + "Error executing request: " + + ex.getLocalizedMessage(), "Cannot communicate with remote server."); + } + } + // Any failure and we would have returned already. + return OK; - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error communicating with remote server: " + ex.getMessage(), ex); - return new Failure("Error executing request: " + ex.getLocalizedMessage(), - "Cannot communicate with remote server."); + } catch (URISyntaxException e) { + return new Failure( + "LDNAnnounceDatasetVersion workflow step failed: unable to parse inbox in :LDNTarget setting."); } } return new Failure("LDNAnnounceDatasetVersion workflow step failed: :LDNTarget setting missing or invalid."); @@ -118,144 +137,168 @@ public void rollback(WorkflowContext context, Failure reason) { throw new UnsupportedOperationException("Not supported yet."); // This class does not need to resume. } - HttpPost buildAnnouncement(boolean qb, WorkflowContext ctxt, JsonObject target) throws URISyntaxException { - - // First check that we have what is required - DatasetVersion dv = ctxt.getDataset().getReleasedVersion(); - List dvf = dv.getDatasetFields(); - Map fields = new HashMap(); - String[] requiredFields = ((String) ctxt.getSettings().getOrDefault(REQUIRED_FIELDS, "")).split(",\\s*"); - for (String field : requiredFields) { - fields.put(field, null); - } - Set reqFields = fields.keySet(); - for (DatasetField df : dvf) { - if(!df.isEmpty() && reqFields.contains(df.getDatasetFieldType().getName())) { - fields.put(df.getDatasetFieldType().getName(), df); - } - } - if (fields.containsValue(null)) { - logger.fine("DatasetVersion doesn't contain metadata required to trigger announcement"); - return null; - } - // We do, so construct the json-ld body and method - + /** + * Scan through all fields and return an array of relationship JsonObjects with + * subjectId, relationship, objectId, and @context + * + * @param ctxt + * @param fields + * @return JsonArray of JsonObjects with subjectId, relationship, objectId, + * and @context + */ + JsonArray getObjects(WorkflowContext ctxt, Map fields) { + JsonArrayBuilder jab = Json.createArrayBuilder(); Map localContext = new HashMap(); - JsonObjectBuilder coarContext = Json.createObjectBuilder(); Map emptyCvocMap = new HashMap(); - boolean includeLocalContext = false; + + Dataset d = ctxt.getDataset(); for (Entry entry : fields.entrySet()) { DatasetField field = entry.getValue(); DatasetFieldType dft = field.getDatasetFieldType(); - String dfTypeName = entry.getKey(); JsonValue jv = OREMap.getJsonLDForField(field, false, emptyCvocMap, localContext); - switch (dfTypeName) { - case RELATED_PUBLICATION: - JsonArrayBuilder relArrayBuilder = Json.createArrayBuilder(); - publicationIDType = null; - publicationIDNumber = null; - publicationURL = null; - Collection childTypes = dft.getChildDatasetFieldTypes(); - for (DatasetFieldType cdft : childTypes) { - switch (cdft.getName()) { - case "publicationURL": - publicationURL = cdft.getJsonLDTerm(); - break; - case "publicationIDType": - publicationIDType = cdft.getJsonLDTerm(); - break; - case "publicationIDNumber": - publicationIDNumber = cdft.getJsonLDTerm(); - break; + // jv is a JsonArray for multi-val fields, so loop + if (jv != null) { + if (jv instanceof JsonArray) { + JsonArray rels = (JsonArray) jv; + Iterator iter = rels.iterator(); + while (iter.hasNext()) { + JsonValue jval = iter.next(); + jab.add(getRelationshipObject(dft, jval, d, localContext)); } - + } else { + jab.add(getRelationshipObject(dft, jv, d, localContext)); } + } - if (jv != null) { - if (jv instanceof JsonArray) { - JsonArray rels = (JsonArray) jv; - for (JsonObject jo : rels.getValuesAs(JsonObject.class)) { - String id = getBestPubId(jo); - relArrayBuilder.add(Json.createObjectBuilder().add("id", id).add("ietf:cite-as", id) - .add("type", "sorg:ScholaryArticle").build()); - } - } + } + return jab.build(); + } - else { // JsonObject - String id = getBestPubId((JsonObject) jv); - relArrayBuilder.add(Json.createObjectBuilder().add("id", id).add("ietf:cite-as", id) - .add("type", "sorg:ScholaryArticle").build()); - } - } - coarContext.add("IsSupplementTo", relArrayBuilder); - break; - default: - if (jv != null) { - includeLocalContext = true; - coarContext.add(dft.getJsonLDTerm().getLabel(), jv); - } + private JsonObject getRelationshipObject(DatasetFieldType dft, JsonValue jval, Dataset d, + Map localContext) { + String id = getBestId(dft, jval); + return Json.createObjectBuilder().add("object", id).add("relationship", dft.getJsonLDTerm().getUrl()) + .add("subject", d.getGlobalId().asURL().toString()).add("id", "urn:uuid:" + UUID.randomUUID().toString()).add("type","Relationship").build(); + } + + HttpPost buildAnnouncement(Dataset d, JsonObject rel, JsonObject target) throws URISyntaxException { - } - } - dvf.get(0).getDatasetFieldType().getName(); JsonObjectBuilder job = Json.createObjectBuilder(); JsonArrayBuilder context = Json.createArrayBuilder().add("https://purl.org/coar/notify") .add("https://www.w3.org/ns/activitystreams"); - if (includeLocalContext && !localContext.isEmpty()) { - JsonObjectBuilder contextBuilder = Json.createObjectBuilder(); - for (Entry e : localContext.entrySet()) { - contextBuilder.add(e.getKey(), e.getValue()); - } - context.add(contextBuilder); - } job.add("@context", context); job.add("id", "urn:uuid:" + UUID.randomUUID().toString()); job.add("actor", Json.createObjectBuilder().add("id", SystemConfig.getDataverseSiteUrlStatic()) .add("name", BrandingUtil.getInstallationBrandName()).add("type", "Service")); - job.add("context", coarContext); - Dataset d = ctxt.getDataset(); - job.add("object", - Json.createObjectBuilder().add("id", d.getLocalURL()) - .add("ietf:cite-as", d.getGlobalId().asURL()) - .add("sorg:name", d.getDisplayName()).add("type", "sorg:Dataset")); + job.add("object", rel); job.add("origin", Json.createObjectBuilder().add("id", SystemConfig.getDataverseSiteUrlStatic()) .add("inbox", SystemConfig.getDataverseSiteUrlStatic() + "/api/inbox").add("type", "Service")); job.add("target", target); - job.add("type", Json.createArrayBuilder().add("Announce").add("coar-notify:ReleaseAction")); + job.add("type", Json.createArrayBuilder().add("Announce").add("coar-notify:RelationshipAction")); HttpPost annPost = new HttpPost(); annPost.setURI(new URI(target.getString("inbox"))); String body = JsonUtil.prettyPrint(job.build()); - logger.fine("Body: " + body); + logger.info("Body: " + body); annPost.setEntity(new StringEntity(JsonUtil.prettyPrint(body), "utf-8")); annPost.setHeader("Content-Type", "application/ld+json"); return annPost; } - private String getBestPubId(JsonObject jo) { + private String getBestId(DatasetFieldType dft, JsonValue jv) { + // Primitive value + if (jv instanceof JsonString) { + return ((JsonString) jv).getString(); + } + // Compound - apply type specific logic to get best Id + JsonObject jo = jv.asJsonObject(); String id = null; - if (jo.containsKey(publicationURL.getLabel())) { - id = jo.getString(publicationURL.getLabel()); - } else if (jo.containsKey(publicationIDType.getLabel())) { - if ((jo.containsKey(publicationIDNumber.getLabel()))) { - String number = jo.getString(publicationIDNumber.getLabel()); - - switch (jo.getString(publicationIDType.getLabel())) { - case "doi": - if (number.startsWith("https://doi.org/")) { - id = number; - } else if (number.startsWith("doi:")) { - id = "https://doi.org/" + number.substring(4); - } + switch (dft.getName()) { + case RELATED_PUBLICATION: + JsonLDTerm publicationIDType = null; + JsonLDTerm publicationIDNumber = null; + JsonLDTerm publicationURL = null; + Collection childTypes = dft.getChildDatasetFieldTypes(); + for (DatasetFieldType cdft : childTypes) { + switch (cdft.getName()) { + case "publicationURL": + publicationURL = cdft.getJsonLDTerm(); break; - case "DASH-URN": - if (number.startsWith("http")) { - id = number; - } + case "publicationIDType": + publicationIDType = cdft.getJsonLDTerm(); + break; + case "publicationIDNumber": + publicationIDNumber = cdft.getJsonLDTerm(); break; } } + if (jo.containsKey(publicationURL.getLabel())) { + id = jo.getString(publicationURL.getLabel()); + } else if (jo.containsKey(publicationIDType.getLabel())) { + if ((jo.containsKey(publicationIDNumber.getLabel()))) { + String number = jo.getString(publicationIDNumber.getLabel()); + + switch (jo.getString(publicationIDType.getLabel())) { + case "doi": + if (number.startsWith("https://doi.org/")) { + id = number; + } else if (number.startsWith("doi:")) { + id = "https://doi.org/" + number.substring(4); + } else { + // Assume a raw DOI, e.g. 10.5072/FK2ABCDEF + id = "https://doi.org/" + number; + } + break; + case "DASH-URN": + if (number.startsWith("http")) { + id = number; + } + break; + } + } + } + break; + default: + Collection childDFTs = dft.getChildDatasetFieldTypes(); + // Loop through child fields and select one + // The order of preference is for a field with URL in the name, followed by one + // with 'ID',then 'Name', and as a last resort, a field. + for (DatasetFieldType cdft : childDFTs) { + String fieldname = cdft.getName(); + if (fieldname.contains("URL")) { + if (jo.containsKey(cdft.getJsonLDTerm().getLabel())) { + id = jo.getString(cdft.getJsonLDTerm().getLabel()); + break; + } + } + } + if (id == null) { + for (DatasetFieldType cdft : childDFTs) { + String fieldname = cdft.getName(); + + if (fieldname.contains("ID") || fieldname.contains("Id")) { + if (jo.containsKey(cdft.getJsonLDTerm().getLabel())) { + id = jo.getString(cdft.getJsonLDTerm().getLabel()); + break; + } + + } + } + } + if (id == null) { + for (DatasetFieldType cdft : childDFTs) { + String fieldname = cdft.getName(); + + if (fieldname.contains("Name")) { + if (jo.containsKey(cdft.getJsonLDTerm().getLabel())) { + id = jo.getString(cdft.getJsonLDTerm().getLabel()); + break; + } + } + } + } + id = jo.getString(jo.keySet().iterator().next()); } return id; } diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index c47356008ff..fd592d5a036 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -244,6 +244,7 @@ notification.workflowFailed=An external workflow run on {0} in {1} has failed. C notification.workflowSucceeded=An external workflow run on {0} in {1} has succeeded. Check your email and/or view the Dataset page which may have additional details. notification.statusUpdated=The status of dataset {0} has been updated to {1}. notification.datasetMentioned=Announcement Received: Newly released {0} {2} {3} Dataset {4}. +notification.datasetMentioned.itemType=Resource notification.ingestCompleted=Dataset {1} has one or more tabular files that completed the tabular ingest process and are available in archival formats. notification.ingestCompletedWithErrors=Dataset {1} has one or more tabular files that are available but are not supported for tabular ingest. diff --git a/src/main/webapp/dataverseuser.xhtml b/src/main/webapp/dataverseuser.xhtml index bcb71733716..478931264ae 100644 --- a/src/main/webapp/dataverseuser.xhtml +++ b/src/main/webapp/dataverseuser.xhtml @@ -454,9 +454,9 @@ - + - + #{item.theObject.getDisplayName()}