This directory contains reusable packages for LinkedDataHub dataspaces. Packages provide vocabulary support with custom ontologies and XSLT templates for rendering specific RDF vocabularies.
Version: Packages were introduced in LinkedDataHub 5.2.
Each package consists of:
packages/<package-path>/
├── ns.ttl # Ontology with property views (ldh:view/ldh:inverseView)
└── <name>.xsl # XSLT stylesheet with custom templates
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 may carry as many segments as the grouping
needs. A package folder holds one .xsl file, and install.sh makes it the package's ac:stylesheet;
its name is not a convention.
Package metadata is Linked Data that resolves from the package URI (e.g., https://packages.linkeddatahub.com/editor/taxonomy/#this).
packages/editor/taxonomy/
├── ns.ttl # SKOS vocabulary with ldh:view attachments to properties
└── skos.xsl # XSLT templates for SKOS concepts, schemes, collections
Metadata for this package resolves from https://packages.linkeddatahub.com/editor/taxonomy/#this.
The package is named for what it does; its stylesheet is named for the vocabulary it speaks.
Package metadata resolves as Linked Data from the package URI using standard LinkedDataHub properties.
The descriptor is the foaf:primaryTopic of the package's own document, packages/editor/taxonomy.ttl,
which ldh push PUTs to https://packages.linkeddatahub.com/editor/taxonomy/:
@prefix lds: <https://w3id.org/atomgraph/linkeddatahub/dataspaces#> .
@prefix dct: <http://purl.org/dc/terms/> .
<#this> a lds:Package ;
dct:title "Taxonomy Editor" ;
dct:description "Turns a dataspace into a taxonomy editor: ..." ;
lds:ontology <ns/#> .lds:ontology <ns/#> is registry-relative: the same push PUTs ns.ttl as the ns/ document beside the
descriptor, so the ontology is served by the registry itself. It names the ontology IRI that ns.ttl
declares (<#> resolved against the ns/ document), not the document URL: the imports closure keys
graphs by ontology IRI, and a descriptor naming the document loads it a second time under the IRI,
which fails with "Another graph with name ... is already in the hierarchy".
The descriptor in this repository has no ac:stylesheet. The push uploads the package's .xsl file into
the package document as text/xsl, and install.sh then adds the reference to that upload, whose URI it
computes from the same file:
<https://packages.linkeddatahub.com/editor/taxonomy/#this>
ac:stylesheet <https://packages.linkeddatahub.com/uploads/01db8c939f6841c397c0e4eda60a863318ca5126> .An upload's URI is the SHA-1 of its content, so the published reference names exactly the stylesheet that was published with it, and a changed stylesheet gets a new URI rather than changing under the dataspaces that already copied it (see What the Declaration Does).
Note: Uses standard lds:ontology and ac:stylesheet properties instead of inventing new ones.
Contains two layers:
A. Vocabulary Import
Imports the external vocabulary using owl:imports:
@prefix : <#> . # resolves against the ns/ document the registry serves it as
: a owl:Ontology ;
owl:imports <http://www.w3.org/2004/02/skos/core> .B. Property Views (ldh:view/ldh:inverseView)
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
""" .Use ldh:view for forward relationships (resource has property) or ldh:inverseView for inverse relationships (other resources point to this resource via property).
XSLT template rules in the platform's open modes:
<!-- Hide the hierarchy predicates from the property list: the concept tree shows them -->
<xsl:template match="skos:narrower | skos:broader" mode="ac:PropertyEditor"/>
<!-- Render a concept's tree node as expandable, keeping the platform's markup -->
<xsl:template match="*[@rdf:about][rdf:type/@rdf:resource = '&skos;Concept']" mode="ldh:TreeNode" priority="1">
<xsl:next-match>
<xsl:with-param name="expandable" select="true()"/>
</xsl:next-match>
</xsl:template>The package stylesheet is composed into the platform's import tree right above hooks.xsl, the module
that declares the open modes and their generic fallbacks, and below everything else. Import precedence
beats template priority, so that position is the contract: a package rule outranks a fallback in an open
mode whatever the priorities, and loses to any rule in a sealed mode whatever its own priority. A package
cannot replace the page head, the content body, a typed rule or a global - those are sealed by
precedence, not by policy.
An open mode is a leaf: it renders or contributes for one node and carries no control flow. The open modes, with what a rule in each one owes:
| Mode | What it renders | A package rule |
|---|---|---|
ldh:TreeNode |
one tree node | replaces the fallback, or decorates it with xsl:next-match |
ac:PropertyEditor |
one resource's property list, or one statement row | replaces or decorates; an empty rule hides |
ldh:ContentColumn |
the navigation slot beside the content body | fills it; nothing to inherit |
ldh:TreeChildrenLoad |
(client) the children fetch for one tree node | replaces |
ldh:RowHook |
(client) factories of deferred work for one rendered row | contributes; nothing to inherit |
ac:FormControl |
one property's form control, or one value's | replaces the generic control for the package's property or datatype, or decorates it with xsl:next-match |
ac:PropertyListValue |
one value cell in the property list | replaces or decorates |
ac:ValueAnnotations |
the term-kind / datatype / language chip beside a value | replaces or decorates |
ldh:TypeControl, ac:property-label, ac:object-label, the unnamed mode |
the property row and how a value is labelled and linked | replaces or decorates |
hooks.xsl in the platform sources carries the same table beside the declarations; the value leaves are
in imports/values.xsl, which it imports. The platform's own vocabulary modules (imports/rdf.xsl,
imports/dct.xsl...) sit above the packages, so a package specialises core's generic rendering and
cannot contradict how core renders its own terms. The component modes (ldh:Modal, ac:FieldShell...),
the ldh:Combobox widget and the library in imports/default.xsl (keys, params, functions) are sealed.
The declaration is the installation. A dataspace imports a package by carrying a single
ldh:import triple in its settings, and the packages CLI group reads the registry and writes that
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/#thispackages list prints one tab-separated line per package — state, URI, title. The registry defaults
to https://packages.linkeddatahub.com/; --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.
The dataspace settings modal offers the same thing as a checkbox per package, saved with the rest
of the settings in one PATCH.
Both paths go through PATCH /settings, which is the live route: the change takes effect on the
next request, and lives in the running dataspace's context dataset. Declaring the same triple in
config/dataspaces.trig is the permanent one, applied on restart.
<urn:linkeddatahub:apps/end-user> ldh:import <https://packages.linkeddatahub.com/editor/taxonomy/#this> .From the next request onwards, the server resolves it:
- Resolves the package description from the package URI. Cached graphs come from the graph repository; other URIs are dereferenced over HTTP.
- Materializes the package ontology (
lds:ontology) as a document under the admin dataspace'sontologies/container, named after the package path —ontologies/editor-taxonomy/for the taxonomy editor. The ontology is copied verbatim, once; the document names it as itsfoaf:primaryTopicand is skipped on later requests. - Adds that document to the dataspace's ontology imports closure, as an
owl:importsof the namespace ontology. Its classes, constructors, constraints and views become available on thensendpoint and in the UI, and are edited there like the namespace ontology's own. - 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. - Composes that copy into the dataspace stylesheet by inserting an
xsl:importright after the platform'shooks.xslimport, so package templates override the open modes' fallbacks and nothing else (see the stylesheet section above). The client-side stylesheet is composed the same way and compiled by thesef-compiler.
Packages are applied in the order of their URIs. One that declares only an ontology, or only a stylesheet, contributes only that; one whose description cannot be resolved is skipped. If the composed stylesheet fails to compile — an unreachable stylesheet URL, say — the dataspace falls back to its own.
Both copies are taken at import and kept. No restart is needed, but 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.
Uninstalling is the same in reverse: retract the triple, and from the next request the ontology is out of the closure and the stylesheet is no longer composed in. Data created with the package's vocabulary stays in the dataspace, and may not display or validate correctly without it.
| Package | URI | Provides |
|---|---|---|
editor/taxonomy |
https://packages.linkeddatahub.com/editor/taxonomy/#this |
SKOS constructors, constraints, hierarchy views and a concept tree. Used by demo/unesco-thesaurus. |
ldh packages list prints the registry's current contents.
- Create directory:
packages/<path>/, naming it for what the package does - Write
ns.ttlwith vocabulary and property views (usingldh:vieworldh:inverseView) - Write the stylesheet with XSLT templates (using system modes like
ac:*,ldh:*,xhtml:*, etc.), naming the file for the vocabulary it covers; it is the only.xslfile in the folder - Describe the package in
packages/<path>.ttlas<#this>, thefoaf:primaryTopicof the document, with anlds:ontologynaming the ontology IRIns.ttldeclares; leaveac:stylesheetout,install.shadds it - List the package in the catalog,
root.ttl, as anrdfs:memberof the registry root with the samedct:titleanddct:descriptionas its descriptor: the settings modal andldh packages listread the catalog, not the descriptors
lds:Package- Package classlds:ontology- Points to package ontology URI
ac:stylesheet- Points to package stylesheet URI (from AtomGraph Client vocabulary)
- Packages are declarative only (RDF + XSLT, no Java code)
- Package ontologies use
owl:imports(handled automatically by Jena) - Package ontologies are materialized as editable documents under the admin
ontologies/container, and package stylesheets are copied under/static/com/linkeddatahub/packages/, both once at import - Package stylesheets are composed into the dataspace stylesheet with
xsl:importat thehooks.xslmarker, per dataspace - Property views (
ldh:view/ldh:inverseView) are separate from XSLT overrides - Both mechanisms work independently and complement each other