Skip to content

Commit

Permalink
MIR-1361 Allow to enter ROR as affiliation for authors (#1074)
Browse files Browse the repository at this point in the history
* Display ror id as of https://ror.readme.io/docs/display
* Added id attribute to ror input
* Added ror-search.js
* Enable search in ROR registry
  • Loading branch information
Possommi authored Nov 18, 2024
1 parent 7490930 commit d23b3a7
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ $grayDark: #ECF0F1 !default;
height:200px;
}

.mir-ror-logo {
height: 20px;
}
.mir-toc-sections {
list-style-type: none;
padding-left: 0;
Expand Down
1 change: 1 addition & 0 deletions mir-module/GruntFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ module.exports = function(grunt) {
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/relatedItem-modal.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/relatedItem-modal.js',
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/relatedItem-autocomplete.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/relatedItem-autocomplete.js',
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/xeditor-form.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/xeditor-form.js',
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/ror-search.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/ror-search.js',
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/openaire.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/openaire.js',
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/classification-modal-select.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/classification-modal-select.js',
'<%= globalConfig.moduleDirectory %>/target/classes/META-INF/resources/js/mir/geo-coords.min.js': '<%= globalConfig.moduleDirectory %>/src/main/resources/META-INF/resources/js/mir/geo-coords.js',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,8 @@
</xed:template>

<xed:template id="person.affiliation">
<mir:textfield repeat="true" xpath="mods:affiliation" help-text="{i18n:mir.help.affiliation}" label="mir.affiliation" />
<mir:textfield repeat="true" xpath="mods:affiliation[not(@authorityURI)]" help-text="{i18n:mir.help.affiliation}" label="mir.affiliation" />
<mir:textfield repeat="true" xpath="mods:affiliation[@authorityURI='https://ror.org/']" help-text="{i18n:mir.help.affiliation.ror}" label="mir.affiliation.ror" id="mir-ror-input"/>
</xed:template>

<xed:template id="nameIdentifier.repeated">
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
148 changes: 148 additions & 0 deletions mir-module/src/main/resources/META-INF/resources/js/mir/ror-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const RORSearch = {
baseURL: "https://api.ror.org/",

search: async function (text) {
const q = await fetch(RORSearch.baseURL + "organizations?query=" + encodeURIComponent(text));
let json = await q.json();

let rorIdentifiers = [];

json.items.forEach((item) => {
item.label = item.name + " (" + item.id + ")";
rorIdentifiers.push(item);
});

rorIdentifiers.sort((a, b) => {
if (a.label < b.label) {
return -1;
}
if (a.label > b.label) {
return 1;
}
return 0;
});
return rorIdentifiers;
},

/**
* Find the option (if any) matching the given label.
*
* @param dataListId the id of the datalist
* @param label the label to find in the given datalist
*
* @return the option element with the givel label or null
* */
getOption: function (dataListId, label) {
let dataList = document.getElementById(dataListId);

for (const option of dataList.childNodes) {
if (option.value === label) {
return option;
}
}

return null;
},

/**
* Searches ROR for the given text at https://api.ror.org/ .
* */
onInput: async function (event) {
let input = event.target;
let dataListId = input.getAttribute("list");

let option = RORSearch.getOption(dataListId, input.value);

// User selects value from list
if (option != null) {
input.value = option.getAttribute("data-ror");
input.title = option.getAttribute("value")
event.preventDefault();
return;
}

// Selected value is not in list, actually execute search at ROR
let result = await RORSearch.search(input.value);
let dataList = document.getElementById(dataListId);
dataList.textContent = "";

// Update datalist with results from ROR
for (const suggestion of result) {
let option = document.createElement("option");
option.setAttribute("value", suggestion.label);
option.setAttribute("data-ror", suggestion.id);
dataList.appendChild(option);
}
},

debounce: (f, wait) => {
let timeoutId = null;

return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
f.apply(null, args);
}, wait);
};
},

/**
* Resolve ror links in xEditor and update title attributes of ror inputs.
* */
updateXEditorInputTitle: async function () {
let rorInputs = document.querySelectorAll("[id='mir-ror-input']");
for (const rorInput of rorInputs) {
if (rorInput.value.length == 0) {
continue;
}

RORSearch.search(rorInput.value).then(data => {
if (data.length > 0) {
rorInput.setAttribute("title", data[0].name + " (" + data[0].id + ")");
}
});
}
},

/**
* Resolve ror links on metadata page.
* */
resolve: async function () {
let rorLinks = document.querySelectorAll("a.mir-ror-link");
for (const rorLink of rorLinks) {
RORSearch.search(rorLink.href).then(data => {
if (data.length > 0) {
if (data[0].name.length > 25) {
rorLink.innerHTML = data[0].name.substring(0, 25).trim() + " …";
} else {
rorLink.innerHTML = data[0].name;
}
rorLink.setAttribute("title", data[0].name + " | " + data[0].id);
}
});
}
}
};

/**
* Adds event handler to every ror input element.
* */
document.addEventListener('DOMContentLoaded', function () {
const debouncedInputHandler = RORSearch.debounce(RORSearch.onInput, 500);

for (const input of document.querySelectorAll('[id="mir-ror-input"]')) {
// create datalist element with random uuid as element id
let dataList = document.createElement("datalist");
dataList.id = self.crypto.randomUUID();

// set the previously created datalist as list to the ror input element
input.setAttribute("list", dataList.id);
input.parentElement.append(dataList);

// add the actual event handler
input.addEventListener('input', debouncedInputHandler);
}

RORSearch.resolve();
RORSearch.updateXEditorInputTitle();
});
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ mir.actions.noaccess = Um das Dokument zu bearbeiten,
mir.actions.norights = Sie besitzen nicht die ben\u00F6tigten Rechte, um das Dokument zu bearbeiten.
mir.admineditor = Bearbeiten im Admin-Editor
mir.affiliation = Zugeh\u00F6rigkeit
mir.affiliation.ror = Zugeh\u00F6rigkeit (ROR ID)
mir.articlenumber = Artikelnummer:
mir.articlenumber.short = Art.-Nr.:
mir.breadcrumb.home = Start
Expand Down Expand Up @@ -384,6 +385,7 @@ mir.help.abstract.language = Geben Sie die Sprache an, in d
mir.help.abstract.ortext = Eine kurze Zusammenfassung des Dokumentinhalts.
mir.help.access = Geben Sie hier an, wer auf die angehangenen Dokumente zugreifen darf.
mir.help.affiliation = Organisation bzw. Institution der betreffenden Person.
mir.help.affiliation.ror = <a href="https://ror.org/">ROR ID</a> der Organisation bzw. Institution der betreffenden Person.
mir.help.articlenumber = Die Nummer eines Artikels, der in einer Online-Zeitschrift oder auf einer Publikationsplattform erschienen ist.
mir.help.artist.photographer = K\u00FCnstler des Werkes oder Fotograf des Bildes.
mir.help.authorSpecification = Genauere Beschreibung der Autor(innen)-Rolle, meist relevant f\u00C3\u00BCr Kontaktaufnahme oder statistische Auswertungen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ mir.actions.noaccess = To edit the document, please lo
mir.actions.norights = You don't have the rights to edit the document.
mir.admineditor = Edit in admin editor
mir.affiliation = Affiliation
mir.affiliation.ror = Affiliation (ROR ID)
mir.articlenumber = Article number:
mir.articlenumber.short = Art.-No.:
mir.breadcrumb.home = Home
Expand Down Expand Up @@ -362,6 +363,7 @@ mir.help.abstract.aslink = Weblink to abstract.
mir.help.abstract.ortext = A short summary of the document content.
mir.help.access = Specify who should be able to access the dataset after publication. The metadata will be public in any case.
mir.help.affiliation = Organisation/Affiliation of the Person.
mir.help.affiliation.ror = <a href="https://ror.org/">ROR ID</a> of the Organisation/Affiliation of the Person.
mir.help.articlenumber = The number of an article published in an online journal or publication platform.
mir.help.artist.photographer = Creator of the Picture/Image.
mir.help.author.interviewee = A person, family or organization responsible for creating or contributing to a resource by responding to an interviewer, usually a reporter, pollster, or some other information gathering agent.
Expand Down
6 changes: 6 additions & 0 deletions mir-module/src/main/resources/xsl/editor/mods2xeditor.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@
</xsl:copy>
</xsl:template>

<xsl:template match="mods:affiliation[@authorityURI='https://ror.org/'][@valueURI]">
<mods:affiliation authorityURI="{@authorityURI}">
<xsl:value-of select="@valueURI"/>
</mods:affiliation>
</xsl:template>

<!-- Remove this mods:classification entry, will be created again while saving using mods:accessCondtition (see MIR-161) -->
<xsl:template match="mods:classification[@authority='accessRestriction']">
<!-- do nothing -->
Expand Down
4 changes: 4 additions & 0 deletions mir-module/src/main/resources/xsl/editor/xeditor2mods.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@
</mods:nameIdentifier>
</xsl:template>

<xsl:template match="mods:affiliation[@authorityURI='https://ror.org/']">
<mods:affiliation authorityURI="{@authorityURI}" valueURI="{.}"/>
</xsl:template>

<!-- Copy content of mods:accessCondtition to mods:classification to enable classification support (see MIR-161) -->
<xsl:template match="mods:accessCondition[@type='restriction on access'][contains(@xlink:href,'mir_access')]">
<mods:accessCondition type="restriction on access">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@
<script src="{$WebApplicationBaseURL}js/mir/sherpa.js"></script>
<script src="{$WebApplicationBaseURL}modules/webtools/upload/js/upload-api.js"></script>
<script src="{$WebApplicationBaseURL}modules/webtools/upload/js/upload-gui.js"></script>
<script src="{$WebApplicationBaseURL}js/mir/ror-search.min.js"/>
<link rel="stylesheet" type="text/css" href="{$WebApplicationBaseURL}modules/webtools/upload/css/upload-gui.css" />
</xsl:template>

Expand Down
15 changes: 14 additions & 1 deletion mir-module/src/main/resources/xsl/mir-mods-utils.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
</xsl:variable>

<xsl:variable name="affiliation" select="mods:affiliation/text()" />
<xsl:variable name="affiliation-ror" select="mods:affiliation[contains(@authorityURI, '//ror.org')]" />
<xsl:variable name="personNodeId" select="generate-id(.)"/>
<xsl:variable name="personName"><xsl:apply-templates select="." mode="nameString"/></xsl:variable>
<xsl:if test="count($nameIdentifiers) &gt; 0 or string-length($affiliation) &gt; 0">
Expand All @@ -71,12 +72,24 @@
</dd>
</xsl:for-each>
</xsl:if>
<xsl:if test="string-length($affiliation) &gt; 0">
<xsl:if test="string-length($affiliation) &gt; 0 or $affiliation-ror">
<dt>
<xsl:value-of select="i18n:translate('mir.affiliation')"/>
</dt>
<dd>
<xsl:value-of select="$affiliation"/>
<xsl:for-each select="mods:affiliation[contains(@authorityURI, '//ror.org')][@valueURI]">
<xsl:sort select="@valueURI"/>
<div class="mir-affiliation-ror">
<a href="https://ror.org/" alt="ROR logo">
<img src="{$WebApplicationBaseURL}images/ror/ror-icon-rgb-transparent.svg"
class="pr-1 mir-ror-logo"/>
</a>
<a class="mir-ror-link" href="{@valueURI}">
<xsl:value-of select="@valueURI"/>
</a>
</div>
</xsl:for-each>
</dd>
</xsl:if>
</dl>
Expand Down

0 comments on commit d23b3a7

Please sign in to comment.