Skip to content

Commit 2e6deae

Browse files
authored
DD-174 Signposting (#46)
* 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
1 parent c6d3df2 commit 2e6deae

File tree

3 files changed

+96
-82
lines changed

3 files changed

+96
-82
lines changed

src/main/java/edu/harvard/iq/dataverse/util/SignpostingResources.java

Lines changed: 94 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
11
package edu.harvard.iq.dataverse.util;
22

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

2719
import edu.harvard.iq.dataverse.*;
@@ -48,13 +40,14 @@ public class SignpostingResources {
4840
private static final Logger logger = Logger.getLogger(SignpostingResources.class.getCanonicalName());
4941
SystemConfig systemConfig;
5042
DatasetVersion workingDatasetVersion;
51-
JsonObject idschemaJsonObj;
5243
JsonObject licJsonObj;
53-
JsonObject citeAsJsonObj;
44+
JsonObject describedByJsonObj;
5445
Boolean useDefaultFileType;
5546
String defaultFileTypeValue;
47+
int maxAuthors;
48+
int maxItems;
5649

57-
public SignpostingResources(SystemConfig systemConfig, DatasetVersion workingDatasetVersion, String jsonSetting){
50+
public SignpostingResources(SystemConfig systemConfig, DatasetVersion workingDatasetVersion, String jsonSetting) {
5851
this.systemConfig = systemConfig;
5952
this.workingDatasetVersion = workingDatasetVersion;
6053
if (jsonSetting == null) {
@@ -63,46 +56,47 @@ public SignpostingResources(SystemConfig systemConfig, DatasetVersion workingDat
6356
JsonReader jsonReader = Json.createReader(new StringReader(jsonSetting));
6457
JsonObject spJsonSetting = jsonReader.readObject();
6558
jsonReader.close();
66-
idschemaJsonObj = spJsonSetting.getJsonObject("indetifier-schema");
6759
licJsonObj = spJsonSetting.getJsonObject("license");
68-
citeAsJsonObj = spJsonSetting.getJsonObject("cite-as");
60+
describedByJsonObj = spJsonSetting.getJsonObject("describedby");
6961
useDefaultFileType = spJsonSetting.getBoolean("useDefaultFileType", true);
7062
defaultFileTypeValue = spJsonSetting.getString("defaultFileTypeValue", "https://schema.org/Dataset");
63+
maxAuthors = spJsonSetting.getInt("maxAuthors", 5);
64+
maxItems = spJsonSetting.getInt("maxItems", 5);
7165
}
7266

7367
/**
7468
* Get identifier schema for each author
7569
*
76-
* Author may have identifiers from different providers
77-
* ORCID: the url format is https://orcid.org/:id
78-
* ISNI: the url format is https://isni.org/isni/:id
79-
* ScopusID: the url format is https://www.scopus.com/authid/detail.uri?authorId=:id
80-
* VIAF: the url format is http://viaf.org/viaf/:id
81-
*
8270
* For example:
83-
* if author has VIAF
84-
* Link: <http://viaf.org/viaf/:id/>; rel="author"
71+
* if author has VIAF
72+
* Link: <http://viaf.org/viaf/:id/>; rel="author"
8573
*
86-
* @param datasetAuthors
87-
* @return
74+
* @param datasetAuthors list of all DatasetAuthor object
75+
* @return all the non empty author links in a string
8876
*/
8977
private String getIdentifierSchema(List<DatasetAuthor> datasetAuthors) {
90-
StringBuilder sb = new StringBuilder();
78+
String singleAuthorString;
9179
String identifierSchema = "";
9280

93-
for (DatasetAuthor da:datasetAuthors){
94-
if (da.getIdValue() != null && !da.getIdValue().isEmpty()) {
95-
sb.append("<").
96-
append(idschemaJsonObj.getString(da.getIdType())).
97-
append(da.getIdValue()).
98-
append(">;rel=\"author\"");
99-
if (identifierSchema == "") {
100-
identifierSchema = sb.toString();
81+
for (DatasetAuthor da : datasetAuthors) {
82+
logger.info(String.format(
83+
"idtype: %s; idvalue: %s, affiliation: %s; identifierUrl: %s",
84+
da.getIdType(),
85+
da.getIdValue(),
86+
da.getAffiliation(),
87+
da.getIdentifierAsUrl()
88+
));
89+
if (da.getIdentifierAsUrl() != null && !da.getIdentifierAsUrl().trim().isEmpty()) {
90+
singleAuthorString = "<" + da.getIdentifierAsUrl() + ">;rel=\"author\"";
91+
if (Objects.equals(identifierSchema, "")) {
92+
identifierSchema = singleAuthorString;
10193
} else {
102-
identifierSchema = String.join(",", identifierSchema, sb.toString());
94+
identifierSchema = String.join(",", identifierSchema, singleAuthorString);
10395
}
10496
}
10597
}
98+
99+
logger.info(String.format("identifierSchema: %s", identifierSchema));
106100
return identifierSchema;
107101
}
108102

@@ -111,7 +105,7 @@ private String getIdentifierSchema(List<DatasetAuthor> datasetAuthors) {
111105
*
112106
* @return comma delimited string
113107
*/
114-
public String getLinks(){
108+
public String getLinks() {
115109
List<String> valueList = new LinkedList<>();
116110
Dataset ds = workingDatasetVersion.getDataset();
117111

@@ -125,8 +119,8 @@ public String getLinks(){
125119
valueList.add(citeAs);
126120
}
127121

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

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

154-
private JsonArrayBuilder getIdentifiersSchema(List<DatasetAuthor> datasetAuthors){
148+
private JsonArrayBuilder getIdentifiersSchema(List<DatasetAuthor> datasetAuthors) {
149+
if (datasetAuthors.size() > maxAuthors) return null;
155150
JsonArrayBuilder authors = Json.createArrayBuilder();
156-
for (DatasetAuthor da:datasetAuthors){
157-
if (da.getIdValue() != null && !da.getIdValue().isEmpty()) {
158-
authors.add(jsonObjectBuilder().add("href", idschemaJsonObj.getString(da.getIdType()) + da.getIdValue()));
151+
boolean returnNull = true;
152+
for (DatasetAuthor da : datasetAuthors) {
153+
if (da.getIdentifierAsUrl() != null && !da.getIdentifierAsUrl().trim().isEmpty()) {
154+
authors.add(jsonObjectBuilder().add("href", da.getIdentifierAsUrl()));
155+
returnNull = false;
159156
}
160157
}
161-
return authors;
158+
return returnNull ? null : authors;
159+
}
160+
161+
private JsonArrayBuilder getJsonItems(List<FileMetadata> fms) {
162+
if (fms.size() > maxItems) return null;
163+
JsonArrayBuilder items = Json.createArrayBuilder();
164+
for (FileMetadata fm : fms) {
165+
DataFile df = fm.getDataFile();
166+
items.add(jsonObjectBuilder().add("href", getPublicDownloadUrl(df)).add("type", df.getContentType()));
167+
}
168+
169+
return items;
162170
}
163171

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

169-
JsonArrayBuilder items = Json.createArrayBuilder();
170-
171177
List<FileMetadata> fms = workingDatasetVersion.getFileMetadatas();
172-
for (FileMetadata fm:fms){
173-
DataFile df = fm.getDataFile();
174-
items.add(jsonObjectBuilder().add("href", getPublicDownloadUrl(df)).add("type",df.getContentType()));
175-
}
178+
JsonArrayBuilder items = getJsonItems(fms);
176179

177180
String license = "";
178-
if (workingDatasetVersion.getTermsOfUseAndAccess().getLicense() == TermsOfUseAndAccess.License.CC0){
181+
if (workingDatasetVersion.getTermsOfUseAndAccess().getLicense() == TermsOfUseAndAccess.License.CC0) {
179182
license = licJsonObj.getString(TermsOfUseAndAccess.License.CC0.name());
180183
}
181184

182185
JsonArrayBuilder mediaTypes = Json.createArrayBuilder();
183186
mediaTypes.add(
184187
jsonObjectBuilder().add(
185188
"href",
186-
citeAsJsonObj.getJsonObject(ds.getProtocol() + ds.getAuthority() + "/" + ds.getIdentifier())
189+
describedByJsonObj.getString(ds.getProtocol()) + ds.getAuthority() + "/"
190+
+ ds.getIdentifier()
187191
).add(
188192
"type",
189-
citeAsJsonObj.getString("type")
193+
describedByJsonObj.getString("type")
190194
)
191195
);
192196

@@ -199,35 +203,45 @@ public JsonArrayBuilder getJsonLinkset() {
199203
"application/json+ld"
200204
)
201205
);
202-
JsonArrayBuilder linkset = Json.createArrayBuilder();
206+
JsonArrayBuilder linksetJsonObj = Json.createArrayBuilder();
203207
JsonObjectBuilder mandatory = jsonObjectBuilder()
204208
.add("anchor", landingPage)
205209
.add("cite-as", Json.createArrayBuilder().add(jsonObjectBuilder().add("href", ds.getPersistentURL())))
206-
.add("type", Json.createArrayBuilder().add(jsonObjectBuilder().add("href", "https://schema.org/AboutPage")))
207-
.add("author", authors)
208-
.add("license", jsonObjectBuilder().add("href", license))
209-
.add("item", items).add("describedby", mediaTypes);
210-
linkset.add(mandatory);
210+
.add("type", Json.createArrayBuilder().add(jsonObjectBuilder().add("href", "https://schema.org/AboutPage")));
211+
212+
if (authors != null) {
213+
mandatory.add("author", authors);
214+
}
215+
if (license != null && !license.trim().isEmpty()) {
216+
mandatory.add("license", jsonObjectBuilder().add("href", license));
217+
}
218+
if (!mediaTypes.toString().trim().isEmpty()) {
219+
mandatory.add("describedby", mediaTypes);
220+
}
221+
if (items != null) {
222+
mandatory.add("item", items);
223+
}
224+
linksetJsonObj.add(mandatory);
211225

212226
if (useDefaultFileType) {
213-
for (FileMetadata fm:fms){
227+
for (FileMetadata fm : fms) {
214228
DataFile df = fm.getDataFile();
215229
JsonObjectBuilder itemAnchor = jsonObjectBuilder().add("anchor", getPublicDownloadUrl(df));
216230
itemAnchor.add("collection", Json.createArrayBuilder().add(jsonObjectBuilder()
217231
.add("href", landingPage)).add(jsonObjectBuilder().add("type", defaultFileTypeValue)));
218-
linkset.add(itemAnchor);
232+
linksetJsonObj.add(itemAnchor);
219233
}
220234
} else {
221-
for (FileMetadata fm:fms){
235+
for (FileMetadata fm : fms) {
222236
DataFile df = fm.getDataFile();
223237
JsonObjectBuilder itemAnchor = jsonObjectBuilder().add("anchor", getPublicDownloadUrl(df));
224238
itemAnchor.add("collection", Json.createArrayBuilder().add(jsonObjectBuilder()
225239
.add("href", landingPage)));
226-
linkset.add(itemAnchor);
240+
linksetJsonObj.add(itemAnchor);
227241
}
228242
}
229243

230-
return linkset;
244+
return linksetJsonObj;
231245
}
232246

233247

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

242256
if (storageIO instanceof SwiftAccessIO) {
243-
String fileDownloadUrl = null;
257+
String fileDownloadUrl;
244258
SwiftAccessIO<DataFile> swiftIO = (SwiftAccessIO<DataFile>) storageIO;
245259
try {
246260
swiftIO.open();
@@ -249,7 +263,7 @@ private String getPublicDownloadUrl(DataFile dataFile) {
249263
}
250264

251265
//if its a public install, lets just give users the permanent URL!
252-
if (systemConfig.isPublicInstall()){
266+
if (systemConfig.isPublicInstall()) {
253267
fileDownloadUrl = swiftIO.getRemoteUrl();
254268
} else {
255269
//TODO: if a user has access to this file, they should be given the swift url

src/main/java/propertyFiles/Bundle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2570,4 +2570,4 @@ publishDatasetCommand.pidNotReserved=Cannot publish dataset because its persiste
25702570
api.errors.invalidApiToken=Invalid API token.
25712571

25722572
# Signposting configuration
2573-
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"}
2573+
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}

src/main/webapp/dataset.xhtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
<o:importFunctions type="edu.harvard.iq.dataverse.util.MarkupChecker" />
8282
<f:metadata>
8383
<!-- Add Signposting-->
84-
<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('link', DatasetPage.getSignpostingLinkHeader())}" />
84+
<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('Link', DatasetPage.getSignpostingLinkHeader())}" />
8585
<!-- End add Signposting-->
8686
<f:viewParam name="id" value="#{DatasetPage.id}"/>
8787
<o:viewParam name="ownerId" value="#{DatasetPage.ownerId}"/>

0 commit comments

Comments
 (0)