Reusable bundles of an ontology and a stylesheet that add support and custom rendering for an RDF vocabulary

Packages were introduced in LinkedDataHub 5.2. Since 5.10 a dataspace installs one by declaring a single ldh:import triple in its settings, replacing the packages/install and packages/uninstall endpoints and the file system mutations they performed.

Packages are declarative only (RDF + XSLT) — they contain no Java code. See Architecture for how they are composed into the dataspace.

Overview

Packages enable:

  • Rapid dataspace setup with pre-configured domain vocabularies
  • Sharing and reusing common vocabulary definitions
  • Custom rendering for vocabulary-specific resources
  • Modular extension of dataspace functionality

Package structure

Each package consists of two files:

ns.ttl — Package ontology
An RDF ontology file that imports the external vocabulary using owl:imports and attaches views to properties using ldh:view (forward relationships) or ldh:inverseView (inverse relationships). Views are typically ldh:View resources with SPARQL queries that render related data for property values.
XSLT stylesheet — named by ac:stylesheet
XSLT transformation file with templates that override default rendering using system modes like ac:*, ldh:* and xhtml:*. See the Stylesheets reference for details on XSLT customization.

Package files are organized in the LinkedDataHub-Apps repository, where the directory path is the package URI — a package under packages/a/b/ is published at https://packages.linkeddatahub.com/a/b/, so a path carries as many segments as the grouping needs:

packages/
├── editor/
│   └── taxonomy/
│       ├── ns.ttl     # Ontology with views
│       └── skos.xsl   # XSLT stylesheet

The ontology is always ns.ttl; the stylesheet filename is not a convention, it is whatever the package's ac:stylesheet names. The taxonomy editor is named for what it does and its stylesheet for the vocabulary it speaks.

Package metadata

Package metadata is published as Linked Data that resolves from the package URI (e.g. https://packages.linkeddatahub.com/editor/taxonomy/#this). A package descriptor is an instance of the lds:Package class with these properties:

rdfs:label
Human-readable package name
dct:description
Package description and purpose
lds:ontology
The package ontology URI (LDT vocabulary)
ac:stylesheet
The package stylesheet URI (AtomGraph Client vocabulary)

Example package metadata:

@prefix lds: <https://w3id.org/atomgraph/linkeddatahub/dataspaces#> .
@prefix ac:   <https://w3id.org/atomgraph/client#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dct:  <http://purl.org/dc/terms/> .

<https://packages.linkeddatahub.com/editor/taxonomy/#this> a lds:Package ;
    rdfs:label "Taxonomy Editor" ;
    dct:description "Taxonomy editing on SKOS, with custom templates" ;
    lds:ontology <https://raw.githubusercontent.com/AtomGraph/LinkedDataHub-Apps/master/packages/editor/taxonomy/ns.ttl#> ;
    ac:stylesheet <https://raw.githubusercontent.com/AtomGraph/LinkedDataHub-Apps/master/packages/editor/taxonomy/skos.xsl> .

Package ontology

The package ontology file contains two parts:

Vocabulary import

Imports the external vocabulary using owl:imports. See the Ontologies reference for ontology management details.

<https://raw.githubusercontent.com/AtomGraph/LinkedDataHub-Apps/master/packages/editor/taxonomy/ns.ttl#> a owl:Ontology ;
    owl:imports <http://www.w3.org/2004/02/skos/core> .

Property views

SPARQL-based views attached to properties from the imported vocabulary:

skos:narrower ldh:view ns:NarrowerConcepts .

ns:NarrowerConcepts a ldh:View ;
    dct:title "Narrower concepts" ;
    spin:query ns:SelectNarrowerConcepts .

ns:SelectNarrowerConcepts a sp:Select ;
    sp:text """SELECT DISTINCT ?narrower
    WHERE { GRAPH ?graph { $about skos:narrower ?narrower } }
    ORDER BY ?narrower""" .

Views are rendered when displaying resources that have the specified property. Use ldh:view for forward relationships (the resource carries the property) or ldh:inverseView for inverse relationships (other resources point at this resource through the property).

Package stylesheet

XSLT templates in the platform's open modes, specialising the rendering of the package's own vocabulary:

<!-- The hierarchy predicates render as the concept tree, not as statement rows -->
<xsl:template match="skos:narrower | skos:broader | skos:related | skos:member" mode="ac:PropertyEditor"/>

<!-- Fill the column beside the content body on a concept scheme's document -->
<xsl:template match="rdf:RDF[...]" mode="ldh:ContentColumn">
    <xsl:param name="mode" as="xs:anyURI?"/>
    <!-- the concept tree -->
</xsl:template>

A package stylesheet is composed into the platform's import tree right after hooks.xsl, the module that declares the open modes and their generic fallbacks, and below everything else. Import precedence decides what a package can do: its rule for a node in an open mode outranks the fallback whatever the priorities, and it cannot contradict a sealed rule for the same node — a component mode, a typed rule, a global variable, named template or function — since that rule outranks the package. An open mode is a leaf: it renders or contributes for one node and carries no control flow. The open modes are:

  • ldh:TreeNode — one tree node; replace the default renderer, or decorate it with xsl:next-match
  • ac:PropertyEditor — one resource's property list, or one statement row; replace or decorate, and an empty rule hides
  • ldh:ContentColumn — a slot beside the content body, empty unless a package fills it (the taxonomy editor's concept tree)
  • ldh:TreeChildrenLoad and ldh:RowHook — client only: the children fetch for one tree node, and deferred work for one rendered row
  • The value leaves — ac:FormControl, ac:PropertyListValue, ac:ValueAnnotations, ldh:TypeControl, ac:property-label, ac:object-label, ac:lang-tag, ac:ResultsTableHeaderCell, xhtml:Anchor and the unnamed mode's property row and value link. A rule for the package's own property or datatype replaces the generic one, or decorates it

The modes are documented in the Stylesheets reference. The package stylesheet reaches the browser as well: the platform composes each dataspace's client stylesheet with its package stylesheets and has the sef-compiler compile the result, so a package's rendering is the same on a direct page load and on client-side navigation.

Installing packages

A dataspace imports a package with a single triple in its settings:

<urn:linkeddatahub:apps/end-user> ldh:import <https://packages.linkeddatahub.com/editor/taxonomy/#this> .

That declaration is the installation. Packages are installed into the end-user dataspace of a dataspace, and writing the triple requires write access to its settings document. See the step-by-step installation guide for the user interface and command line instructions.

What the declaration does

From the next request onwards, the server resolves the declaration:

  1. Resolves the package description from the package URI. Bundled descriptions and cached graphs are read from the graph repository; other URIs are dereferenced over HTTP.
  2. Materializes the package ontology (lds:ontology) as a document under the admin dataspace's ontologies/ container, named after the package path — ontologies/editor-taxonomy/ for the taxonomy editor. The ontology is copied verbatim and the document names it as its foaf:primaryTopic; the copy is taken once, and the document is skipped on later requests.
  3. Adds the package ontology to the dataspace's ontology imports closure — it is declared as an owl:imports of the namespace ontology and resolves to the materialized document. The package's classes, constructors, constraints and views become available on the ns endpoint and in the UI, and are edited in that document like the namespace ontology's own.
  4. Copies the package stylesheet (ac:stylesheet) once under the platform's package root and serves it from the dataspace's own origin under /static/com/linkeddatahub/packages/, so what the instance compiles cannot change under it.
  5. Composes that copy into the dataspace stylesheet with an xsl:import right after hooks.xsl, so package templates outrank the open modes' fallbacks and nothing else; the client-side stylesheet is composed the same way and compiled by the sef-compiler.

Both copies are taken at import and kept: a change to the published package reaches a dataspace that already imported it only after the materialized ontology document is deleted (the next request materializes it again) and the ontology cache is cleared.

Packages are applied in the order of their URIs. A package that declares only an ontology, or only a stylesheet, contributes only that, and one whose description cannot be resolved is skipped. If the composed stylesheet fails to compile — an unreachable package stylesheet URL, for instance — the dataspace falls back to its own stylesheet.

On the Northwind Traders dataspace the taxonomy editor package pays off immediately: the product categories that the UNESCO mapping import doubled as skos:Concepts pick up the package's concept rendering and views, and their mapping relations link into the UNESCO Thesaurus dataspace.

A Northwind category page with the taxonomy editor package installed, concept rendering and UNESCO mappings visible

Uninstalling packages

Removing the ldh:import triple uninstalls the package: from the next request onwards its ontology is out of the imports closure and its stylesheet is no longer composed in.

DELETE
{
    <urn:linkeddatahub:apps/end-user> ldh:import <https://packages.linkeddatahub.com/editor/taxonomy/#this> .
}
WHERE {}

Uninstalling a package does not remove user-created data that uses the package's vocabulary. That data stays in the dataspace but may not display or function correctly without the package.

Nor does it remove the materialized ontology document under ontologies/, so edits made to it survive an uninstall and a later re-import picks the document up as it is.

Architecture

Runtime composition

Packages are composed at request time out of the declaration, not integrated into the dataspace ahead of it:

  • The dataspace settings hold one ldh:import triple per package and nothing else
  • The ontology closure and the stylesheet imports are assembled from the resolved package descriptions
  • Installing and uninstalling take effect on the next request — no restart, and nothing is written into the webapp: the ontology copy is a document in the admin store and the stylesheet copy lives under the package root outside the war

Packages installed before 5.10 were applied by copying files into the webapp; those installations do not carry over. Re-declare them with ldh:import.

Creating custom packages

Developers can create custom packages for their own domain vocabularies. The process has four steps.

Write package ontology

Create ns.ttl with vocabulary import and views:

<https://raw.githubusercontent.com/you/repo/master/packages/schema.org/ns.ttl#> a owl:Ontology ;
    owl:imports <https://schema.org/> .

# Attach view to a property
schema:knows ldh:view :PersonKnows .

:PersonKnows a ldh:View ;
    dct:title "Knows" ;
    spin:query :SelectPersonKnows .

:SelectPersonKnows a sp:Select ;
    sp:text """
    SELECT DISTINCT ?person
    WHERE { GRAPH ?graph { $about schema:knows ?person } }
    ORDER BY ?person
    """ .

Write XSLT stylesheet

Create the stylesheet — name the file for the vocabulary it covers — with XSLT templates using system modes like ac:*, ldh:* and xhtml:*. See the Stylesheets reference for template customization patterns.

<xsl:template match="schema:knows" mode="ac:PropertyEditor"/>

Publish package metadata

Publish package metadata as Linked Data at your package URI:

<https://packages.linkeddatahub.com/schema.org/#this> a lds:Package ;
    rdfs:label "Schema.org Package" ;
    dct:description "Schema.org vocabulary support" ;
    lds:ontology <https://raw.githubusercontent.com/you/repo/master/packages/schema.org/ns.ttl#> ;
    ac:stylesheet <https://raw.githubusercontent.com/you/repo/master/packages/schema.org/schema.xsl> .

Ensure the metadata contains lds:ontology and ac:stylesheet properties pointing to the package resources.

Test installation

Declare the ldh:import triple in a test dataspace's settings document — see Management for the command.

Available packages

Published packages resolve from packages.linkeddatahub.com; their sources live in the LinkedDataHub-Apps repository, where each package directory contains the package ontology (ns.ttl) and the stylesheet its ac:stylesheet names.

Taxonomy Editor
Taxonomy editing on SKOS: a concept tree beside the content, hierarchy views in both assertion directions, and constructors and constraints for concepts, schemes and collections. Package URI: https://packages.linkeddatahub.com/editor/taxonomy/#this

Management

The packages group reads the registry and writes the ldh:import triple:

ldh packages list
ldh packages add --package https://packages.linkeddatahub.com/editor/taxonomy/#this
ldh packages remove --package https://packages.linkeddatahub.com/editor/taxonomy/#this

packages list prints one tab-separated line per package — state, URI, title — so the listing greps and cuts. The registry defaults to https://packages.linkeddatahub.com/ and --registry overrides it; it is read through the dataspace's Linked Data proxy rather than fetched directly, so list needs --base as much as the other two do.

All three go through PATCH on the settings document, which is the live path: the change takes effect on the next request with no restart, but lives in the running dataspace's context dataset. Declaring the same ldh:import triple in config/dataspaces.trig is the permanent one, applied on restart. packages remove is idempotent.

To install and manage packages step by step, follow the Manage packages guide.