Skip to content

Commit c84060f

Browse files
authoredMay 8, 2023
SEO features (#159)
* Replacing @data-content-uri with @about * Restored $backlink-string $about replaced with $this Passing $this to onChartQueryLoad and setting on the query string * $about => $this * Replaced ixsl:get($container, 'about') with $container/@about * Use $container/@about attribute instead of property * Fixed $query-string in onChartQueryLoad * `schema:BreadcrumbList` JSON-LD output (#157) JSON-LD output for [`https://schema.org/BreadcrumbList`](https://schema.org/BreadcrumbList) * Moved HTTP client's SSL config out of the document root folder into WEB-INF * XML sitemap generation on startup (#158) * Fixed setup config sample * Update README.md Added LDH Uploader * Fixed typo * Replacing @data-content-uri with @about * Restored $backlink-string $about replaced with $this Passing $this to onChartQueryLoad and setting on the query string * $about => $this * Replaced ixsl:get($container, 'about') with $container/@about * Use $container/@about attribute instead of property * Fixed $query-string in onChartQueryLoad * `schema:BreadcrumbList` JSON-LD output (#157) JSON-LD output for [`https://schema.org/BreadcrumbList`](https://schema.org/BreadcrumbList) * Script, SPARQL query, and XSLT stylesheet for [XML sitemap](https://www.sitemaps.org/protocol.html) generation * chmod +x on script * Optimized query * Create temp sitemap.xml file if $GENERATE_SITEMAP is true * ENV GENERATE_SITEMAP * XML sitemap generation in the entrypoint * Sitemap query checks ACL * Set $admin_endpoint_url in sitemap.rq.template * Sitemap script uses exposed Fuseki ports * Fixed $admin_endpoint_url value * Added generate-sitemap.sh "documentation" * Make sure sitemap query returns only documents only acl:Read-able by unauthenticated foaf:Agent * Changelog update
1 parent a53bc3c commit c84060f

File tree

15 files changed

+239
-64
lines changed

15 files changed

+239
-64
lines changed
 

‎CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [4.0.2] - 2023-05-08
2+
### Added
3+
- [XML sitemap](https://www.sitemaps.org/protocol.html) generation when env param `GENERATE_SITEMAP=true` is specified (enabled by default)
4+
- JSON-LD output in the `<script>` tag containing [schema:BreadCrumbList](https://schema.org/BreadcrumbList) structured data
5+
6+
### Changed
7+
- Content blocks use `@about` attributes as identifiers instead of `@data-content-uri`
8+
19
## [4.0.1] - 2023-04-23
210
### Added
311
- Backlink navigation on XHTML content

‎Dockerfile

+10-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ ENV SECRETARY_CERT_ALIAS=secretary
7878

7979
ENV CLIENT_KEYSTORE_MOUNT=/var/linkeddatahub/ssl/secretary/keystore.p12
8080

81-
ENV CLIENT_KEYSTORE="$CATALINA_HOME/webapps/ROOT/ssl/keystore.p12"
81+
ENV CLIENT_KEYSTORE="$CATALINA_HOME/webapps/ROOT/WEB-INF/keystore.p12"
8282

83-
ENV CLIENT_TRUSTSTORE="$CATALINA_HOME/webapps/ROOT/ssl/client.truststore"
83+
ENV CLIENT_TRUSTSTORE="$CATALINA_HOME/webapps/ROOT/WEB-INF/client.truststore"
8484

8585
ENV OWNER_PUBLIC_KEY=/var/linkeddatahub/ssl/owner/public.pem
8686

@@ -108,6 +108,8 @@ ENV GOOGLE_CLIENT_ID=
108108

109109
ENV GOOGLE_CLIENT_SECRET=
110110

111+
ENV GENERATE_SITEMAP=true
112+
111113
# HEALTHCHECK --start-period=80s CMD curl -f http://localhost:$HTTP_PORT || exit 1
112114

113115
# remove default Tomcat webapps and install xmlstarlet (used for XPath queries) and envsubst (for variable substitution)
@@ -144,6 +146,12 @@ COPY platform/datasets/admin.trig /var/linkeddatahub/datasets/admin.trig
144146

145147
COPY platform/datasets/end-user.trig /var/linkeddatahub/datasets/end-user.trig
146148

149+
# copy sitemap query & stylesheet
150+
151+
COPY platform/sitemap/sitemap.rq.template /var/linkeddatahub/sitemap/sitemap.rq.template
152+
153+
COPY platform/sitemap/sitemap.xsl /var/linkeddatahub/sitemap/sitemap.xsl
154+
147155
# copy webapp config
148156

149157
COPY platform/conf/ROOT.xml conf/Catalina/localhost/ROOT.xml

‎platform/entrypoint.sh

+29-9
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ readarray apps < <(xmlstarlet sel -B \
407407
-o "\" \"" \
408408
-v "srx:binding[@name = 'endUserQuadStore']" \
409409
-o "\" \"" \
410+
-v "srx:binding[@name = 'endUserEndpoint']" \
411+
-o "\" \"" \
410412
-v "srx:binding[@name = 'endUserAuthUser']" \
411413
-o "\" \"" \
412414
-v "srx:binding[@name = 'endUserAuthPwd']" \
@@ -419,6 +421,8 @@ readarray apps < <(xmlstarlet sel -B \
419421
-o "\" \"" \
420422
-v "srx:binding[@name = 'adminQuadStore']" \
421423
-o "\" \"" \
424+
-v "srx:binding[@name = 'adminEndpoint']" \
425+
-o "\" \"" \
422426
-v "srx:binding[@name = 'adminAuthUser']" \
423427
-o "\" \"" \
424428
-v "srx:binding[@name = 'adminAuthPwd']" \
@@ -433,15 +437,17 @@ for app in "${apps[@]}"; do
433437
end_user_app="${app_array[0]//\"/}"
434438
end_user_base_uri="${app_array[1]//\"/}"
435439
end_user_quad_store_url="${app_array[2]//\"/}"
436-
end_user_service_auth_user="${app_array[3]//\"/}"
437-
end_user_service_auth_pwd="${app_array[4]//\"/}"
438-
end_user_owner="${app_array[5]//\"/}"
439-
admin_app="${app_array[6]//\"/}"
440-
admin_base_uri="${app_array[7]//\"/}"
441-
admin_quad_store_url="${app_array[8]//\"/}"
442-
admin_service_auth_user="${app_array[9]//\"/}"
443-
admin_service_auth_pwd="${app_array[10]//\"/}"
444-
admin_owner="${app_array[11]//\"/}"
440+
end_user_endpoint_url="${app_array[3]//\"/}"
441+
end_user_service_auth_user="${app_array[4]//\"/}"
442+
end_user_service_auth_pwd="${app_array[5]//\"/}"
443+
end_user_owner="${app_array[6]//\"/}"
444+
admin_app="${app_array[7]//\"/}"
445+
admin_base_uri="${app_array[8]//\"/}"
446+
admin_quad_store_url="${app_array[9]//\"/}"
447+
admin_endpoint_url="${app_array[10]//\"/}"
448+
admin_service_auth_user="${app_array[11]//\"/}"
449+
admin_service_auth_pwd="${app_array[12]//\"/}"
450+
admin_owner="${app_array[13]//\"/}"
445451

446452
printf "\n### Processing dataspace. End-user app: %s Admin app: %s\n" "$end_user_app" "$admin_app"
447453

@@ -625,6 +631,20 @@ if [ -n "$OIDC_REFRESH_TOKENS" ] && [ ! -f "$OIDC_REFRESH_TOKENS" ]; then
625631
touch "$OIDC_REFRESH_TOKENS"
626632
fi
627633

634+
# if configured, generate XML sitemap: https://www.sitemaps.org/protocol.html
635+
636+
if [ "$GENERATE_SITEMAP" = true ]; then
637+
export admin_endpoint_url
638+
envsubst < /var/linkeddatahub/sitemap/sitemap.rq.template > /var/linkeddatahub/sitemap/sitemap.rq
639+
sitemap_results=$(mktemp)
640+
641+
curl -k -G -H "Accept: application/sparql-results+xml" "$end_user_endpoint_url" --data-urlencode "query@/var/linkeddatahub/sitemap/sitemap.rq" -o "$sitemap_results"
642+
643+
xsltproc --output "${PWD}/webapps/ROOT/sitemap.xml" /var/linkeddatahub/sitemap/sitemap.xsl "$sitemap_results"
644+
645+
rm "$sitemap_results"
646+
fi
647+
628648
# change context configuration
629649

630650
BASE_URI_PARAM="--stringparam ldhc:baseUri '$BASE_URI' "

‎platform/select-root-services.rq

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ PREFIX a: <https://w3id.org/atomgraph/core#>
44
PREFIX lapp: <https://w3id.org/atomgraph/linkeddatahub/apps#>
55
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
66

7-
SELECT ?endUserApp ?endUserBase ?endUserQuadStore ?endUserAuthUser ?endUserAuthPwd ?endUserMaker ?adminApp ?adminBase ?adminQuadStore ?adminAuthUser ?adminAuthPwd ?adminMaker
7+
SELECT ?endUserApp ?endUserBase ?endUserQuadStore ?endUserEndpoint ?endUserAuthUser ?endUserAuthPwd ?endUserMaker ?adminApp ?adminBase ?adminQuadStore ?adminEndpoint ?adminAuthUser ?adminAuthPwd ?adminMaker
88
{
99
?endUserApp ldt:base ?endUserBase ;
1010
ldt:service ?endUserService ;
1111
lapp:adminApplication ?adminApp .
1212
?adminApp ldt:service ?adminService ;
1313
ldt:base ?adminBase .
14-
?endUserService a:quadStore ?endUserQuadStore .
15-
?adminService a:quadStore ?adminQuadStore .
14+
?endUserService a:quadStore ?endUserQuadStore ;
15+
sd:endpoint ?endUserEndpoint .
16+
?adminService a:quadStore ?adminQuadStore ;
17+
sd:endpoint ?adminEndpoint .
1618
OPTIONAL
1719
{
1820
?endUserService a:authUser ?endUserAuthUser ;

‎platform/sitemap/sitemap.rq.template

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
2+
PREFIX acl: <http://www.w3.org/ns/auth/acl#>
3+
PREFIX dct: <http://purl.org/dc/terms/>
4+
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
5+
6+
SELECT ?loc ?lastmod
7+
WHERE
8+
{ GRAPH ?loc
9+
{ ?loc a ?Type
10+
SERVICE <$admin_endpoint_url>
11+
{ GRAPH ?authGraph
12+
{ ?auth acl:mode acl:Read ;
13+
acl:agentClass foaf:Agent
14+
{ ?auth acl:accessTo ?loc }
15+
UNION
16+
{ { ?auth acl:accessToClass ?Type }
17+
UNION
18+
{ ?auth acl:accessToClass ?Class .
19+
?Type (rdfs:subClassOf)* ?Class
20+
}
21+
}
22+
}
23+
}
24+
OPTIONAL
25+
{ ?loc dct:created ?created }
26+
OPTIONAL
27+
{ ?loc dct:modified ?modified }
28+
BIND(coalesce(?modified, ?created) AS ?lastmod)
29+
}
30+
}

‎platform/sitemap/sitemap.xsl

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsl:stylesheet version="1.0"
3+
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
4+
xmlns:srx="http://www.w3.org/2005/sparql-results#"
5+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
6+
>
7+
8+
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
9+
10+
<xsl:template match="/srx:sparql">
11+
<urlset>
12+
<xsl:apply-templates/>
13+
</urlset>
14+
</xsl:template>
15+
16+
<xsl:template match="srx:head"/>
17+
18+
<xsl:template match="srx:results">
19+
<xsl:apply-templates/>
20+
</xsl:template>
21+
22+
<xsl:template match="srx:result">
23+
<url>
24+
<xsl:apply-templates/>
25+
</url>
26+
</xsl:template>
27+
28+
<xsl:template match="srx:binding[@name = 'loc'][srx:uri]">
29+
<loc>
30+
<xsl:value-of select="srx:uri"/>
31+
</loc>
32+
</xsl:template>
33+
34+
<xsl:template match="srx:binding[@name = 'lastmod'][srx:literal/@datatype = 'http://www.w3.org/2001/XMLSchema#dateTime']">
35+
<lastmod>
36+
<xsl:value-of select="srx:literal"/>
37+
</lastmod>
38+
</xsl:template>
39+
40+
</xsl:stylesheet>

‎scripts/sitemap/generate-sitemap.sh

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This script directly queries the fuseki-end-user endpoint exposed on localhost:3030. You can configure in docker-compose.override.yml like this:
2+
# fuseki-end-user:
3+
# ports:
4+
# - 3031:3030
5+
6+
end_user_endpoint="http://localhost:3031/ds"
7+
export admin_endpoint_url="http://fuseki-admin:3030/ds"
8+
9+
envsubst < ../../platform/sitemap/sitemap.rq.template > sitemap.rq
10+
11+
curl -k -G -H "Accept: application/sparql-results+xml" "$end_user_endpoint" --data-urlencode "query@sitemap.rq" -o results.xml
12+
13+
docker run --rm -v "$PWD/../../platform/sitemap/sitemap.xsl":"/transform/sitemap.xsl" -v "$PWD/results.xml":"/transform/results.xml" atomgraph/saxon -s:/transform/results.xml -xsl:/transform/sitemap.xsl

‎src/main/webapp/WEB-INF/web.xml

+1
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ support@atomgraph.com]]></param-value>
357357
<url-pattern>/static/*</url-pattern>
358358
<url-pattern>/robots.txt</url-pattern>
359359
<url-pattern>/favicon.ico</url-pattern>
360+
<url-pattern>/sitemap.xml</url-pattern>
360361
</servlet-mapping>
361362
<servlet-mapping>
362363
<servlet-name>com.atomgraph.linkeddatahub.Application</servlet-name>

‎src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/chart.xsl

+8-5
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ exclude-result-prefixes="#all"
169169
<!-- chart content -->
170170
<xsl:template match="*[@rdf:about][spin:query/@rdf:resource][ldh:chartType/@rdf:resource]" mode="ldh:RenderContent" priority="1">
171171
<xsl:param name="container" as="element()"/>
172-
<xsl:param name="about" as="xs:anyURI"/>
173-
<xsl:param name="content-uri" select="xs:anyURI(($container/@about, ixsl:get($container, 'dataset.contentUri'))[1])" as="xs:anyURI"/>
172+
<xsl:param name="this" as="xs:anyURI"/>
173+
<xsl:param name="content-uri" select="xs:anyURI($container/@about)" as="xs:anyURI"/>
174174
<xsl:variable name="query-uri" select="xs:anyURI(spin:query/@rdf:resource)" as="xs:anyURI"/>
175175
<xsl:variable name="chart-type" select="xs:anyURI(ldh:chartType/@rdf:resource)" as="xs:anyURI?"/>
176176
<xsl:variable name="category" select="ldh:categoryProperty/@rdf:resource | ldh:categoryVarName" as="xs:string?"/>
@@ -184,6 +184,7 @@ exclude-result-prefixes="#all"
184184
<xsl:variable name="request" as="item()*">
185185
<ixsl:schedule-action http-request="map{ 'method': 'GET', 'href': $request-uri, 'headers': map{ 'Accept': 'application/rdf+xml' } }">
186186
<xsl:call-template name="onChartQueryLoad">
187+
<xsl:with-param name="this" select="$this"/>
187188
<xsl:with-param name="content-uri" select="$content-uri"/>
188189
<xsl:with-param name="query-uri" select="$query-uri"/>
189190
<xsl:with-param name="chart-type" select="$chart-type"/>
@@ -212,7 +213,7 @@ exclude-result-prefixes="#all"
212213
</xsl:for-each>
213214
</xsl:variable>
214215
<xsl:variable name="container" select="ancestor::div[@about][1]" as="element()?"/>
215-
<xsl:variable name="content-uri" select="xs:anyURI(($container/@about, ixsl:get($container, 'dataset.contentUri'))[1])" as="xs:anyURI"/>
216+
<xsl:variable name="content-uri" select="xs:anyURI($container/@about)" as="xs:anyURI"/>
216217
<xsl:variable name="chart-canvas-id" select="ancestor::form/following-sibling::div/@id" as="xs:string"/>
217218
<xsl:variable name="results" select="if (ixsl:contains(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'results')) then ixsl:get(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'results') else root(ixsl:get(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'content'))" as="document-node()"/>
218219

@@ -244,7 +245,7 @@ exclude-result-prefixes="#all"
244245
</xsl:for-each>
245246
</xsl:variable>
246247
<xsl:variable name="container" select="ancestor::div[@about][1]" as="element()?"/>
247-
<xsl:variable name="content-uri" select="xs:anyURI(($container/@about, ixsl:get($container, 'dataset.contentUri'))[1])" as="xs:anyURI"/>
248+
<xsl:variable name="content-uri" select="xs:anyURI($container/@about)" as="xs:anyURI"/>
248249
<xsl:variable name="chart-canvas-id" select="ancestor::form/following-sibling::div/@id" as="xs:string"/>
249250
<xsl:variable name="results" select="if (ixsl:contains(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'results')) then ixsl:get(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'results') else root(ixsl:get(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'content'))" as="document-node()"/>
250251

@@ -274,7 +275,7 @@ exclude-result-prefixes="#all"
274275
</xsl:for-each>
275276
</xsl:variable>
276277
<xsl:variable name="container" select="ancestor::div[@about][1]" as="element()?"/>
277-
<xsl:variable name="content-uri" select="xs:anyURI(($container/@about, ixsl:get($container, 'dataset.contentUri'))[1])" as="xs:anyURI"/>
278+
<xsl:variable name="content-uri" select="xs:anyURI($container/@about)" as="xs:anyURI"/>
278279
<xsl:variable name="chart-canvas-id" select="ancestor::form/following-sibling::div/@id" as="xs:string"/>
279280
<xsl:variable name="results" select="if (ixsl:contains(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'results')) then ixsl:get(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'results') else root(ixsl:get(ixsl:get(ixsl:get(ixsl:window(), 'LinkedDataHub.contents'), '`' || $content-uri || '`'), 'content'))" as="document-node()"/>
280281

@@ -336,6 +337,7 @@ exclude-result-prefixes="#all"
336337
<xsl:template name="onChartQueryLoad">
337338
<xsl:context-item as="map(*)" use="required"/>
338339
<xsl:param name="container" as="element()"/>
340+
<xsl:param name="this" as="xs:anyURI"/>
339341
<xsl:param name="content-uri" as="xs:anyURI"/>
340342
<xsl:param name="query-uri" as="xs:anyURI"/>
341343
<xsl:param name="chart-type" as="xs:anyURI"/>
@@ -348,6 +350,7 @@ exclude-result-prefixes="#all"
348350
<xsl:for-each select="?body">
349351
<xsl:variable name="query-type" select="xs:anyURI(key('resources', $query-uri)/rdf:type/@rdf:resource)" as="xs:anyURI"/>
350352
<xsl:variable name="query-string" select="key('resources', $query-uri)/sp:text" as="xs:string"/>
353+
<xsl:variable name="query-string" select="replace($query-string, '$this', '&lt;' || $this || '&gt;', 'q')" as="xs:string"/>
351354
<!-- TO-DO: use SPARQLBuilder to set LIMIT -->
352355
<!--<xsl:variable name="query-string" select="concat($query-string, ' LIMIT 100')" as="xs:string"/>-->
353356
<xsl:variable name="service-uri" select="xs:anyURI(key('resources', $query-uri)/ldh:service/@rdf:resource)" as="xs:anyURI?"/>

0 commit comments

Comments
 (0)
Please sign in to comment.