Stylesheets
Built-in XSLT and CSS stylesheets
XSLT
XSLT is a functional, Turing-complete XML transformation language.
LinkedDataHub's XSLT 3.0 stylesheets work by transforming RDF/XML response body from the underlying HTTP API. Additional metadata from RDF vocabularies is used to improve user experience.
Plain RDF/XML
RDF/XML is an important RDF syntax which functions as a bridge to the XML technology stack. The stylesheets use Jena's "plain" RDF/XML output which groups statements by subject and does not nest resource descriptions. This allows for predictable XPath patterns:
/rdf:RDF
— represents the RDF graph/rdf:RDF/rdf:Description
or/rdf:RDF/*[*][@rdf:about] | /rdf:RDF/*[*][@rdf:nodeID]
— resource description which contains properties/rdf:RDF/rdf:Description/@rdf:about
— subject resource URI/rdf:RDF/rdf:Description/@rdf:nodeID
— subject blank node ID/rdf:RDF/rdf:Description/*
predicate (e.g.rdf:type
) which URI isconcat(namespace-uri(), local-name())
/rdf:RDF/rdf:Description/*/@rdf:resource
— object resource/rdf:RDF/rdf:Description/*/@rdf:nodeID
— object blank node ID/rdf:RDF/rdf:Description/*/text()
— literal value
Stylesheets
XSLT stylesheet components used by LinkedDataHub:
- Includes
<xsl:include>
is used to inclkude one stylesheet into another. The import mechanism is specified in 3.10.2 Stylesheet Inclusion the XSLT 3.0 specification. The templates from the included stylesheets have the same priority as those of the importing stylesheet.- Imports
<xsl:import>
is used to import one stylesheet into another. The import mechanism is specified in 3.10.3 Stylesheet Import the XSLT 3.0 specification. The templates from the imported stylesheets have lower priority than those of the importing stylesheet.- Parameters
- XSD-typed global parameters passed to the stylesheet
- Keys
- Lookup keys
- Templates
- Template rules for XML node processing
One XSLT stylesheet can be specified per application. In order to reuse LinkedDataHub's built-in templates, it should import the system stylesheet layout.xsl and only override the necessary templates. That is however not a requirement, the stylesheet could also use its own independent transformation logic.
If there is no stylesheet specified for the application, the system stylesheet is used. It defines the overall layout and imports resource-level and container-specific stylesheets, as well as per-vocabulary stylesheets.
Note that LinkedDataHub itself imports stylesheets from Web-Client, which uses the same template modes but produces a much simpler layout.
There is also a special client-side stylesheet which is not used to render a full layout, but only manipulate DOM elements in the browser in response to user or system events. It is processed using Saxon-JS which provides IXSL (client-side extensions for XSLT). It imports and reuses some of the same sub-stylesheets as the server-side system stylesheet does, but avoids loading per-vocabulary stylesheets in order to improve page load time. Templates of the client-side stylesheet can also be overridden.
Namespaces
Prefix | Namespace | Vocabulary | Description |
---|---|---|---|
rdf: |
http://www.w3.org/1999/02/22-rdf-syntax-ns# |
The RDF Concepts Vocabulary | Namespace for the RDF/XML elements, mostly used for matching input data |
srx: |
http://www.w3.org/2005/sparql-results# |
Namespace for the SPARQL Query Results XML elements, mostly used for matching input data | |
xsl: |
http://www.w3.org/1999/XSL/Transform |
Namespace for the XSLT stylesheet elements | |
ixsl: |
http://saxonica.com/ns/interactiveXSLT |
Namespace for the Interactive XSL extensions | |
bs2: |
http://graphity.org/xsl/bootstrap/2.3.2 |
XSLT-only namespace that is used for Bootstrap 2.3.2-based layout templates | |
xhtml: |
http://www.w3.org/2011/http# |
XSLT-only namespace that is used for generic (X)HTML templates | |
ldt: |
https://www.w3.org/ns/ldt# |
Linked Data Templates | LDT processing-related concepts |
ac: |
https://w3id.org/atomgraph/client# |
Web-Client vocabulary | Client-side concepts |
lapp: |
https://w3id.org/atomgraph/linkeddatahub/apps/domain# |
AtomGraph Platform | LDT application concepts |
lacl: |
https://w3id.org/atomgraph/linkeddatahub/admin/acl/domain# |
LinkedDataHub ACL ontology | ACL concepts |
Parameters
Both global (i.e. stylesheet-level) and template parameters are declared using <xsl:param>
. For example:
<xsl:param name="ac:uri" as="xs:anyURI"/>
This parameter can be accessed using $ac:uri
. For example:
<xsl:if test="$ac:uri"> <xsl:value-of select="$ac:uri"/> </xsl:if>
LinkedDataHub sets these global parameters by default (the list is not exhaustive):
$ldt:base
- Base URI of the current application
$ldt:ontology
- The URI of the ontology of the current application
$ac:uri
- The absolute URI of the current document (without query string)
$ac:mode
- The URI of the current layout mode
$ac:forClass
- The URI of the class which instance is being created. See also
aplt:ForClass
. $ac:sitemap
- RDF/XML document of the application's ontology, with a transitive closure of imports
$lapp:Application
- RDF/XML document with the metadata of the current LDT application
$lacl:Agent
- RDF/XML document with the metadata of the currently authenticated agent (if any)
Templates
XSLT template components:
- Match
- XPath-based match pattern which either does or does not match an XML node
- Mode
- Allows to group templates and distinguish them from other groups which have the same match patterns (e.g. different layout modes)
- Parameters
- XSD-typed parameters passed to the template invocation
- Body
- Contains the XML output nodes as well as XSLT processing instructions
XSLT processing starts at the root of the RDF/XML document and produces HTML elements by applying templates on all of the RDF/XML nodes while moving down the XML tree. In other words, it starts at the graph level, moves down to resource description elements, then to property elements, and ends with identifier attributes and literal text nodes.
The content of the RDF/XML document normally includes the description of the requested document, and optionally a "thing"/concept paired with it. However, it can be completely customized by overriding the LDT template that matches that document and specifying a custom query. For example, for a document with a SKOS concept it might make sense to include its broader and/or narrower concepts in the RDF description as well. XML documents (which by definition includes RDF/XML) can also be loaded separately from the main RDF/XML document, but at a cost of an HTTP request per document. There is no rule on how to split data between the main RDF/XML document and the additional ones — it is a balancing act in terms of query maintainability and stylesheet performance.
Templates are applied (invoked) using <xsl:apply-templates>
. Mode can be specified, e.g. <xsl:apply-templates mode="bs2:Header">
. To stay in the current mode without explicitly specifying it, use <xsl:apply-templates mode="#current">
. <xsl:with-param>
is used to supply parameters.
LinkedDataHub provides the following default template modes, which are used to render the layout modes:
- Graph-level modes that apply to
rdf:RDF
bs2:Block
which renders full resource description with all propertiesbs2:BlockList
renders a list of resourcesxhtml:Table
renders a table with resources as rows and properties as columnsbs2:Grid
renders a gallery of thumbnailsbs2:Form
which renders an RDF/POST form for for creation of new resources (when$ac:forClass
parameter is set) or editing of existing resource
- Resource-level templates modes that apply to
rdf:Description
bs2:Block
renders full resource description (by default header and property list)bs2:Header
renders resource header (by default with type information)bs2:PropertyList
renders definition list with property names and values (by default grouped by resource types)
When adding new user-defined modes, it is recommended to choose a new namespace for them as well as a user-defined prefix.
An example of a template that matches rdf:Description
:
<xsl:template match="*[*][@rdf:about] | *[*][@rdf:nodeID]" mode="bs2:Block"> <xsl:param name="id" as="xs:string?"/> <xsl:param name="class" as="xs:string?"/> <div> <xsl:if test="$id"> <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute> </xsl:if> <xsl:if test="$class"> <xsl:attribute name="class"><xsl:value-of select="$class"/></xsl:attribute> </xsl:if> <xsl:apply-templates select="." mode="bs2:Header"/> <xsl:apply-templates select="." mode="bs2:PropertyList"/> </div> </xsl:template>
apl:Content
mode embeds XHTML from the sioc:content
value of the current document.
There are a few special template modes such as ac:label
and ac:description
and related functions ac:label()
and ac:description()
which are
used not to render layout but to extract metadata from resource descriptions. They
can be used to retrieve a resource label and description no matter which RDF
vocabularies are used in the data. They do so by invoking templates of respective
mode from vocabulary-specific stylesheets.
Overriding templates
Templates are overridden by redefining them in the importing stylesheet and providing the same or more specific match pattern and the same mode. The XSLT specification specifies exactly how template priorities are determined in 6.4 Conflict Resolution for Template Rules.
The overriding template can then get the output of the overridden template by invoking
either <xsl:apply-imports>
or <xsl:next-match>
. Read more about 6.7 Overriding Template Rules.
Always override the most specific template, i.e. if you want to change how a property is rendered, do not override the template for resource description, only the one for the property.
Keys
Keys are a lookup mechanism. They are defined on the stylesheet level using <xsl:key>
and invoked using the
key()
function. For example:
<xsl:key name="resources" match="*[*][@rdf:about] | *[*][@rdf:nodeID]" use="@rdf:about | @rdf:nodeID"/> <xsl:template match="*"> <xsl:for-each select="key('resources', $ac:uri)"> <xsl:value-of select="ac:label(.)"/> </xsl:for-each> </xsl:template>
They key definition matches rdf:Description
elements and uses their identifiers (URI or blank node ID). The template then looks
up the RDF description of the current resource, i.e. the resource with URI that equals $ac:uri
which is the absolute URI of the current request, and outputs its label.
Loading data
The stylesheet is processing one main RDF/XML document at a time, which is supplied
by the LinkedDataHub's HTML writer. However it is possible to load additional XML
documents over HTTP
using the document()
XSLT function. To avoid XSLT errors on any possible error responses, it is advisable
to do a conditional check using the doc-available()
function before doing the actual document()
call.
For example, instead of hardcoding the title of this document as Stylesheets, we can use the following code to load it and output it on the fly:
<xsl:value-of select="key('resources', 'https://linkeddatahub.com/linkeddatahub/docs/reference/stylesheet/', document('https://linkeddatahub.com/linkeddatahub/docs/reference/stylesheet/'))"/>
In case this document changes its title, all such references would automatically render the updated title. On the other hand, it incurs the overhead of making an HTTP request.
LinkedDataHub's default stylesheets are using this feature extensively. In fact, one HTML page is rendered from a dozen of RDF/XML documents.
Built-in model ontologies and sitemap ontologies, as well as some other system and well-known ontologies, have a local copy in
each LinkedDataHub instance. As a result, retrieving their descriptions by dereferencing
their URIs using document()
does not incur an HTTP request and is much faster. The URI-to-file mapping
is defined as Jena's location mapping and can be found in
location-mapping.n3 and prefix-mapping.n3.
Client-side stylesheets use ixsl:schedule-action
to load XML documents asynchronously.
CSS
Bootstrap 2.3.2 theme is used with some customizations.
The CSS stylesheets is specified in the xhtml:Style
XSLT template mode.
JavaScript
The JavaScript files are specified in the xhtml:Script
XSLT template mode.
LinkedDataHub only uses JavaScript for the functionality that cannot be achieved using client-side XSLT.