Change layout

How to customize the layout of an application and the UI of the resources

LinkedDataHub's user interface is simply a rendering of the underlying Linked Data resource descriptions, which are exposed via the HTTP API.

LinkedDataHub provides XSLT stylesheets that render a default UI layout. When building an application you might want to render a custom layout however. The recommended way of doing that is to create a new stylesheet which imports the system stylesheet and only override specific templates, while reusing the rest of the layout.

Create a stylesheet

First we need to create a new XSLT 3.0 stylesheet and use <xsl:import> to import the system stylesheet static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/layout.xsl.

See an example in the SKOS demo app.

Configure the dataspace

First, either upload the XSLT file or mount it using Docker and docker-compose.override.yml::

version: "2.3"
services:
  linkeddatahub:
    volumes:
      - ../LinkedDataHub-Apps/demo/skos/files/skos.xsl:/usr/local/tomcat/webapps/ROOT/static/com/atomgraph/linkeddatahub/demo/skos/xsl/index.xsl:ro

Then change the value of ac:stylesheet on the dataspace with base URI https://localhost:4443/ to the relative URI of the stylesheet:

<urn:linkeddatahub:apps/end-user> a lapp:EndUserApplication ;
    ...
    ac:stylesheet <static/com/atomgraph/linkeddatahub/demo/skos/xsl/index.xsl> ;
    ...

Default layout

In the examples below we assume that the graph contains a SKOS taxonomy with concept instances, which can also have broader/narrower concepts.

At this point, the default layout of the current document, its topic concept, and narrower/broader concepts looks like this:

Default layout

Augment output

Keep the default output using <xsl:apply-imports> or <xsl:next-match> and add new output before/after it:

<xsl:key name="resources-by-broader" match="*[@rdf:about] | *[@rdf:nodeID]" use="skos:broader/@rdf:resource"/>
        
<xsl:template match="*[foaf:isPrimaryTopicOf/@rdf:resource = $ac:uri][key('resources-by-broader', @rdf:about)]  | *[foaf:isPrimaryTopicOf/@rdf:resource = $ac:uri][key('resources', skos:narrower/@rdf:resource)]" mode="bs2:Block" priority="1">
    <xsl:next-match/>

    <h3>Narrower concepts</h3>
    <ul>
        <xsl:apply-templates select="key('resources-by-broader', @rdf:about) | key('resources', skos:narrower/@rdf:resource)" mode="bs2:List">
            <xsl:sort select="ac:label(.)"/>
        </xsl:apply-templates>
    </ul>
</xsl:template>

The match pattern will match the resource descriptions that:

  • are the primary topics of the requested document (i.e. its concept) and is a broader concept of some other resources in the RDF graph
  • are the primary topics of the requested document (i.e. its concept) and has narrower concept(s) in the RDF graph

We can render broader concepts by making a copy of the key and the template above and replacing all occurences of skos:narrower with skos:broader and vice versa.

With the overriding template, the layout looks like this:

Augmented output

Override output

To completely change the layout without keeping the default one, use the same logic as for augmenting it, but do not call <xsl:apply-imports>/<xsl:next-match>.

Suppress output

In the above example, we have rendered skos:narrower properties in our own special way. Which means that the default output of the same properties from bs2:PropertyList is no longer desired.

You can specify an empty template at any level (graph/property/resource) to disable output of that layout mode. For example, this will suppress the default rendering of concepts which are now shown in the customized broader/narrower list:

<xsl:template match="*[key('resources', skos:narrower/@rdf:resource)/foaf:isPrimaryTopicOf/@rdf:resource = $ac:uri] | *[key('resources', skos:broader/@rdf:resource)/foaf:isPrimaryTopicOf/@rdf:resource = $ac:uri] | *[@rdf:about = key('resources', key('resources', $ac:uri)/foaf:primaryTopic/@rdf:resource)/skos:narrower/@rdf:resource] | *[@rdf:about = key('resources', key('resources', $ac:uri)/foaf:primaryTopic/@rdf:resource)/skos:broader/@rdf:resource]" mode="bs2:Block"/>

The match pattern will match the resource descriptions that:

  • have narrower concept(s) in the RDF graph that are the primary topic of the current document
  • have broader concept(s) in the RDF graph that are the primary topic of the current document
  • are narrower concept of the primary topic of the current document
  • are broader concept of the primary topic of the current document
<xsl:template match="*[foaf:isPrimaryTopicOf/@rdf:resource = $ac:uri][key('resources', skos:broader/@rdf:resource)]/skos:broader | *[foaf:isPrimaryTopicOf/@rdf:resource = $ac:uri][key('resources', skos:narrower/@rdf:resource)]/skos:narrower" mode="bs2:PropertyList"/>

The match pattern will match the resource properties that:

  • are skos:broader properties of a resource that is the primary topic of the current document and has broader concepts in the RDF graph
  • are skos:narrower properties of a resource that is the primary topic of the current document and has narrower concepts in the RDF graph

With the supressing templates, the layout now looks like this:

Suppressed output