Date: Thu, 28 Mar 2024 16:41:25 -0400 (EDT) Message-ID: <486923656.28906.1711658485574@lyrasis1-roc-mp1> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_28905_479749823.1711658485573" ------=_Part_28905_479749823.1711658485573 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
Custom list views provide a way to expand the data that is displayed for= object and data properties. For example, with the default list view the ha= sPresenterRole object property would only display the rdfs:label of the rol= e individual; but with a custom list view, the "presentations" view include= s not only the role but also the title of the presentation, the name of the= conference where the presentation was given and the date the presentation = was given. This wiki page provides guidelines for developing custom list vi= ews as well as an example of a custom list view.
A custom list view is associated with an object property in the RDF file=
s in the directory /vivo/rdf/display/everytime
. To register a =
list view, create a new .rdf
or .n3
fil=
e in that directory. The file must be well-formed RDF/XML or N3.
Here is an example of registering a new association in a file named
<htt= p://vivoweb.org/ontology/core#authorInAuthorship> <http://vitro.mannlib.cornell.edu/ontologies/display/1.1#listViewConfigF= ile> "listViewConfig-authorInAuthorship.xml" .
With this triple the authorInAuthorship
object propert=
y is associated with a list view configuration that is defined in a file na=
med listViewConfig-authorInAuthorship.xml
.
Place the N3 file containing this triple (or the well-formed RDF/XML fil=
e) in the /vivo/rdf/display/everytime
directory, redeploy VIVO=
and restart tomcat to put the new custom list view in effect.
Note: Faux property custom list views are not registered in the sam= e way. The list view is specified in the configuration of the faux property= itself, using the faux property editing form. See details in Create and edit faux pr= operties.
The list view configuration file requires three elements:
The list-view-config root element can also contain two optional elements= :
Because SPARQL queries with multiple OPTIONAL clauses are converted to h= ighly inefficient SQL by the Jena API, one or more construct queries should= be included to improve query performance. They are used to construct a mod= el significantly smaller than the entire dataset that the select quer= y can be run against with reasonable performance.
The construct queries themselves should not contain multiple OPTIONAL cl= auses, to prevent the same type of inefficiency. Instead, use multiple cons= truct queries to construct a model that includes all the necessary data.
In the absence of any construct queries, the select query is run against= the entire dataset. If your select query does not involve a lot of OPTIONA= L clauses, you do not need to include construct queries.
The construct queries must be designed to collect all the data that the = select query will request. They can be flexibly constructed to contain more= data than is currently selected, to allow for possible future expansion of= the SELECT and to simplify the WHERE clause. For example, one of the const= ruct queries for core:hasRole includes:
CONSTRU= CT {=20 ?role ?roleProperty ?roleValue .=20 ... } WHERE { ?role ?roleProperty ?roleValue .=20 ... }
That is, it includes all the properties of the role, rather than just th= ose currently selected by the select query.
The ordering of the construct queries is not significant.
Use a SELECT DISTINCT clause rather than a simple SELECT. There can stil= l be cases where the same individual is retrieved more than once, if there = are multiple solutions to the other assertions, but DISTINCT provides a sta= rt at uniqueness.
The WHERE clause must contain a statement ?subject ?property ?object, wi= th the variables ?subject and ?property named as such. For a default list v= iew, the ?object variable must also be named as such. For a custom list vie= w, the object can be given any name, but it must be included in the SELECT = terms retrieved by the query. This is the statement that will be edited fro= m the edit links.
Incomplete data can result in a missing linked individual or other criti= cal data (such as a URL or anchor text on a link object). When the user has= editing privileges on the page, these statements are displayed so that the= user can edit them and provide the missing data. They should be hidden fro= m non-editors. Follow these steps in the select query to ensure this behavi= or:
OPTIONA= L { ?authorship core:linkedInformationResource ?infoResource }
<cri= tical-data-required> FILTER ( bound(?infoResource) ) </critical-data-required>
The query should contain <collated> elements, which are used when = the property is collated. For uncollated queries, the fragments are removed= by a query preprocessor. Since any ontology property can be collated in th= e Ontology Editor, all queries should contain the following <collated>= ; elements:
SELECT = DISTINCT <collated> ?subclass </collated> ...
ORDER B= Y <collated> ?subclass </collated> ...
<col= lated> OPTIONAL { ?infoResource a ?subclass =20 ?subclass rdfs:subClassOf core:InformationResource . } =20 </collated>
Postprocessing removes all but the most specific subclass value from the= query result set.
Alternatively (and preferably):
<col= lated> OPTIONAL { ?infoResource vitro:mostSpecificType ?subclass = =20 ?subclass rdfs:subClassOf core:InformationResource . } =20 </collated>
Automatic postprocessing to filter out all but the most specific subclas= s will be removed in favor of this implementation in the future.
Both collated and uncollated versions of the query should be tested, sin= ce the collation value is user-configurable via the ontology editor.
To retrieve a datetime interval, use the following fragment, substitutin= g the appropriate variable for ?edTraining:
OPTIONA= L {=20 ?edTraining core:dateTimeInterval ?dateTimeInterval OPTIONAL { ?dateTimeInterval core:start ?dateTimeStartValue . = =20 ?dateTimeStartValue core:dateTime ?dateTimeStart=20 } OPTIONAL { ?dateTimeInterval core:end ?dateTimeEndValue . =20 ?dateTimeEndValue core:dateTime ?dateTimeEnd=20 } =20 }
The variables ?dateTimeStart and ?dateTimeEnd are included in the SELECT= clause.
Many properties that retrieve dates order by end datetime descending (mo= st recent first). In this case, a post-processor must apply to sort null va= lues at the top rather than the bottom of the list, which is the ordering r= eturned by the SPARQL ORDER BY clause in the case of nulls in a descending = order. In that case, the variable names must be exactly as shown to allow t= he post-processor to do its work.
To ensure that values set in the template on one iteration do not bleed = into the next statement:
To allow for a missing linked individual, the template should include co= de such as:
<#lo= cal linkedIndividual> <#if statement.org??> <a href=3D"${url(statement.org)}">${statement.orgName}</a&= gt; <#else> <#-- This shouldn't happen, but we must provide for it --> <a href=3D"${url(statement.edTraining)}">${statement.edTraini= ngName}</a> (no linked organization) </#if> </#local>
The query must have been constructed to return orgName (see above under = "General select query requirements"), or alternatively the template can use= the localname function: ${localname(org)}.
If a variable is in an OPTIONAL clause in the query, the display of the = value in the template should include the default value operator ! to preven= t an error on null values.
This example will walk through the custom list view for the core:researc= hAreaOf object property. This property is displayed on the profile pa= ge for research area individuals.
In this example we're using RDF/XML to associate the researchAreaOf obje= ct property (line 1) with a specific list view configuration (line 2):
<rdf= :Description rdf:about=3D"http://vivoweb.org/ontology/core#researchAreaOf"&= gt; <display:listViewConfigFile rdf:datatype=3D"http://www.w3.org/2001/X= MLSchema#string">listViewConfig-researchAreaOf.xml</display:listViewC= onfigFile> </rdf:Description>
The root <list-view-config> element in our listViewConfig-research= AreaOf.xml file contains the required <query-select> and <template= > elements as well as two optional <query-construct> sections and = an optional <postprocessor> element.
This is the <query-select> element:
<= ;query-select> =20 PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#> PREFIX core: <http://vivoweb.org/ontology/core#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>= =20 PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7= #>=20 PREFIX foaf: <http://xmlns.com/foaf/0.1/> =20 SELECT DISTINCT ?person=20 ?personName ?posnLabel ?orgLabel ?type ?personType ?title=20 WHERE {=20 ?subject ?property ?person . ?person core:personInPosition ?position . OPTIONAL { ?person rdfs:label ?personName }=20 OPTIONAL { ?person core:preferredTitle ?title } OPTIONAL { ?person vitro:mostSpecificType ?personType . ?personType rdfs:subClassOf foaf:Person } OPTIONAL { ?position rdfs:label ?posnLabel } OPTIONAL { ?position core:positionInOrganization ?org . ?org rdfs:label ?orgLabel=20 } OPTIONAL { ?position core:hrJobTitle ?hrJobTitle } OPTIONAL { ?position core:rank ?rank } }=20 ORDER BY ?personName ?type </query-select>
Here is the first <query-construct> element:
<= ;query-construct> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> = =20 PREFIX core: <http://vivoweb.org/ontology/core#> =20 CONSTRUCT { ?subject ?property ?person . ?person core:personInPosition ?position . ?position rdfs:label ?positionLabel . ?position core:positionInOrganization ?org . ?org rdfs:label ?orgName . ?position core:hrJobTitle ?hrJobTitle } WHERE { {=20 ?subject ?property ?person=20 } UNION { =20 ?subject ?property ?person . ?person core:personInPosition ?position =20 } UNION { =20 ?subject ?property ?person . ?person core:personInPosition ?position . ?position rdfs:label ?positionLabel=20 } UNION { =20 ?subject ?property ?person . ?person core:personInPosition ?position . ?position core:positionInOrganization ?org } UNION { =20 ?subject ?property ?person . ?person core:personInPosition ?position . ?position core:positionInOrganization ?org . ?org rdfs:label ?orgName } UNION { =20 ?subject ?property ?person . ?person core:personInPosition ?position . ?position core:hrJobTitle ?hrJobTitle } } </query-construct>
The second <query-construct> element:
<= ;query-construct> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> = =20 PREFIX core: <http://vivoweb.org/ontology/core#> PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7= #> =20 CONSTRUCT { ?subject ?property ?person . ?person rdfs:label ?label . ?person core:preferredTitle ?title . ?person vitro:mostSpecificType ?personType . ?personType rdfs:subClassOf foaf:Person=20 } WHERE { {=20 ?subject ?property ?person=20 } UNION { =20 ?subject ?property ?person . ?person rdfs:label ?label =20 } UNION { =20 ?subject ?property ?person . ?person core:preferredTitle ?title=20 } UNION { ?subject ?property ?person . ?person vitro:mostSpecificType ?personType . ?personType rdfs:subClassOf foaf:Person }=20 } </query-construct>
Next is the required <template> element:
<tem= plate>propStatement-researchAreaOf.ftl</template>
And here is the <postprocessor> element:
<pos= tprocessor>edu.cornell.mannlib.vitro.webapp.web.templatemodels.individua= l.ResearchAreaOfPostProcessor</postprocessor>
Note: the <postprocessor> is included here only to show the syntax= . The actual listViewConfig-researchAreaOf.xml file in the VIVO code base d= oes not use a custom post-processor.
Finally, here are the contents of our Freemarker template, propStatement= -researchAreaOf.ftl.
<#im= port "lib-sequence.ftl" as s> <@showResearchers statement /> <#-- Use a macro to keep variable assignments local; otherwise the value= s carry over to the next statement --> <#macro showResearchers statement> <#local linkedIndividual> <a href=3D"${profileUrl(statement.uri("person"))}" title=3D"${i1= 8n().person_name}">${statement.personName}</a> </#local> <#if statement.title?has_content > <#local posnTitle =3D statement.title> <#else> <#local posnTitle =3D statement.posnLabel!statement.personType&g= t; </#if> <@s.join [ linkedIndividual, posnTitle, statement.orgLabel!"" ] />= ; ${statement.type!} </#macro>