Skip to content

Commit

Permalink
DD-174 Signposting (IQSS#46)
Browse files Browse the repository at this point in the history
* set default when no config is set for signposting
* modification according to reviews
* move long json string from code to bunddle
* allow empty config on the level 2 profile
* revision based on Herbert feedback
* coding style cleanup SignpostingResources
* remove leading comma
* fix capitalize with header name
  • Loading branch information
vicding-mi authored Feb 18, 2021
1 parent c6d3df2 commit 2e6deae
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 82 deletions.
174 changes: 94 additions & 80 deletions src/main/java/edu/harvard/iq/dataverse/util/SignpostingResources.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
package edu.harvard.iq.dataverse.util;

/**
* Eko Indarto, DANS
* Vic Ding, DANS
*
* This file prepares the resources used in Signposting
*
* It requires correspondence configuration to function well.
* The configuration key used is SignpostingConf.
* It is a json structure shown below
*
* useDefaultFileType is an on/off switch during linkset creating time, it controls whether the default type is
* used, which is always Dataset
* {
* "indetifier-schema": {"ORCID":"https://orcid.org/", "ISNI":"https://isni.org/isni/", "ScopusID":"https://www.scopus.com/authid/detail.uri?authorId="},
* "license": {"CC0":"https://creativecommons.org/licenses/by/4.0/", "MIT": "https://url", "APACHE":"https://url"},
* "cite-as": {"doi":"https://citation.crosscite.org/format?style=bibtex&doi=", "type":"application/vnd.datacite.datacite+json"},
* "useDefaultFileType": true,
* "defaultFileTypeValue": "https://schema.org/Dataset"
* }
*
* The configuration can be modified during run time by the administrator.
*
/*
Eko Indarto, DANS
Vic Ding, DANS
This file prepares the resources used in Signposting
It requires correspondence configuration to function well.
The configuration key used is SignpostingConf.
It is a json structure shown below
useDefaultFileType is an on/off switch during linkset creating time, it controls whether the default type is
used, which is always Dataset
The configuration can be modified during run time by the administrator.
*/

import edu.harvard.iq.dataverse.*;
Expand All @@ -48,13 +40,14 @@ public class SignpostingResources {
private static final Logger logger = Logger.getLogger(SignpostingResources.class.getCanonicalName());
SystemConfig systemConfig;
DatasetVersion workingDatasetVersion;
JsonObject idschemaJsonObj;
JsonObject licJsonObj;
JsonObject citeAsJsonObj;
JsonObject describedByJsonObj;
Boolean useDefaultFileType;
String defaultFileTypeValue;
int maxAuthors;
int maxItems;

public SignpostingResources(SystemConfig systemConfig, DatasetVersion workingDatasetVersion, String jsonSetting){
public SignpostingResources(SystemConfig systemConfig, DatasetVersion workingDatasetVersion, String jsonSetting) {
this.systemConfig = systemConfig;
this.workingDatasetVersion = workingDatasetVersion;
if (jsonSetting == null) {
Expand All @@ -63,46 +56,47 @@ public SignpostingResources(SystemConfig systemConfig, DatasetVersion workingDat
JsonReader jsonReader = Json.createReader(new StringReader(jsonSetting));
JsonObject spJsonSetting = jsonReader.readObject();
jsonReader.close();
idschemaJsonObj = spJsonSetting.getJsonObject("indetifier-schema");
licJsonObj = spJsonSetting.getJsonObject("license");
citeAsJsonObj = spJsonSetting.getJsonObject("cite-as");
describedByJsonObj = spJsonSetting.getJsonObject("describedby");
useDefaultFileType = spJsonSetting.getBoolean("useDefaultFileType", true);
defaultFileTypeValue = spJsonSetting.getString("defaultFileTypeValue", "https://schema.org/Dataset");
maxAuthors = spJsonSetting.getInt("maxAuthors", 5);
maxItems = spJsonSetting.getInt("maxItems", 5);
}

/**
* Get identifier schema for each author
*
* Author may have identifiers from different providers
* ORCID: the url format is https://orcid.org/:id
* ISNI: the url format is https://isni.org/isni/:id
* ScopusID: the url format is https://www.scopus.com/authid/detail.uri?authorId=:id
* VIAF: the url format is http://viaf.org/viaf/:id
*
* For example:
* if author has VIAF
* Link: <http://viaf.org/viaf/:id/>; rel="author"
* if author has VIAF
* Link: <http://viaf.org/viaf/:id/>; rel="author"
*
* @param datasetAuthors
* @return
* @param datasetAuthors list of all DatasetAuthor object
* @return all the non empty author links in a string
*/
private String getIdentifierSchema(List<DatasetAuthor> datasetAuthors) {
StringBuilder sb = new StringBuilder();
String singleAuthorString;
String identifierSchema = "";

for (DatasetAuthor da:datasetAuthors){
if (da.getIdValue() != null && !da.getIdValue().isEmpty()) {
sb.append("<").
append(idschemaJsonObj.getString(da.getIdType())).
append(da.getIdValue()).
append(">;rel=\"author\"");
if (identifierSchema == "") {
identifierSchema = sb.toString();
for (DatasetAuthor da : datasetAuthors) {
logger.info(String.format(
"idtype: %s; idvalue: %s, affiliation: %s; identifierUrl: %s",
da.getIdType(),
da.getIdValue(),
da.getAffiliation(),
da.getIdentifierAsUrl()
));
if (da.getIdentifierAsUrl() != null && !da.getIdentifierAsUrl().trim().isEmpty()) {
singleAuthorString = "<" + da.getIdentifierAsUrl() + ">;rel=\"author\"";
if (Objects.equals(identifierSchema, "")) {
identifierSchema = singleAuthorString;
} else {
identifierSchema = String.join(",", identifierSchema, sb.toString());
identifierSchema = String.join(",", identifierSchema, singleAuthorString);
}
}
}

logger.info(String.format("identifierSchema: %s", identifierSchema));
return identifierSchema;
}

Expand All @@ -111,7 +105,7 @@ private String getIdentifierSchema(List<DatasetAuthor> datasetAuthors) {
*
* @return comma delimited string
*/
public String getLinks(){
public String getLinks() {
List<String> valueList = new LinkedList<>();
Dataset ds = workingDatasetVersion.getDataset();

Expand All @@ -125,8 +119,8 @@ public String getLinks(){
valueList.add(citeAs);
}

String describedby = "<" + citeAsJsonObj.getString(ds.getProtocol()) + ds.getAuthority() + "/"
+ ds.getIdentifier() + ">;rel=\"describedby\"" + "; type=\""+ citeAsJsonObj.getString("type") + "\"";
String describedby = "<" + describedByJsonObj.getString(ds.getProtocol()) + ds.getAuthority() + "/"
+ ds.getIdentifier() + ">;rel=\"describedby\"" + ";type=\"" + describedByJsonObj.getString("type") + "\"";
describedby += ",<" + systemConfig.getDataverseSiteUrl() + "/api/datasets/export?exporter=schema.org&persistentId="
+ ds.getProtocol() + ":" + ds.getAuthority() + "/" + ds.getIdentifier() + ">;rel=\"describedby\"" + ";type=\"application/json+ld\"";
valueList.add(describedby);
Expand All @@ -135,8 +129,8 @@ public String getLinks(){
valueList.add(type);

// TODO: support only CC0 now, should add flexible license support when flex-terms is ready
String license = "";
if (workingDatasetVersion.getTermsOfUseAndAccess().getLicense() == TermsOfUseAndAccess.License.CC0){
String license;
if (workingDatasetVersion.getTermsOfUseAndAccess().getLicense() == TermsOfUseAndAccess.License.CC0) {
// On the current Dataverse, only None and CC0. In the signposting protocol: cardinality is 1
license = "<https://creativecommons.org/publicdomain/zero/1.0/>;rel=\"license\"";
valueList.add(license);
Expand All @@ -151,42 +145,52 @@ public String getLinks(){
return String.join(", ", valueList);
}

private JsonArrayBuilder getIdentifiersSchema(List<DatasetAuthor> datasetAuthors){
private JsonArrayBuilder getIdentifiersSchema(List<DatasetAuthor> datasetAuthors) {
if (datasetAuthors.size() > maxAuthors) return null;
JsonArrayBuilder authors = Json.createArrayBuilder();
for (DatasetAuthor da:datasetAuthors){
if (da.getIdValue() != null && !da.getIdValue().isEmpty()) {
authors.add(jsonObjectBuilder().add("href", idschemaJsonObj.getString(da.getIdType()) + da.getIdValue()));
boolean returnNull = true;
for (DatasetAuthor da : datasetAuthors) {
if (da.getIdentifierAsUrl() != null && !da.getIdentifierAsUrl().trim().isEmpty()) {
authors.add(jsonObjectBuilder().add("href", da.getIdentifierAsUrl()));
returnNull = false;
}
}
return authors;
return returnNull ? null : authors;
}

private JsonArrayBuilder getJsonItems(List<FileMetadata> fms) {
if (fms.size() > maxItems) return null;
JsonArrayBuilder items = Json.createArrayBuilder();
for (FileMetadata fm : fms) {
DataFile df = fm.getDataFile();
items.add(jsonObjectBuilder().add("href", getPublicDownloadUrl(df)).add("type", df.getContentType()));
}

return items;
}

public JsonArrayBuilder getJsonLinkset() {
Dataset ds = workingDatasetVersion.getDataset();
String landingPage = systemConfig.getDataverseSiteUrl() + "/dataset.xhtml?persistentId=" + ds.getProtocol() + ":" + ds.getAuthority() + "/" + ds.getIdentifier() ;
String landingPage = systemConfig.getDataverseSiteUrl() + "/dataset.xhtml?persistentId=" + ds.getProtocol() + ":" + ds.getAuthority() + "/" + ds.getIdentifier();
JsonArrayBuilder authors = getIdentifiersSchema(workingDatasetVersion.getDatasetAuthors());

JsonArrayBuilder items = Json.createArrayBuilder();

List<FileMetadata> fms = workingDatasetVersion.getFileMetadatas();
for (FileMetadata fm:fms){
DataFile df = fm.getDataFile();
items.add(jsonObjectBuilder().add("href", getPublicDownloadUrl(df)).add("type",df.getContentType()));
}
JsonArrayBuilder items = getJsonItems(fms);

String license = "";
if (workingDatasetVersion.getTermsOfUseAndAccess().getLicense() == TermsOfUseAndAccess.License.CC0){
if (workingDatasetVersion.getTermsOfUseAndAccess().getLicense() == TermsOfUseAndAccess.License.CC0) {
license = licJsonObj.getString(TermsOfUseAndAccess.License.CC0.name());
}

JsonArrayBuilder mediaTypes = Json.createArrayBuilder();
mediaTypes.add(
jsonObjectBuilder().add(
"href",
citeAsJsonObj.getJsonObject(ds.getProtocol() + ds.getAuthority() + "/" + ds.getIdentifier())
describedByJsonObj.getString(ds.getProtocol()) + ds.getAuthority() + "/"
+ ds.getIdentifier()
).add(
"type",
citeAsJsonObj.getString("type")
describedByJsonObj.getString("type")
)
);

Expand All @@ -199,35 +203,45 @@ public JsonArrayBuilder getJsonLinkset() {
"application/json+ld"
)
);
JsonArrayBuilder linkset = Json.createArrayBuilder();
JsonArrayBuilder linksetJsonObj = Json.createArrayBuilder();
JsonObjectBuilder mandatory = jsonObjectBuilder()
.add("anchor", landingPage)
.add("cite-as", Json.createArrayBuilder().add(jsonObjectBuilder().add("href", ds.getPersistentURL())))
.add("type", Json.createArrayBuilder().add(jsonObjectBuilder().add("href", "https://schema.org/AboutPage")))
.add("author", authors)
.add("license", jsonObjectBuilder().add("href", license))
.add("item", items).add("describedby", mediaTypes);
linkset.add(mandatory);
.add("type", Json.createArrayBuilder().add(jsonObjectBuilder().add("href", "https://schema.org/AboutPage")));

if (authors != null) {
mandatory.add("author", authors);
}
if (license != null && !license.trim().isEmpty()) {
mandatory.add("license", jsonObjectBuilder().add("href", license));
}
if (!mediaTypes.toString().trim().isEmpty()) {
mandatory.add("describedby", mediaTypes);
}
if (items != null) {
mandatory.add("item", items);
}
linksetJsonObj.add(mandatory);

if (useDefaultFileType) {
for (FileMetadata fm:fms){
for (FileMetadata fm : fms) {
DataFile df = fm.getDataFile();
JsonObjectBuilder itemAnchor = jsonObjectBuilder().add("anchor", getPublicDownloadUrl(df));
itemAnchor.add("collection", Json.createArrayBuilder().add(jsonObjectBuilder()
.add("href", landingPage)).add(jsonObjectBuilder().add("type", defaultFileTypeValue)));
linkset.add(itemAnchor);
linksetJsonObj.add(itemAnchor);
}
} else {
for (FileMetadata fm:fms){
for (FileMetadata fm : fms) {
DataFile df = fm.getDataFile();
JsonObjectBuilder itemAnchor = jsonObjectBuilder().add("anchor", getPublicDownloadUrl(df));
itemAnchor.add("collection", Json.createArrayBuilder().add(jsonObjectBuilder()
.add("href", landingPage)));
linkset.add(itemAnchor);
linksetJsonObj.add(itemAnchor);
}
}

return linkset;
return linksetJsonObj;
}


Expand All @@ -240,7 +254,7 @@ private String getPublicDownloadUrl(DataFile dataFile) {
}

if (storageIO instanceof SwiftAccessIO) {
String fileDownloadUrl = null;
String fileDownloadUrl;
SwiftAccessIO<DataFile> swiftIO = (SwiftAccessIO<DataFile>) storageIO;
try {
swiftIO.open();
Expand All @@ -249,7 +263,7 @@ private String getPublicDownloadUrl(DataFile dataFile) {
}

//if its a public install, lets just give users the permanent URL!
if (systemConfig.isPublicInstall()){
if (systemConfig.isPublicInstall()) {
fileDownloadUrl = swiftIO.getRemoteUrl();
} else {
//TODO: if a user has access to this file, they should be given the swift url
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/propertyFiles/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2570,4 +2570,4 @@ publishDatasetCommand.pidNotReserved=Cannot publish dataset because its persiste
api.errors.invalidApiToken=Invalid API token.

# Signposting configuration
signposting.configuration.SignpostingConf = {"indetifier-schema": {"ORCID":"https://orcid.org/", "ISNI":"https://isni.org/isni/", "ScopusID":"https://www.scopus.com/authid/detail.uri?authorId="}, "license": {"CC0":"https://creativecommons.org/licenses/by/4.0/", "MIT": "https://url", "APACHE":"https://url"},"cite-as": {"doi":"https://citation.crosscite.org/format?style=bibtex&doi=", "type":"application/vnd.datacite.datacite+json"},"useDefaultFileType": true,"defaultFileTypeValue": "https://schema.org/Dataset"}
signposting.configuration.SignpostingConf={"license": {"CC0": "https://creativecommons.org/licenses/cc0/"},"describedby": {"doi": "https://doi.org/","type": "application/vnd.citationstyles.csl+json"},"useDefaultFileType": true,"defaultFileTypeValue": "https://schema.org/Dataset","maxItems": 5,"maxAuthors": 5}
2 changes: 1 addition & 1 deletion src/main/webapp/dataset.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
<o:importFunctions type="edu.harvard.iq.dataverse.util.MarkupChecker" />
<f:metadata>
<!-- Add Signposting-->
<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('link', DatasetPage.getSignpostingLinkHeader())}" />
<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('Link', DatasetPage.getSignpostingLinkHeader())}" />
<!-- End add Signposting-->
<f:viewParam name="id" value="#{DatasetPage.id}"/>
<o:viewParam name="ownerId" value="#{DatasetPage.ownerId}"/>
Expand Down

0 comments on commit 2e6deae

Please sign in to comment.