@prefix this: . @prefix sub: . @prefix np: . @prefix grlc: . @prefix dct: . @prefix nt: . @prefix npx: . @prefix xsd: . @prefix rdfs: . @prefix orcid: . @prefix prov: . @prefix foaf: . sub:Head { this: a np:Nanopublication; np:hasAssertion sub:assertion; np:hasProvenance sub:provenance; np:hasPublicationInfo sub:pubinfo . } sub:assertion { sub:get-template-info a grlc:grlc-query; dct:description "Returns key-value facts about a space-governed template kind given as the resource, for display on its maintained-resource page: the label, description, tag, governing space, current governed version nanopub (shown as its short artifact code), and its last-modified date and author, all read off the current governed winner of the (kind, space) pair (newest non-invalidated version signed by a current member+ of the governing space, with the kind validated as maintained by that space -- mirroring get-latest-governed-version). It also yields two action rows, both root-relative links (/publish?...) that Nanodash renders as buttons: 'Use this template', which opens the current version's template node as a fillable form with the template's title as the link text, and 'Update this template', which opens that same version in the publish form's override fill mode, i.e. as the starting point of the next version. The update row is shown only to a viewer who currently holds admin, maintainer or member tier in the governing space, which is what the per-row override action it replaces expressed with gen:isVisibleTo gen:MemberRole: the tier holders of the kind's governing space ref are materialized once in a self-contained sub-select (its own values block, one federated call, no per-row lookup) and the viewer, bound by Nanodash into the magic parameter ?__CURRENTUSER_multi_iri, is matched against them by string containment. The match is coalesce-guarded rather than bound-tested, so a logged-out viewer -- for whom the variable stays unbound -- fails closed and simply gets no update row. The form it opens is the template the current version itself was created from (nt:wasCreatedFromTemplate in its publication info, at template-version=latest, so a provenance or pubinfo template kind opens its own meta-template), falling back to the assertion-template meta-template where that link is absent; the lookup keeps only the lexicographically first value, so a nanopub carrying several such links cannot multiply the rows of the table. Governed winners are materialized once per kind in a self-contained sub-select. Both columns use the '_noheader' convention so the redundant header row is dropped. Two things to keep in mind when editing this query: every sub-select scope needs its own values block for ?_resource_multi_iri, or a pattern inside it is left unscoped and matches every space; and a disjunctive filter inside a service block that is evaluated without incoming bindings returns nothing on RDF4J, which is why the tier restriction of the member sub-select is written as a values block rather than as the OR-filter used in the winner sub-select."; dct:license ; rdfs:label "Get template info"; grlc:endpoint ; grlc:sparql """prefix rdfs: prefix dct: prefix np: prefix npa: prefix npx: prefix nt: prefix gen: select ?Property_noheader ?Value_noheader ?Value_label where { values ?_resource_multi_iri {} values ?__CURRENTUSER_multi_iri {} { select ?gKind (max(concat(str(?gDate), '>', str(?gNp))) as ?maxW) where { graph npa:graph { ?gNp npa:hasValidSignatureForPublicKeyHash ?gPubkeyhash ; dct:created ?gDate ; npx:embeds ?gVersion ; np:hasAssertion ?gA . filter not exists { ?gInv npx:invalidates ?gNp ; npa:hasValidSignatureForPublicKeyHash ?gPubkeyhash . } } graph ?gA { ?gVersion dct:isVersionOf ?gKind ; gen:governedBy ?gSpace . } service { graph npa:graph { npa:hasCurrentSpaceState ?stateG . } graph ?stateG { ?gKind npa:isMaintainedBy ?gSpace ; npa:hasGoverningSpaceRef ?gRef . ?gRi a gen:RoleInstantiation ; npa:forSpace ?gSpace ; npa:forSpaceRef ?gRef ; npa:hasRoleType ?gTier ; npa:forAgent ?gAgent . filter(?gTier = gen:AdminRole || ?gTier = gen:MaintainerRole || ?gTier = gen:MemberRole) ?gAcct a npa:AccountState ; npa:agent ?gAgent ; npa:pubkey ?gPubkeyhash . } } } group by ?gKind } { select (group_concat(distinct ?mEntry; separator='|') as ?memberList) where { values ?_resource_multi_iri {} service { graph npa:graph { npa:hasCurrentSpaceState ?mStateG . } graph ?mStateG { ?_resource_multi_iri npa:hasGoverningSpaceRef ?mRef . values ?mTier { gen:AdminRole gen:MaintainerRole gen:MemberRole } ?mRi a gen:RoleInstantiation ; npa:forSpaceRef ?mRef ; npa:hasRoleType ?mTier ; npa:forAgent ?mAgent . } } bind(str(?mAgent) as ?mEntry) } } filter(?gKind = ?_resource_multi_iri) bind(iri(strafter(?maxW, '>')) as ?np) graph npa:graph { ?np npx:embeds ?tmpl ; dct:created ?dateVal ; np:hasAssertion ?a ; np:hasPublicationInfo ?pi . optional { ?np npx:signedBy ?authorVal . } } graph ?a { ?tmpl dct:isVersionOf ?_resource_multi_iri ; gen:governedBy ?spaceVal . } optional { graph ?a { ?tmpl rdfs:label ?labelVal } } optional { graph ?a { ?tmpl dct:description ?descVal } } optional { graph ?a { ?tmpl nt:hasTag ?tagVal } } optional { graph ?pi { ?np nt:wasCreatedFromTemplate ?metaTmplVal . } filter not exists { graph ?pi { ?np nt:wasCreatedFromTemplate ?metaOther . filter(str(?metaOther) < str(?metaTmplVal)) } } } bind(concat('', coalesce(?labelVal, str(?tmpl)), '') as ?formLink) bind(coalesce(str(?metaTmplVal), 'https://w3id.org/np/RAKmcrbEVkxF0ivA0_yGg8bnl6iKkwVDp_2SSHvPBrkPw/template') as ?metaTmpl) bind(coalesce(contains(concat('|', ?memberList, '|'), concat('|', str(?__CURRENTUSER_multi_iri), '|')), false) as ?isMember) bind(if(?isMember, concat('✏ publish a new version'), ?noUpdateLink) as ?updateLink) values (?ord ?Property_noheader) { (1 'Label:') (2 'Description:') (3 'Tag:') (4 'Governed by:') (5 'Current version:') (6 'Last modified:') (7 'Last modified by:') (8 'Use this template:') (9 'Update this template:') } bind(if(?ord=1, ?labelVal, if(?ord=2, ?descVal, if(?ord=3, ?tagVal, if(?ord=4, ?spaceVal, if(?ord=5, ?np, if(?ord=6, str(?dateVal), if(?ord=7, ?authorVal, if(?ord=8, ?formLink, if(?ord=9, ?updateLink, ''))))))))) as ?Value_noheader) filter(bound(?Value_noheader)) bind(if(?ord=5, concat(substr(strafter(str(?np), '/np/'), 1, 10), '…'), '') as ?Value_label) } order by ?ord""" . } sub:provenance { sub:assertion prov:wasAttributedTo orcid:0000-0002-1267-0234 . } sub:pubinfo { orcid:0000-0002-1267-0234 foaf:name "Tobias Kuhn" . this: dct:created "2026-10-12T13:21:13Z"^^xsd:dateTime; dct:creator orcid:0000-0002-1267-0234; dct:license ; npx:embeds sub:get-template-info; npx:supersedes ; rdfs:label "Get template info"; nt:wasCreatedFromProvenanceTemplate ; nt:wasCreatedFromPubinfoTemplate , ; nt:wasCreatedFromTemplate . sub:sig npx:hasAlgorithm "RSA"; npx:hasPublicKey "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwUtewGCpT5vIfXYE1bmf/Uqu1ojqnWdYxv+ySO80ul8Gu7m8KoyPAwuvaPj0lvPtHrg000qMmkxzKhYknEjq8v7EerxZNYp5B3/3+5ZpuWOYAs78UnQVjbHSmDdmryr4D4VvvNIiUmd0yxci47dTFUj4DvfHnGd6hVe5+goqdcwIDAQAB"; npx:hasSignature "YtgeymvuP3XbFEdCq1BdQRXDyPRFs9bQe+UHEywonE9tYlCKbgETGuDh8jFfo7DwIO4/AxnS6aSoTYSki7piwzn702zvjtSWIqDw3Z3rDWHIqNTn8gOHYxn1XlhxwtrtejmvZP+tT1i8UgUZZBoBxJfPVwZUEFg6Xmn4hm7pG68="; npx:hasSignatureTarget this:; npx:signedBy orcid:0000-0002-1267-0234 . }