From 5b5d0c9911d2548f08b24310aa888b4bba9d2c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Wed, 19 Aug 2026 00:08:29 +0200 Subject: [PATCH 1/4] Inline creation in views Implements the Inline-Creation-in-Views proposal: views attached via ldh:view/ldh:inverseView get a Create button in the view toolbar when the View carries the new ldh:container metadata. The modal constructor form PUTs the new instance document into the container; forward views PATCH the linking triple into the current document (INSERT/WHERE form), inverse views ship it inside the PUT body. Property, direction and constructed class are inferred from the ontology (rdfs:domain/rdfs:range incl. rdfs:subPropertyOf paths); the Create button is gated on acl:Write obtained by HEADing the container for its acl:mode Link headers. Also adds optional ldh:showWhenEmpty (default true). Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 + .../com/atomgraph/linkeddatahub/ldh.ttl | 22 +- .../xsl/bootstrap/2.3.2/client/block.xsl | 112 +++++++- .../xsl/bootstrap/2.3.2/client/block/view.xsl | 263 +++++++++++++++++- .../xsl/bootstrap/2.3.2/client/modal.xsl | 7 +- 5 files changed, 394 insertions(+), 14 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9e7ee8ae5..5a9b979f0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -123,6 +123,10 @@ Key implementation files: The SPARQL endpoint forwarding chain ensures ContentMode blocks (charts, maps) query the **remote** app's SPARQL endpoint, not the local one. `LinkedDataHub.endpoint` is reset to the local endpoint by `ldh:HTMLDocumentLoaded` on every HTML page navigation, so there is no stale state when navigating back to local documents. +### UI Conventions + +**Form placement follows document membership.** A creation/edit form for a resource that lives (or will live) in its **own document** renders as a modal (`div.modal.modal-constructor` — Container/Item creation, document edit, inline creation in views); a form for a resource being added **to the current document** renders inline in the content body (`ldh:render-add-row-form`, content-block forms). The main content body renders only the current document's content. + ### Key Extension Points - **Vocabulary definitions** in `com.atomgraph.linkeddatahub.vocabulary` - **Custom resource handlers** in `com.atomgraph.linkeddatahub.resource` diff --git a/src/main/resources/com/atomgraph/linkeddatahub/ldh.ttl b/src/main/resources/com/atomgraph/linkeddatahub/ldh.ttl index fce8071bb..c6a2c8523 100644 --- a/src/main/resources/com/atomgraph/linkeddatahub/ldh.ttl +++ b/src/main/resources/com/atomgraph/linkeddatahub/ldh.ttl @@ -22,7 +22,7 @@ owl:imports dh:, ac:, , foaf:, sd:, nfo:, owl: ; rdfs:label "LinkedDataHub ontology" ; rdfs:comment "Should be imported and reused by all extending applications" ; - owl:versionInfo "1.1.6" . + owl:versionInfo "1.1.7" . # PROPERTIES @@ -105,6 +105,20 @@ rdfs:comment "Attaches a view to a property for inverse relationships (?value property $about)" ; rdfs:isDefinedBy : . +:container a owl:ObjectProperty ; + rdfs:domain :View ; + rdfs:range dh:Container ; + rdfs:label "Item container" ; + rdfs:comment "Container under which instances created inline from the view are stored as child documents" ; + rdfs:isDefinedBy : . + +:showWhenEmpty a owl:DatatypeProperty ; + rdfs:domain :View ; + rdfs:range xsd:boolean ; + rdfs:label "Show when empty" ; + rdfs:comment "Whether the view block is shown when its query returns no results. Defaults to true" ; + rdfs:isDefinedBy : . + # CLASSES # constructor @@ -324,11 +338,15 @@ PREFIX spin: PREFIX sp: PREFIX rdfs: + PREFIX xsd: PREFIX ac: + PREFIX ldh: CONSTRUCT { $this spin:query [ a sp:Select ] ; - ac:mode [ a rdfs:Resource ] . + ac:mode [ a rdfs:Resource ] ; + ldh:container [ a rdfs:Resource ] ; + ldh:showWhenEmpty [ a xsd:boolean ] . } WHERE {}""" ; rdfs:label "View constructor" ; diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl index f02bb7970..9cb256959 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl @@ -116,19 +116,28 @@ exclude-result-prefixes="#all" ]]> - + PREFIX ldh: - SELECT DISTINCT ?block + SELECT DISTINCT ?block ?property ?inverse ?forClass ?container ?showWhenEmpty WHERE { { ?property ldh:view ?block . { ?property rdfs:domain ?type } UNION { ?property rdfs:subPropertyOf+/rdfs:domain ?type } + BIND(false AS ?inverse) + OPTIONAL { + { ?property rdfs:range ?forClass } + UNION + { ?property rdfs:subPropertyOf+/rdfs:range ?forClass } + } } UNION { @@ -136,7 +145,15 @@ exclude-result-prefixes="#all" { ?property rdfs:range ?type } UNION { ?property rdfs:subPropertyOf+/rdfs:range ?type } + BIND(true AS ?inverse) + OPTIONAL { + { ?property rdfs:domain ?forClass } + UNION + { ?property rdfs:subPropertyOf+/rdfs:domain ?forClass } + } } + OPTIONAL { ?block ldh:container ?container } + OPTIONAL { ?block ldh:showWhenEmpty ?showWhenEmpty } } ]]> @@ -673,13 +690,31 @@ exclude-result-prefixes="#all" - - - + + + - + + + + + + + + + + + + + + + + + + + - + - + + + ldh:ontology-view-render-thunk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - ldh:ontology-view-render-thunk + ldh:ontology-view-insert @@ -738,6 +811,27 @@ exclude-result-prefixes="#all" + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl index fd06e20ca..581ad96fc 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl @@ -809,7 +809,21 @@ exclude-result-prefixes="#all" - + + + + + + +
+ +
+
+ @@ -895,6 +909,18 @@ exclude-result-prefixes="#all" + + + + + + + + + + + + @@ -2268,4 +2294,239 @@ exclude-result-prefixes="#all"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ldh:render-view-instance-modal-form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ldh:view-instance-form-response + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ldh:view-instance-link-response + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/modal.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/modal.xsl index 9e130a159..984db0b64 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/modal.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/modal.xsl @@ -718,9 +718,12 @@ LIMIT 10 + + + - - + + From 7b7c647fbda13d83ac44d13fcf3e380f0b5088b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Wed, 19 Aug 2026 01:14:08 +0200 Subject: [PATCH 2/4] Inline creation fixes: row-form rendering, cache-driven view refresh The creation modal now renders through the document-level bs2:Form pipeline over the instantiated resource (ldh:set-typeahead-form-resource), producing the full row-form UI with the form shell; the instance is minted as a fragment within the new document and the description ships as the PUT body. The post-creation refresh rebuilds the results context from the contents cache like the pager listeners (the view's spin:query RDFa is consumed by the initial render, so ldh:RenderRow re-entry cannot match). Also resets the progress cursor on terminal paths and floats the Create button (now btn-primary) left of the mandatory order-by/mode controls. Co-Authored-By: Claude Fable 5 --- .../xsl/bootstrap/2.3.2/client/block/view.xsl | 185 +++++++++++------- 1 file changed, 117 insertions(+), 68 deletions(-) diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl index 581ad96fc..baf135f1f 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl @@ -810,20 +810,6 @@ exclude-result-prefixes="#all" - - - - - -
- -
-
- @@ -895,6 +881,20 @@ exclude-result-prefixes="#all" + + + + + +
+ +
+
+

@@ -2314,16 +2314,16 @@ exclude-result-prefixes="#all" - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + ldh:render-view-instance-modal-form - + + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + - - @@ -2417,7 +2437,7 @@ exclude-result-prefixes="#all" - + @@ -2450,21 +2470,23 @@ exclude-result-prefixes="#all" + - + + - + @@ -2504,28 +2526,55 @@ exclude-result-prefixes="#all" + + + + - + + ldh:refresh-view block-id: '' matched views: + - - + + + + ldh:refresh-view block URI: cache found: + + + + + + + + + + + + + + + - + + - - - - + + + From 85a0478bc56a87254a0cacf5f2da820c322c0a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Wed, 19 Aug 2026 01:14:08 +0200 Subject: [PATCH 3/4] SKOS package views tolerate untagged prefLabels LDH forms submit plain xsd:string literals, which the hardcoded langMatches(lang(?prefLabel), "en") filters excluded from every package view. Co-Authored-By: Claude Fable 5 --- src/main/resources/com/linkeddatahub/packages/skos/ns.ttl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/com/linkeddatahub/packages/skos/ns.ttl b/src/main/resources/com/linkeddatahub/packages/skos/ns.ttl index e82cc117b..698357797 100644 --- a/src/main/resources/com/linkeddatahub/packages/skos/ns.ttl +++ b/src/main/resources/com/linkeddatahub/packages/skos/ns.ttl @@ -55,7 +55,7 @@ WHERE GRAPH ?narrowerGraph { ?narrower skos:prefLabel ?prefLabel . - FILTER (langMatches(lang(?prefLabel), "en")) + FILTER (langMatches(lang(?prefLabel), "en") || lang(?prefLabel) = "") } } } @@ -84,7 +84,7 @@ WHERE GRAPH ?broaderGraph { ?broader skos:prefLabel ?prefLabel . - FILTER (langMatches(lang(?prefLabel), "en")) + FILTER (langMatches(lang(?prefLabel), "en") || lang(?prefLabel) = "") } } } @@ -130,7 +130,7 @@ WHERE GRAPH ?memberGraph { ?member skos:prefLabel ?prefLabel . - FILTER (langMatches(lang(?prefLabel), "en")) + FILTER (langMatches(lang(?prefLabel), "en") || lang(?prefLabel) = "") } } } @@ -174,7 +174,7 @@ WHERE { GRAPH ?graph { ?concept skos:inScheme $about ; skos:prefLabel ?prefLabel . - FILTER (langMatches(lang(?prefLabel), "en")) + FILTER (langMatches(lang(?prefLabel), "en") || lang(?prefLabel) = "") } } ORDER BY ?prefLabel From 11c74daa0e58fba3805e32759af4a62d6c9f73ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Wed, 19 Aug 2026 10:57:31 +0200 Subject: [PATCH 4/4] Keep the progress cursor while the view refreshes after inline creation ldh:render-view restores the default cursor once the results render, same as the pager/order/mode interactions. Co-Authored-By: Claude Fable 5 --- .../xsl/bootstrap/2.3.2/client/block/view.xsl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl index baf135f1f..752a54ec9 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl @@ -2476,10 +2476,8 @@ exclude-result-prefixes="#all" - + - - @@ -2526,8 +2524,7 @@ exclude-result-prefixes="#all" - - +