@lblod/ember-rdfa-editor-lblod-plugins 32.0.0 → 32.1.0-dev.2e9fd650499765c0030b9262f90aa975fdfd7a2f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/empty-donkeys-change.md +5 -0
- package/.changeset/tidy-dancers-grow.md +5 -0
- package/.changeset/wicked-tools-serve.md +5 -0
- package/CHANGELOG.md +11 -0
- package/addon/components/citation-plugin/citations/search-modal.hbs +2 -2
- package/addon/components/document-validation-plugin/card.gts +131 -0
- package/addon/components/roadsign-regulation-plugin/roadsigns-table.gts +5 -2
- package/addon/components/snippet-plugin/nodes/snippet.gts +1 -1
- package/addon/components/structure-plugin/_private/structure.gts +1 -3
- package/addon/components/template-comments-plugin/template-comment.hbs +1 -3
- package/addon/components/worship-plugin/administrative-unit-picker.hbs +3 -3
- package/addon/components/worship-plugin/search-modal.hbs +4 -4
- package/addon/plugins/document-validation-plugin/index.ts +150 -0
- package/addon/plugins/roadsign-regulation-plugin/actions/insert-measure.ts +22 -2
- package/addon/plugins/roadsign-regulation-plugin/constants.ts +22 -6
- package/addon/plugins/roadsign-regulation-plugin/queries/road-sign-category.ts +6 -1
- package/addon/plugins/roadsign-regulation-plugin/queries/sign-concept.ts +19 -13
- package/addon/plugins/roadsign-regulation-plugin/schemas/sign-concept.ts +22 -8
- package/addon/plugins/snippet-plugin/nodes/snippet.ts +1 -0
- package/addon/plugins/structure-plugin/node.ts +1 -0
- package/addon/plugins/template-comments-plugin/node.ts +1 -0
- package/addon/plugins/variable-plugin/variables/date.ts +1 -0
- package/addon/utils/remove-quotes.ts +7 -0
- package/app/components/document-validation-plugin/card.js +1 -0
- package/app/styles/document-validation.scss +24 -0
- package/app/styles/snippet-plugin.scss +7 -1
- package/app/styles/structure-plugin.scss +2 -1
- package/app/styles/template-comments-plugin.scss +1 -1
- package/declarations/addon/components/document-validation-plugin/card.d.ts +18 -0
- package/declarations/addon/components/roadsign-regulation-plugin/roadsigns-modal.d.ts +20 -8
- package/declarations/addon/plugins/document-validation-plugin/index.d.ts +14 -0
- package/declarations/addon/plugins/roadsign-regulation-plugin/constants.d.ts +11 -5
- package/declarations/addon/plugins/roadsign-regulation-plugin/queries/mobility-measure-concept.d.ts +10 -4
- package/declarations/addon/plugins/roadsign-regulation-plugin/queries/road-sign-category.d.ts +1 -0
- package/declarations/addon/plugins/roadsign-regulation-plugin/queries/sign-concept.d.ts +14 -4
- package/declarations/addon/plugins/roadsign-regulation-plugin/schemas/mobility-measure-concept.d.ts +52 -21
- package/declarations/addon/plugins/roadsign-regulation-plugin/schemas/sign-concept.d.ts +32 -13
- package/declarations/addon/utils/remove-quotes.d.ts +1 -0
- package/package.json +8 -4
- package/pnpm-lock.yaml +1530 -1166
- package/translations/en-US.yaml +15 -7
- package/translations/nl-BE.yaml +17 -9
- package/types/global.d.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @lblod/ember-rdfa-editor-lblod-plugins
|
|
2
2
|
|
|
3
|
+
## 32.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#573](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/573) [`be9e2f4`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/be9e2f476266037926f43f2729aae7e969c55652) Thanks [@elpoelma](https://github.com/elpoelma)! - Roadsign-regulation-plugin: differentiate between 'Verkeersbord' and 'Onderbord' signs
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#576](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/576) [`c68bef3`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/c68bef3a3e215e0d721894b72f8e0754b9988ca9) Thanks [@elpoelma](https://github.com/elpoelma)! - Slightly readjust styling of `snippet` and `structure` nodes, to ensure better cursor behaviour in firefox.
|
|
12
|
+
Note: for the better cursor behaviour in firefox to work as expected, `@lblod/ember-rdfa-editor` version [12.8.0](https://github.com/lblod/ember-rdfa-editor/releases/tag/%40lblod%2Fember-rdfa-editor%4012.8.0) or higher needs to be installed.
|
|
13
|
+
|
|
3
14
|
## 32.0.0
|
|
4
15
|
|
|
5
16
|
### Major Changes
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
@allowClear={{false}}
|
|
39
39
|
@disabled={{false}}
|
|
40
40
|
@searchEnabled={{true}}
|
|
41
|
-
@loadingMessage={{t '
|
|
42
|
-
@noMatchesMessage={{t '
|
|
41
|
+
@loadingMessage={{t 'common.loading'}}
|
|
42
|
+
@noMatchesMessage={{t 'common.search.no-results'}}
|
|
43
43
|
@searchMessage={{t 'citaten-plugin.search.placeholder'}}
|
|
44
44
|
@options={{this.legislationTypes}}
|
|
45
45
|
@selected={{this.legislationSelected}}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { SayController } from '@lblod/ember-rdfa-editor';
|
|
3
|
+
import AuCard from '@appuniversum/ember-appuniversum/components/au-card';
|
|
4
|
+
import { documentValidationPluginKey } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/document-validation-plugin';
|
|
5
|
+
import AuIcon from '@appuniversum/ember-appuniversum/components/au-icon';
|
|
6
|
+
import { CloseFilledIcon } from '@appuniversum/ember-appuniversum/components/icons/close-filled';
|
|
7
|
+
import { CheckFilledIcon } from '@appuniversum/ember-appuniversum/components/icons/check-filled';
|
|
8
|
+
import { selectNodeBySubject } from '@lblod/ember-rdfa-editor/commands/_private/rdfa-commands/select-node-by-subject';
|
|
9
|
+
import { on } from '@ember/modifier';
|
|
10
|
+
import { fn } from '@ember/helper';
|
|
11
|
+
import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
|
|
12
|
+
import { ExternalLinkIcon } from '@appuniversum/ember-appuniversum/components/icons/external-link';
|
|
13
|
+
import removeQuotes from '@lblod/ember-rdfa-editor-lblod-plugins/utils/remove-quotes';
|
|
14
|
+
import t from 'ember-intl/helpers/t';
|
|
15
|
+
import type { ShaclValidationReport } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/document-validation-plugin';
|
|
16
|
+
import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
|
|
17
|
+
|
|
18
|
+
interface Sig {
|
|
19
|
+
Args: {
|
|
20
|
+
controller: SayController;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default class DocumentValidationPluginCard extends Component<Sig> {
|
|
25
|
+
get documentValidationErrors() {
|
|
26
|
+
const { report } = documentValidationPluginKey.getState(
|
|
27
|
+
this.controller.mainEditorView.state,
|
|
28
|
+
);
|
|
29
|
+
if (!report) return undefined;
|
|
30
|
+
return shaclReportToErrorArray(report);
|
|
31
|
+
}
|
|
32
|
+
get propertiesWithoutErrors() {
|
|
33
|
+
const { propertiesWithoutErrors } = documentValidationPluginKey.getState(
|
|
34
|
+
this.controller.mainEditorView.state,
|
|
35
|
+
);
|
|
36
|
+
return propertiesWithoutErrors;
|
|
37
|
+
}
|
|
38
|
+
get controller() {
|
|
39
|
+
return this.args.controller;
|
|
40
|
+
}
|
|
41
|
+
goToSubject = (subject: string) => {
|
|
42
|
+
this.controller.doCommand(selectNodeBySubject({ subject }), {
|
|
43
|
+
view: this.controller.mainEditorView,
|
|
44
|
+
});
|
|
45
|
+
this.controller.focus();
|
|
46
|
+
};
|
|
47
|
+
get isValidDocument() {
|
|
48
|
+
return this.documentValidationErrors?.length === 0;
|
|
49
|
+
}
|
|
50
|
+
<template>
|
|
51
|
+
{{#if this.documentValidationErrors}}
|
|
52
|
+
<AuCard
|
|
53
|
+
@flex={{true}}
|
|
54
|
+
@divided={{true}}
|
|
55
|
+
@isOpenInitially={{true}}
|
|
56
|
+
@expandable={{true}}
|
|
57
|
+
@shadow={{true}}
|
|
58
|
+
@size='small'
|
|
59
|
+
class={{if
|
|
60
|
+
this.isValidDocument
|
|
61
|
+
'say-document-validation__card-valid'
|
|
62
|
+
'say-document-validation__card-invalid'
|
|
63
|
+
}}
|
|
64
|
+
as |c|
|
|
65
|
+
>
|
|
66
|
+
<c.header>
|
|
67
|
+
<p class='au-u-medium au-u-h6'>
|
|
68
|
+
{{#if this.isValidDocument}}
|
|
69
|
+
{{t 'document-validation-plugin.valid-document-title'}}
|
|
70
|
+
{{else}}
|
|
71
|
+
{{t 'document-validation-plugin.invalid-document-title'}}
|
|
72
|
+
{{/if}}
|
|
73
|
+
</p>
|
|
74
|
+
</c.header>
|
|
75
|
+
<c.content>
|
|
76
|
+
<p class='au-u-medium au-u-para-small'>{{t
|
|
77
|
+
'document-validation-plugin.description'
|
|
78
|
+
}}</p>
|
|
79
|
+
{{#each this.propertiesWithoutErrors as |property|}}
|
|
80
|
+
<div class='say-document-validation__error-container'>
|
|
81
|
+
<AuIcon
|
|
82
|
+
@icon={{CheckFilledIcon}}
|
|
83
|
+
@size='large'
|
|
84
|
+
@ariaHidden={{true}}
|
|
85
|
+
class='say-document-validation__icon-success au-u-margin-right-small'
|
|
86
|
+
/>
|
|
87
|
+
{{property.message}}
|
|
88
|
+
</div>
|
|
89
|
+
{{/each}}
|
|
90
|
+
{{#each this.documentValidationErrors as |error|}}
|
|
91
|
+
<div class='say-document-validation__error-container'>
|
|
92
|
+
<AuIcon
|
|
93
|
+
@icon={{CloseFilledIcon}}
|
|
94
|
+
@size='large'
|
|
95
|
+
@ariaHidden={{true}}
|
|
96
|
+
class='say-document-validation__icon-error au-u-margin-right-small'
|
|
97
|
+
/>
|
|
98
|
+
{{error.message}}
|
|
99
|
+
<AuButton
|
|
100
|
+
class='au-u-padding-left-none au-u-padding-right-none'
|
|
101
|
+
@icon={{ExternalLinkIcon}}
|
|
102
|
+
@skin='link'
|
|
103
|
+
title={{error.subject}}
|
|
104
|
+
{{on 'click' (fn this.goToSubject error.subject)}}
|
|
105
|
+
>{{t 'document-validation-plugin.see-related-node'}}</AuButton>
|
|
106
|
+
</div>
|
|
107
|
+
{{/each}}
|
|
108
|
+
</c.content>
|
|
109
|
+
|
|
110
|
+
</AuCard>
|
|
111
|
+
{{/if}}
|
|
112
|
+
</template>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function shaclReportToErrorArray(report: ShaclValidationReport) {
|
|
116
|
+
let errorArray = [];
|
|
117
|
+
const factory = new SayDataFactory();
|
|
118
|
+
for (const r of report.results) {
|
|
119
|
+
const match = [
|
|
120
|
+
...r.dataset.match(
|
|
121
|
+
r.sourceShape,
|
|
122
|
+
factory.namedNode('http://www.w3.org/ns/shacl#resultMessage'),
|
|
123
|
+
),
|
|
124
|
+
][0];
|
|
125
|
+
errorArray.push({
|
|
126
|
+
message: removeQuotes(match.object.value),
|
|
127
|
+
subject: r.focusNode?.value,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return errorArray;
|
|
131
|
+
}
|
|
@@ -19,6 +19,7 @@ import { eq } from 'ember-truth-helpers';
|
|
|
19
19
|
import t from 'ember-intl/helpers/t';
|
|
20
20
|
import { on } from '@ember/modifier';
|
|
21
21
|
import { fn } from '@ember/helper';
|
|
22
|
+
import { SIGN_CONCEPT_TYPES } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/roadsign-regulation-plugin/constants';
|
|
22
23
|
|
|
23
24
|
type Signature = {
|
|
24
25
|
Args: {
|
|
@@ -44,8 +45,10 @@ export default class RoadSignsTable extends Component<Signature> {
|
|
|
44
45
|
categories = (measureConcept: MobilityMeasureConcept) => {
|
|
45
46
|
const categorySet: Set<string> = new Set();
|
|
46
47
|
for (const signConcept of measureConcept.signConcepts) {
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
if (signConcept.type === SIGN_CONCEPT_TYPES.ROAD_SIGN) {
|
|
49
|
+
const categoryLabels = signConcept.categories.map((cat) => cat.label);
|
|
50
|
+
addAll(categorySet, ...categoryLabels);
|
|
51
|
+
}
|
|
49
52
|
}
|
|
50
53
|
return [...categorySet].sort();
|
|
51
54
|
};
|
|
@@ -226,7 +226,7 @@ export default class SnippetNode extends Component<Signature> {
|
|
|
226
226
|
</span>
|
|
227
227
|
{{this.node.attrs.title}}
|
|
228
228
|
</div>
|
|
229
|
-
<div class='say-snippet-
|
|
229
|
+
<div class='say-snippet-body'>
|
|
230
230
|
{{yield}}
|
|
231
231
|
<div class='say-snippet-icons' contenteditable='false'>
|
|
232
232
|
<SnippetButton
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{{! @glint-nocheck: not typesafe yet }}
|
|
2
2
|
<PowerSelect
|
|
3
3
|
@loadingMessage={{t 'common.search.loading'}}
|
|
4
|
-
@searchMessage={{t 'worship-plugin.modal.
|
|
5
|
-
@noMatchesMessage={{t 'worship-plugin.modal.
|
|
6
|
-
@
|
|
4
|
+
@searchMessage={{t 'worship-plugin.modal.fields.admin-unit.search'}}
|
|
5
|
+
@noMatchesMessage={{t 'worship-plugin.modal.fields.admin-unit.no-results'}}
|
|
6
|
+
@placeholder={{t 'worship-plugin.modal.fields.admin-unit.placeholder'}}
|
|
7
7
|
@allowClear={{true}}
|
|
8
8
|
@renderInPlace={{true}}
|
|
9
9
|
@searchEnabled={{true}}
|
|
@@ -18,21 +18,21 @@
|
|
|
18
18
|
<div class='au-c-sidebar'>
|
|
19
19
|
<div class='au-c-sidebar__content au-u-padding'>
|
|
20
20
|
<AuHeading @level='3' @skin='4' class='au-u-padding-bottom-small'>
|
|
21
|
-
{{t 'worship-plugin.modal.
|
|
21
|
+
{{t 'worship-plugin.modal.filter-on'}}
|
|
22
22
|
</AuHeading>
|
|
23
23
|
<AuLabel class='au-margin-bottom-small' for='searchTerm'>
|
|
24
|
-
{{t 'worship-plugin.modal.fields.name'}}
|
|
24
|
+
{{t 'worship-plugin.modal.fields.name.label'}}
|
|
25
25
|
</AuLabel>
|
|
26
26
|
<AuNativeInput
|
|
27
27
|
@type='text'
|
|
28
28
|
@width='block'
|
|
29
29
|
id='searchTerm'
|
|
30
30
|
value={{this.searchText}}
|
|
31
|
-
placeholder={{t 'worship-plugin.modal.
|
|
31
|
+
placeholder={{t 'worship-plugin.modal.fields.name.placeholder'}}
|
|
32
32
|
{{on 'input' this.setInputSearchText}}
|
|
33
33
|
/>
|
|
34
34
|
<AuLabel class='au-margin-bottom-small' for='admin-unit-select'>
|
|
35
|
-
{{t 'worship-plugin.modal.fields.admin-unit'}}
|
|
35
|
+
{{t 'worship-plugin.modal.fields.admin-unit.label'}}
|
|
36
36
|
</AuLabel>
|
|
37
37
|
<WorshipPlugin::AdministrativeUnitPicker
|
|
38
38
|
id='admin-unit-select'
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import factory from '@rdfjs/dataset';
|
|
2
|
+
import SHACLValidator from 'rdf-validate-shacl';
|
|
3
|
+
import { Parser as ParserN3 } from 'n3';
|
|
4
|
+
import { RdfaParser } from 'rdfa-streaming-parser';
|
|
5
|
+
import { ProsePlugin, PluginKey, EditorView } from '@lblod/ember-rdfa-editor';
|
|
6
|
+
import removeQuotes from '@lblod/ember-rdfa-editor-lblod-plugins/utils/remove-quotes';
|
|
7
|
+
import {
|
|
8
|
+
DataFactory,
|
|
9
|
+
DatasetCore,
|
|
10
|
+
DatasetCoreFactory,
|
|
11
|
+
Quad,
|
|
12
|
+
} from '@rdfjs/types';
|
|
13
|
+
import ValidationReport from 'rdf-validate-shacl/src/validation-report';
|
|
14
|
+
import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
|
|
15
|
+
|
|
16
|
+
export const documentValidationPluginKey = new PluginKey('DOCUMENT_VALIDATION');
|
|
17
|
+
|
|
18
|
+
interface DocumentValidationPluginArgs {
|
|
19
|
+
documentShape: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ShaclValidationReport = ValidationReport.ValidationReport<
|
|
23
|
+
DataFactory<Quad, Quad> &
|
|
24
|
+
DatasetCoreFactory<Quad, Quad, DatasetCore<Quad, Quad>>
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
interface documentValidationTransactionMeta {
|
|
28
|
+
type: string;
|
|
29
|
+
report: ValidationReport;
|
|
30
|
+
propertiesWithoutErrors: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const documentValidationPlugin = (
|
|
34
|
+
options: DocumentValidationPluginArgs,
|
|
35
|
+
) =>
|
|
36
|
+
new ProsePlugin({
|
|
37
|
+
key: documentValidationPluginKey,
|
|
38
|
+
state: {
|
|
39
|
+
init() {
|
|
40
|
+
return {
|
|
41
|
+
validationCallback: validationCallback,
|
|
42
|
+
documentShape: options.documentShape,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
apply(tr, state) {
|
|
46
|
+
const pluginTransaction = tr.getMeta(documentValidationPluginKey) as
|
|
47
|
+
| documentValidationTransactionMeta
|
|
48
|
+
| undefined;
|
|
49
|
+
if (pluginTransaction) {
|
|
50
|
+
if (pluginTransaction.type === 'setNewReport') {
|
|
51
|
+
return {
|
|
52
|
+
...state,
|
|
53
|
+
report: pluginTransaction.report,
|
|
54
|
+
propertiesWithoutErrors:
|
|
55
|
+
pluginTransaction.propertiesWithoutErrors,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return state;
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
async function validationCallback(view: EditorView, documentHtml: string) {
|
|
66
|
+
const { documentShape } = documentValidationPluginKey.getState(view.state);
|
|
67
|
+
const rdf = await htmlToRdf(documentHtml);
|
|
68
|
+
const shacl = await parse(documentShape);
|
|
69
|
+
|
|
70
|
+
const validator = new SHACLValidator(shacl, {
|
|
71
|
+
// @ts-expect-error ts doesn't recognize the configuration parameter not sure why
|
|
72
|
+
allowNamedNodeInList: true,
|
|
73
|
+
});
|
|
74
|
+
const report = validator.validate(rdf);
|
|
75
|
+
const sayFactory = new SayDataFactory();
|
|
76
|
+
const propertyPred = sayFactory.namedNode(
|
|
77
|
+
'http://www.w3.org/ns/shacl#property',
|
|
78
|
+
);
|
|
79
|
+
const propertyNodes = [
|
|
80
|
+
...shacl.match(undefined, propertyPred, undefined),
|
|
81
|
+
].map((quad: Quad) => quad.object);
|
|
82
|
+
|
|
83
|
+
const propertiesWithErrors: string[] = [];
|
|
84
|
+
for (const r of report.results) {
|
|
85
|
+
const shapeId = r.sourceShape?.value;
|
|
86
|
+
if (shapeId) propertiesWithErrors.push(shapeId);
|
|
87
|
+
}
|
|
88
|
+
const propertiesWithoutErrorsArray = propertyNodes.filter(
|
|
89
|
+
(term) => !propertiesWithErrors.includes(term.value),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const successMessagePred = sayFactory.namedNode(
|
|
93
|
+
'http://mu.semte.ch/vocabularies/ext/successMessage',
|
|
94
|
+
);
|
|
95
|
+
const propertiesWithoutErrors = propertiesWithoutErrorsArray
|
|
96
|
+
.map((propertyNode) => {
|
|
97
|
+
const match = shacl.match(propertyNode, successMessagePred, undefined);
|
|
98
|
+
const message = [...match][0]?.object.value;
|
|
99
|
+
return message ? { message: removeQuotes(message) } : undefined;
|
|
100
|
+
})
|
|
101
|
+
.filter((message) => message);
|
|
102
|
+
const transaction = view.state.tr;
|
|
103
|
+
transaction.setMeta(documentValidationPluginKey, {
|
|
104
|
+
type: 'setNewReport',
|
|
105
|
+
report,
|
|
106
|
+
propertiesWithoutErrors,
|
|
107
|
+
});
|
|
108
|
+
transaction.setMeta('addToHistory', false);
|
|
109
|
+
view.dispatch(transaction);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface N3Parser {
|
|
113
|
+
parse: (
|
|
114
|
+
triples: string,
|
|
115
|
+
callback: (error: string, quad: Quad) => void,
|
|
116
|
+
) => void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function parse(triples: string): Promise<DatasetCore<Quad>> {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
// @ts-expect-error we have to use a custom type to make the quads compatible or else ts will complain
|
|
122
|
+
const parser: N3Parser = new ParserN3();
|
|
123
|
+
const dataset = factory.dataset();
|
|
124
|
+
parser.parse(triples, (error, quad) => {
|
|
125
|
+
if (error) {
|
|
126
|
+
console.warn(error);
|
|
127
|
+
reject(error);
|
|
128
|
+
} else if (quad) {
|
|
129
|
+
dataset.add(quad);
|
|
130
|
+
} else {
|
|
131
|
+
resolve(dataset);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function htmlToRdf(html: string): Promise<DatasetCore<Quad>> {
|
|
138
|
+
return new Promise((res, rej) => {
|
|
139
|
+
const myParser = new RdfaParser({ contentType: 'text/html' });
|
|
140
|
+
const dataset = factory.dataset();
|
|
141
|
+
myParser
|
|
142
|
+
.on('data', (data) => {
|
|
143
|
+
dataset.add(data);
|
|
144
|
+
})
|
|
145
|
+
.on('error', rej)
|
|
146
|
+
.on('end', () => res(dataset));
|
|
147
|
+
myParser.write(html);
|
|
148
|
+
myParser.end();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -25,7 +25,8 @@ import { buildArticleStructure } from '../../decision-plugin/utils/build-article
|
|
|
25
25
|
import { insertArticle } from '../../decision-plugin/actions/insert-article';
|
|
26
26
|
import { SignConcept } from '../schemas/sign-concept';
|
|
27
27
|
import {
|
|
28
|
-
|
|
28
|
+
ROAD_SIGN_CATEGORIES,
|
|
29
|
+
SIGN_CONCEPT_TYPES,
|
|
29
30
|
SIGN_TYPE_MAPPING,
|
|
30
31
|
SIGN_TYPES,
|
|
31
32
|
ZONALITY_OPTIONS,
|
|
@@ -190,9 +191,28 @@ function constructMeasureBody(
|
|
|
190
191
|
return schema.nodes.paragraph.create({}, nodes);
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
function determineSignLabel(signConcept: SignConcept) {
|
|
195
|
+
switch (signConcept.type) {
|
|
196
|
+
case SIGN_CONCEPT_TYPES.TRAFFIC_LIGHT:
|
|
197
|
+
return 'Verkeerslicht';
|
|
198
|
+
case SIGN_CONCEPT_TYPES.ROAD_MARKING:
|
|
199
|
+
return 'Wegmarkering';
|
|
200
|
+
case SIGN_CONCEPT_TYPES.ROAD_SIGN:
|
|
201
|
+
if (
|
|
202
|
+
signConcept.categories
|
|
203
|
+
.map((cat) => cat.uri)
|
|
204
|
+
.includes(ROAD_SIGN_CATEGORIES.ONDERBORD)
|
|
205
|
+
) {
|
|
206
|
+
return 'Onderbord';
|
|
207
|
+
} else {
|
|
208
|
+
return 'Verkeersbord';
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
193
213
|
function constructSignNode(signConcept: SignConcept, schema: Schema) {
|
|
194
214
|
const signUri = `http://data.lblod.info/verkeerstekens/${uuid()}`;
|
|
195
|
-
const prefix =
|
|
215
|
+
const prefix = determineSignLabel(signConcept);
|
|
196
216
|
const node = schema.nodes.inline_rdfa.create(
|
|
197
217
|
{
|
|
198
218
|
rdfaNodeType: 'resource',
|
|
@@ -29,9 +29,25 @@ export const SIGN_TYPE_MAPPING = {
|
|
|
29
29
|
[SIGN_CONCEPT_TYPES.ROAD_MARKING]: SIGN_TYPES.ROAD_MARKING,
|
|
30
30
|
} as const;
|
|
31
31
|
|
|
32
|
-
export const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
export const ROAD_SIGN_CATEGORIES = {
|
|
33
|
+
XXBORD:
|
|
34
|
+
'https://data.vlaanderen.be/id/concept/Verkeersbordcategorie/ae1b7231-1f31-492d-947a-25fc5d114492',
|
|
35
|
+
'XX-AWVBORD':
|
|
36
|
+
'https://data.vlaanderen.be/id/concept/Verkeersbordcategorie/8e302648-0eca-478b-8b48-67c3b0e39c0a',
|
|
37
|
+
GEVAARSBORD:
|
|
38
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/2982567006d9e19f04063df73123f56f40e3a28941031a7ba6e6667f64740fa9',
|
|
39
|
+
STILSTAANPARKEERBORD:
|
|
40
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/29ea3335e357e414d07229242607b352941c0c21e78760600cc0f5270f18c38b',
|
|
41
|
+
VOORRANGSBORD:
|
|
42
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/737da5751bc7f311398a834f34df310dd95255a0b62afa2db2882c72d54b47d2',
|
|
43
|
+
ZONEBORD:
|
|
44
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/86a67f3cba6512ae10c4b9b09ba35d8c80109189b44d37e848858af9efb37019',
|
|
45
|
+
VERBODSBORD:
|
|
46
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/955a9adc73d076a2a424754cd540b73da8d15fb002ab6c9f115d080edddb57e8',
|
|
47
|
+
ONDERBORD:
|
|
48
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/991b04b477b77bc7cf1414fb5d255cc4435dd9c1681e8de66f770710c1c83ad0',
|
|
49
|
+
GEBODSBORD:
|
|
50
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/9d84069e70f192b7a474d02f07687bc3343ee324207ad9e093c0b2f5def647f8',
|
|
51
|
+
AANWIJSBORD:
|
|
52
|
+
'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/9ea8f8b421343370d20a8bd45d6226aadc48125bda8ddbbeeb53d99f181ee05a',
|
|
53
|
+
};
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BindingObject,
|
|
3
3
|
executeQuery,
|
|
4
4
|
objectify,
|
|
5
|
+
sparqlEscapeUri,
|
|
5
6
|
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
|
|
6
7
|
import {
|
|
7
8
|
RoadSignCategory,
|
|
@@ -10,16 +11,18 @@ import {
|
|
|
10
11
|
|
|
11
12
|
type QueryOptions = {
|
|
12
13
|
abortSignal?: AbortSignal;
|
|
14
|
+
roadSignConceptUri?: string;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export default async function queryRoadSignCategories(
|
|
16
18
|
endpoint: string,
|
|
17
19
|
options: QueryOptions = {},
|
|
18
20
|
) {
|
|
19
|
-
const { abortSignal } = options;
|
|
21
|
+
const { abortSignal, roadSignConceptUri } = options;
|
|
20
22
|
const query = /* sparql */ `
|
|
21
23
|
PREFIX mobiliteit: <https://data.vlaanderen.be/ns/mobiliteit#>
|
|
22
24
|
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
|
|
25
|
+
PREFIX dct: <http://purl.org/dc/terms/>
|
|
23
26
|
|
|
24
27
|
SELECT DISTINCT
|
|
25
28
|
?uri
|
|
@@ -27,6 +30,8 @@ export default async function queryRoadSignCategories(
|
|
|
27
30
|
WHERE {
|
|
28
31
|
?uri a mobiliteit:Verkeersbordcategorie;
|
|
29
32
|
skos:prefLabel ?label.
|
|
33
|
+
|
|
34
|
+
${roadSignConceptUri ? `${sparqlEscapeUri(roadSignConceptUri)} dct:type ?uri` : ''}
|
|
30
35
|
}
|
|
31
36
|
`;
|
|
32
37
|
const queryResult = await executeQuery<BindingObject<RoadSignCategory>>({
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
sparqlEscapeUri,
|
|
7
7
|
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
|
|
8
8
|
import { SignConcept, SignConceptSchema } from '../schemas/sign-concept';
|
|
9
|
+
import queryRoadSignCategories from './road-sign-category';
|
|
10
|
+
import { SIGN_CONCEPT_TYPES } from '../constants';
|
|
9
11
|
|
|
10
12
|
type QueryOptions = {
|
|
11
13
|
imageBaseUrl?: string;
|
|
@@ -31,7 +33,6 @@ export async function querySignConcepts(
|
|
|
31
33
|
?code
|
|
32
34
|
?zonality
|
|
33
35
|
(CONCAT(${sparqlEscapeString(imageBaseUrl ?? '')}, "/files/", ?imageId, "/download") AS ?image)
|
|
34
|
-
(GROUP_CONCAT(?classification; SEPARATOR="|") AS ?classifications)
|
|
35
36
|
WHERE {
|
|
36
37
|
?uri
|
|
37
38
|
a mobiliteit:Verkeerstekenconcept;
|
|
@@ -44,9 +45,6 @@ export async function querySignConcepts(
|
|
|
44
45
|
OPTIONAL {
|
|
45
46
|
?uri ext:zonality ?zonality.
|
|
46
47
|
}
|
|
47
|
-
OPTIONAL {
|
|
48
|
-
?uri dct:type/skos:prefLabel ?classification.
|
|
49
|
-
}
|
|
50
48
|
|
|
51
49
|
VALUES ?type {
|
|
52
50
|
<https://data.vlaanderen.be/ns/mobiliteit#Verkeersbordconcept>
|
|
@@ -63,13 +61,21 @@ export async function querySignConcepts(
|
|
|
63
61
|
abortSignal,
|
|
64
62
|
});
|
|
65
63
|
const bindings = queryResult.results.bindings;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
const concepts = SignConceptSchema.array().parse(bindings.map(objectify));
|
|
65
|
+
const conceptsWithCategories = await Promise.all(
|
|
66
|
+
concepts.map(async (concept) => {
|
|
67
|
+
if (concept.type === SIGN_CONCEPT_TYPES.ROAD_SIGN) {
|
|
68
|
+
const categories = await queryRoadSignCategories(endpoint, {
|
|
69
|
+
roadSignConceptUri: concept.uri,
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
...concept,
|
|
73
|
+
categories,
|
|
74
|
+
};
|
|
75
|
+
} else {
|
|
76
|
+
return concept;
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
return conceptsWithCategories;
|
|
75
81
|
}
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { SIGN_CONCEPT_TYPES, ZONALITY_OPTIONS } from '../constants';
|
|
3
|
+
import { RoadSignCategorySchema } from './road-sign-category';
|
|
3
4
|
|
|
4
|
-
export const SignConceptSchema = z
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
export const SignConceptSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
uri: z.string(),
|
|
8
|
+
code: z.string(),
|
|
9
|
+
image: z.string(),
|
|
10
|
+
zonality: z.nativeEnum(ZONALITY_OPTIONS).optional(),
|
|
11
|
+
})
|
|
12
|
+
.and(
|
|
13
|
+
z.discriminatedUnion('type', [
|
|
14
|
+
z.object({
|
|
15
|
+
type: z.literal(SIGN_CONCEPT_TYPES.ROAD_SIGN),
|
|
16
|
+
categories: z.array(RoadSignCategorySchema).default([]),
|
|
17
|
+
}),
|
|
18
|
+
z.object({
|
|
19
|
+
type: z.enum([
|
|
20
|
+
SIGN_CONCEPT_TYPES.ROAD_MARKING,
|
|
21
|
+
SIGN_CONCEPT_TYPES.TRAFFIC_LIGHT,
|
|
22
|
+
]),
|
|
23
|
+
}),
|
|
24
|
+
]),
|
|
25
|
+
);
|
|
12
26
|
|
|
13
27
|
export type SignConcept = z.infer<typeof SignConceptSchema>;
|
|
@@ -175,6 +175,7 @@ const emberNodeConfig = (options: SnippetPluginConfig): EmberNodeConfig => ({
|
|
|
175
175
|
allowMultipleSnippets: { default: false },
|
|
176
176
|
},
|
|
177
177
|
component: SnippetComponent,
|
|
178
|
+
contentDomClassNames: ['say-snippet-content'],
|
|
178
179
|
content: options.allowedContent || DEFAULT_CONTENT_STRING,
|
|
179
180
|
serialize(node) {
|
|
180
181
|
const listNames = node.attrs.snippetListNames as string[];
|
|
@@ -22,6 +22,7 @@ export const emberNodeConfig: () => EmberNodeConfig = () => {
|
|
|
22
22
|
return {
|
|
23
23
|
name: 'template-comment',
|
|
24
24
|
component: TemplateCommentsComponent as unknown as ComponentLike,
|
|
25
|
+
contentDomClassNames: ['say-template-comment-content'],
|
|
25
26
|
inline: false,
|
|
26
27
|
group: 'block',
|
|
27
28
|
content: 'block+',
|