*Deprecated* See https://wiki.duraspace.org/display/VIVODOC/All+Documentation for current documentation

Create a Freemarker directive called DataView. The DataView provides a powerful means of customizing the user interface.

Clarify goals: move presentation logic out of the business layer

Add mention of DataGetters hanging on templates

Add a Plan section after Goals and before Specification

With Custom List View, explain how it will differ, and how the logic is moving out of the back end. Talk about collating, sorting on a field

How does a Faux Property fit in here? Need to at least mention it

Allow byClassOf and byProperty to be optional (can we use both?), to take the place of Page DataGetters. Add that to the Goals and Plan sections.

Simplify goals.

Goals

The DataView directive is an attempt to add power to the user interface. Keeping the ability to use RDF to determine the appearance of the application, but also giving more control to the template writer. Removing redundant customization mechanisms, with the ensuing simplification of the code base. Providing easy integration of AJAX with Freemarker. By creating the ability to do custom list views in the presentation layer, allow us to remove complicated presentation logic from the controllers.

  • Maintain and extend the powerful customization capabilities in VIVO.
    • Some techniques may be transformed, but none will be lost.
  • Simplify the user interface code
    • Merge different customization tools into one mechanism.
      • Individual page templates, customized by the class of the individual.
      • Short views for search results, index pages and browse pages.
      • Custom list views.
  • Add clarity to the Freemarker templates
    • Move the display logic into the display layer.
    • Don't hide the customization in the Java code.
  • Add power to the user interface
    • Allow easy use of AJAX with Freemarker for more responsive page loading.
  • Lay the groundwork for GUI editing of the display customizations

Specification

Syntax

<@DataView setting="Sort", byClassOf=individualUri, byProperty=property ajax values=hash>

Where:

settingrequiredThe name of the view type. This might be "Sort", "Browse", "ListView", etc. This will be used as the primary selection criterion. Must match exactly to the view spec.
byClassOfrequires exactly oneA 2nd selection criterion. Provide either a String containing the URI of the individual in question, or an IndividualTemplateModel representing that individual. The class of the individual, or a parent class, must match the view.
byPropertyA 2nd (and possibly 3rd and 4th) selection criterion. If a String, then it is the URI of the property to match. If a PropertyTemplateModel, then the property URI must match, as well as the class of the subject (domain) and the class of the object (range).
ajaxoptionalIf present, this view will be satisfied by an AJAX call.
valuesoptionalA hash of values that will be added to the environment before the view template is rendered.

Behavior

The DataView directive searches the Display model for a DataView object. If found, the DataGetter associated with the DataView is executed, and the template associated with the DataView is rendered and included in the calling template.

If more than one eligible DataView object is found, the most specific DataView object is selected, according to the rules in View Selection.

If no eligible DataView object is found, no action is taken.

View Selection

byClassOf

The byClassOf parameter specifies the URI of an Individual. The directive searches for a view with the correct setting property, and with the most specific match in a restrictByClass property.

In this case, "most specific match" means searching for a view with a restrictByClass property that is equal to the most specific class of the specified individual. If no such view is found, and the most specific class of the individual has a parent class, the search is repeated using that parent class. This continues until a view is found, or until there is no parent class to search for.

If a matching view is found at any step in the search, it is selected. If more than one view is found on a particular step of the search, one of the matching views will be arbitrarily selected. If no matching view is found during the entire search sequence, no action is taken.

Table of search order

 

byProperty

The byProperty parameter specifies a PropertyTemplateObject. 

If the property is a data property, the directive searches for a view with the correct setting property, and the most specific match in a restrictByDomain property and a restrictByProperty property. In this case, most specific match means searching for an exact match, then searching for matches to the super-classes of the domain of the specified proeprty, and then repeating that sequence for matches to the super-properties of the specified property.

If the property is an object property, the directive searches includes a match for the restrictByRange property and its super-classes, before trying less specific matches for the domain and te property URI.

The search for a byProperty match must be optimized using some form of caching, since on an object property it could result in (d+1) x (p + 1) x (r + 1) queries, where

  • d is the number of super-classes of the domain class
  • p is the number of super-properties of the specified property
  • r is the number of super-classes of the range class

Table of search order


Data Getters

A DataView object may have zero or more DataGetter objects associated with it. When the DataView object is selected, the DataGetter objects will be instantiated and run, in an arbitrary order. When ordering is important, the multiple DataGetter objects should be specified within a single SequenceDataGetter object.

Templates

A DataView object may have zero or one freemarker template associated with it. If a DataView object has an associated freemarker template, that template will be rendered in the context of the DataView directive, after any associated DataGetters have been run. Absence of an associated template is not treated as an error.

Freemarker variables

After the directive <#DataView setting="foobar">, the variable dataView_foobar will be set to true if a suitable view was found, otherwise will be set to false. This allows a construct like this:

<@DataView setting="sort" byClass=individual />
<#if !dataView_sort>
  <!-- specify the default view -->
</#if>

An equivalent mechanism would be to specify the default view with a class of owl:Thing. That view would always be used if no other match was found.

AJAX

If "ajax" is specified in the directive, the selection process is performed as usual, but the process stops there. Instead of executing any associated DataGetter object and rendering the template, a JavaScript segment is created which will perform those steps asynchronously, after the page has been loaded.

The directive should have the same net effect, with one caveat: when the associated template is rendered, the Freemarker body map will appear as it did when the main page completed rendering. DataGetters executed by an asynchronous DataView directive have no effect on other DataView directives, whether synchronous or asynchronous. Variable assignments made by an asynchronous DataView directive also are not visible to other DataView directives.

This might not be in the first implementation.

Examples

The Individual Page

Let's look at how we select the correct template for a profile page.

Currently, the IndividualTemplateLocator class searches for the correct template to use when displaying an individual profile page. The data model contains these statements:

foaf:Person 
    vitro:customDisplayViewAnnot "individual--foaf-person.ftl"^^xsd:string .
foaf:Organization 
    vitro:customDisplayViewAnnot "individual--foaf-organization.ftl"^^xsd:string .
skos:Concept 
    vitro:customDisplayViewAnnot "individual--skos-concept.ftl"^^xsd:string .

Using DataView, we can accomplish the same thing without the IndividualTemplateLocator class. Here are two ways to do it.

  • In the first, the Freemarker template says what to do if we don't find a matching DataView.
  • In the second, the Freemarker template does not say what to do if we don't find a matching DataView. However, because we have a DataView restricted by Owl:Thing, that can never happen.

Explicit default

Use these statements in individual.ftl:

<@DataView setting="INDIVIDUAL_PROFILE" byClassOf=individualUri>
<#if !dataView_INDIVIDUAL_PROFILE>
  <#include "individual--default.ftl">
</#if>

and these statements in the data model

 display:individualProfileViewPerson
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass foaf:person ;
    display:hasTemplate "individual--foaf-person.ftl"^^xsd:string .
 display:individualProfileViewOrganization
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass foaf:organization ;
    display:hasTemplate "individual--foaf-organization.ftl"^^xsd:string .
 display:individualProfileViewConcept
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass skos:Concept ;
    display:hasTemplate "individual--skos-concept.ftl"^^xsd:string .

If the class of the individual does not match foaf:Personfoaf:Organization, or skos:Concept, the template explicitly says that we will render the individual using individual--default.ftl.

Implicit default

Use this statement in individual.ftl:

 <@DataView setting="INDIVIDUAL_PROFILE" byClassOf=individualUri>

and these statements in the data model

 display:individualProfileViewPerson
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass foaf:person ;
    display:hasTemplate "individual--foaf-person.ftl"^^xsd:string .
 display:individualProfileViewOrganization
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass foaf:organization ;
    display:hasTemplate "individual--foaf-organization.ftl"^^xsd:string .
 display:individualProfileViewConcept
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass skos:Concept ;
    display:hasTemplate "individual--skos-concept.ftl"^^xsd:string .
 display:individualProfileViewDefault
    a display:DataView;
    display:setting "INDIVIDUAL_PROFILE" ;
    display:restrictByClass owl:Thing ;
    display:hasTemplate "individual--default.ftl"^^xsd:string .

Here, there is no chance of not finding a match, because every individual will be an instance of owl:Thing.

Short view, Sort results.

When the current short view mechanism was implemented in release 1.5, it was with the assumption that it would be driven by the application ontology. Since the application ontology was not ready for release 1.5, a temporary driving mechanism was created.

The driver for short views is the FakeApplicationOntologyService class. This is quite limited, in the sense that it only recognizes a match to the most specific class of the Individual being displayed. So, if you want to configure a short view for foaf:Person and all of its sub-classes, you must do it explicitly with one statement for each such sub-class. Also, we should note that the FakeApplicationOntologyService code contains a hard-coded exception to its matching algorithm, in order to retain compatibility with earlier implementations. This exception is buried deep in the code, and would remain a mystery to the template author.

As an example, the short view in the search results is invoked this way, in search-pagedResults.ftl:

<@shortView uri=individual.uri viewContext="search" />

With the DataView directive, this becomes:

<@DataView setting="SEARCH_RESULT" byClassOf=individual.uri />

The short view is configured by a file called shortview_config.n3. For a custom short view in the sort results, the file might contain these structures:

vivo:FacultyMember 
 display:hasCustomView mydomain:facultySearchView .

mydomain:facultySearchView
 a display:customViewForIndividual ;
 display:appliesToContext "SEARCH" ;
 display:hasTemplate "view-search-faculty.ftl" ;
 display:hasDataGetter mydomain:facultyDepartmentDG .

mydomain:facultyDepartmentDG
 a datagetters:SparqlQueryDataGetter ;
 display:saveToVar "details" ;
 display:query """
 PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
 PREFIX vivo: <http://vivoweb.org/ontology/core#>
 SELECT ?deptName
 WHERE {
   ?individualUri vivo:hasMemberRole ?membership .
   ?membership vivo:roleContributesTo ?deptUri .
   ?deptUri 
     a vivo:AcademicDepartment ;
     rdfs:label ?deptName .
 }
 LIMIT 20 
 """ .

These statements define a customViewForIndividual, assign it to the SEARCH context, add a template and a data getter, and associate it with the class vivo:FacultyMember

With the DataView directive, these statements would change only trivially:

display:facultySearchView
    a display:DataView;
    display:setting "SEARCH_RESULT" ;
    display:restrictByClass vivo:FacultyMember ;
    display:hasTemplate "view-search-faculty.ftl" ;
    display:hasDataGetter mydomain:facultyDepartmentDG .

mydomain:facultyDepartmentDG
    ... unchanged ... 

A primary distinction between <@shortview> and <@DataView> is the behavior when no view is found. In this case, 

  • If no matching view is found, <@shortview> renders a default template, whose name is embedded somewhere in the Java code.
  • If no matching view is configured, <@DataView> takes no action beyond setting a Freemarker variable. This allows the template author to test for a matching view, and to specify explicitly what the default action should be (as illustrated in the previous example).

Custom list view. Something simple?

 

 

TBD


 

 

Editing

TBD

Assign labels to the various DataGetter classes, etc., to help with the GUI.

 

Implementation

TBD

Questions

  • Terminology: is DataView the best name? What about the names of the parameters?
  • What algorithm is used for matching if a PropertyTemplateModel is provided?
    • If the class of the subject is foaf:Person, the property is vivo:teaches, and the class of the object is vivo:Class, which of these views would be preferred?
      • domain=owl:thing, prop=vivo:teaches, range=vivo:Class
      • domain=foaf:Person, prop=isf:relates_to, range=vivo:Class
      • domain=foaf:Person, prop=vivo:teaches, range=owl:thing
    • We want the most specific match, but which is more important to match specifically?
  • How can we use the values parameter to add values to the environment, and then not have those values persist?
  • How can we make a deep copy of the body map, for use by asynchronous directives? Or do we want to? In particulare, should the "pullProperty" method affect other asynchronous DataViews? Are there any ways in which the body map is mutable? Remember that this must be thread-safe.
  • How can the most-specific matching be made sufficiently responsive? Perhaps we read all of the DataViews from the display model into a memory cache, and search against that cache?
  • Do we want to make the successful matching criteria available to the template? Does it care whether it matched on foaf:Person or one of it's super-classes?
  • No labels