@lblod/ember-rdfa-editor-lblod-plugins 32.5.2 → 32.5.3-dev.9d68486643ee171b9ead4c9c49cd2fd273fb08d6

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.
@@ -0,0 +1,5 @@
1
+ ---
2
+ '@lblod/ember-rdfa-editor-lblod-plugins': minor
3
+ ---
4
+
5
+ Add actions to document validation plugin
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 32.5.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#594](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/594) [`b732735`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/b732735c373e2cec7bb4ccce6974cc0934ae1f99) Thanks [@abeforgit](https://github.com/abeforgit)! - Fix parsing of articles
8
+
9
+ - [#595](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/595) [`eb0aea2`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/eb0aea28d0887ef5274aba72c6f6a7daa02aca24) Thanks [@piemonkey](https://github.com/piemonkey)! - Fix parsing of oslo locations to no longer incorrectly catch non-locations
10
+
11
+ - [#596](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/596) [`ece607c`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/ece607cf50f6480277f9b47893df71fbb85e4b82) Thanks [@piemonkey](https://github.com/piemonkey)! - Correctly parse the subject URI of roadsign_regulation nodes
12
+
3
13
  ## 32.5.2
4
14
 
5
15
  ### Patch Changes
@@ -12,6 +12,7 @@ import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
12
12
  import { ExternalLinkIcon } from '@appuniversum/ember-appuniversum/components/icons/external-link';
13
13
  import t from 'ember-intl/helpers/t';
14
14
  import { eq } from 'ember-truth-helpers';
15
+ import ValidationReport from 'rdf-validate-shacl/src/validation-report';
15
16
 
16
17
  interface Sig {
17
18
  Args: {
@@ -37,10 +38,20 @@ export default class DocumentValidationPluginCard extends Component<Sig> {
37
38
  }
38
39
  get documentValidationErrors() {
39
40
  if (!this.validationState) return [];
40
- const { propertiesWithErrors } = this.validationState;
41
+
42
+ const { propertiesWithErrors, actions } = this.validationState;
41
43
  if (!propertiesWithErrors) return undefined;
42
44
 
43
- return propertiesWithErrors;
45
+ const documentValidationErrors = propertiesWithErrors.map((property) => {
46
+ const action = actions.find(
47
+ (action) => property?.shape === action.shaclRule,
48
+ );
49
+ return {
50
+ ...property,
51
+ action,
52
+ };
53
+ });
54
+ return documentValidationErrors;
44
55
  }
45
56
  get propertiesWithoutErrors() {
46
57
  if (!this.validationState) return [];
@@ -65,6 +76,50 @@ export default class DocumentValidationPluginCard extends Component<Sig> {
65
76
  return ['valid', 'no-matches'].includes(this.status);
66
77
  }
67
78
 
79
+ doActionAndTriggerValidation = async (
80
+ action: (controller: SayController, report: ValidationReport) => void,
81
+ ) => {
82
+ action(this.controller, this.validationState?.report as ValidationReport);
83
+ const pluginState = documentValidationPluginKey.getState(
84
+ this.controller.mainEditorView.state,
85
+ );
86
+ if (!pluginState) return;
87
+ const { validationCallback } = pluginState;
88
+ await validationCallback(
89
+ this.controller.mainEditorView,
90
+ this.controller.htmlContent,
91
+ );
92
+ };
93
+
94
+ oldVal: typeof this.documentValidationErrors;
95
+
96
+ dedupe = (val: typeof this.documentValidationErrors) => {
97
+ if (this.myCompare(val, this.oldVal)) {
98
+ return this.oldVal;
99
+ } else {
100
+ this.oldVal = val;
101
+ return val;
102
+ }
103
+ };
104
+
105
+ myCompare = (
106
+ val1: typeof this.documentValidationErrors,
107
+ val2: typeof this.documentValidationErrors,
108
+ ) => {
109
+ if (!val1?.length) return false;
110
+ for (let i = 0; i < val1.length; i++) {
111
+ if (
112
+ !val1 ||
113
+ !val2 ||
114
+ !val1[i] ||
115
+ !val2[i] ||
116
+ val1[i].shape !== val2[i].shape
117
+ )
118
+ return false;
119
+ }
120
+ return true;
121
+ };
122
+
68
123
  <template>
69
124
  <AuCard
70
125
  @flex={{true}}
@@ -94,42 +149,53 @@ export default class DocumentValidationPluginCard extends Component<Sig> {
94
149
  </p>
95
150
  </c.header>
96
151
  <c.content>
97
- <p class='au-u-medium au-u-para-small'>{{t
98
- 'document-validation-plugin.description'
99
- }}</p>
100
- {{#each this.documentValidationErrors as |error|}}
101
- <div class='say-document-validation__error-container'>
102
- <AuIcon
103
- @icon={{CloseFilledIcon}}
104
- @size='large'
105
- @ariaHidden={{true}}
106
- class='say-document-validation__icon-error au-u-margin-right-small'
107
- />
108
- <div>
109
- {{error.message}}
110
- <AuButton
111
- class='au-u-padding-left-none au-u-padding-right-none'
112
- @icon={{ExternalLinkIcon}}
113
- @skin='link'
114
- title={{error.subject}}
115
- {{on 'click' (fn this.goToSubject error.subject)}}
116
- >{{t 'document-validation-plugin.see-related-node'}}</AuButton>
117
- </div>
118
- </div>
119
- {{/each}}
120
- {{#each this.propertiesWithoutErrors as |property|}}
121
- <div class='say-document-validation__error-container'>
122
- <div class='au-u-margin-right-small'>
152
+ <div>
153
+ {{#each (this.dedupe this.documentValidationErrors) as |error|}}
154
+ <div class='say-document-validation__error-container'>
123
155
  <AuIcon
124
- @icon={{CheckFilledIcon}}
156
+ @icon={{CloseFilledIcon}}
125
157
  @size='large'
126
158
  @ariaHidden={{true}}
127
- class='say-document-validation__icon-success'
159
+ class='say-document-validation__icon-error au-u-margin-right-small'
128
160
  />
161
+ <div>
162
+ {{error.message}}
163
+ <AuButton
164
+ class='au-u-padding-left-none au-u-padding-right-none'
165
+ @icon={{ExternalLinkIcon}}
166
+ @skin='link'
167
+ title={{error.subject}}
168
+ {{on 'click' (fn this.goToSubject error.subject)}}
169
+ >{{t 'document-validation-plugin.see-related-node'}}</AuButton>
170
+ {{#if error.action}}
171
+ <AuButton
172
+ class='au-u-padding-left-none au-u-padding-right-none'
173
+ @icon={{ExternalLinkIcon}}
174
+ @skin='link'
175
+ title={{error.subject}}
176
+ {{on
177
+ 'click'
178
+ (fn this.doActionAndTriggerValidation error.action.action)
179
+ }}
180
+ >{{error.action.buttonTitle}}</AuButton>
181
+ {{/if}}
182
+ </div>
183
+ </div>
184
+ {{/each}}
185
+ {{#each this.propertiesWithoutErrors as |property|}}
186
+ <div class='say-document-validation__error-container'>
187
+ <div class='au-u-margin-right-small'>
188
+ <AuIcon
189
+ @icon={{CheckFilledIcon}}
190
+ @size='large'
191
+ @ariaHidden={{true}}
192
+ class='say-document-validation__icon-success'
193
+ />
194
+ </div>
195
+ {{property.message}}
129
196
  </div>
130
- {{property.message}}
131
- </div>
132
- {{/each}}
197
+ {{/each}}
198
+ </div>
133
199
  </c.content>
134
200
 
135
201
  </AuCard>
@@ -0,0 +1,92 @@
1
+ import { SayController } from '@lblod/ember-rdfa-editor';
2
+ import { getCurrentBesluitRange } from '../besluit-topic-plugin/utils/helpers';
3
+ import IntlService from 'ember-intl/services/intl';
4
+ import {
5
+ insertMotivation,
6
+ insertArticleContainer,
7
+ insertDescription,
8
+ insertTitle,
9
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/decision-plugin/commands';
10
+
11
+ export function insertTitleAtCursor(
12
+ controller: SayController,
13
+ intl: IntlService,
14
+ ) {
15
+ const decisionNodeLocation = getDecisionNodeLocation(controller);
16
+ if (!decisionNodeLocation) return;
17
+ controller.doCommand(
18
+ insertTitle({
19
+ placeholderText: intl.t('besluit-plugin.placeholder.decision-title'),
20
+ decisionLocation: decisionNodeLocation,
21
+ }),
22
+ { view: controller.mainEditorView },
23
+ );
24
+ controller.focus();
25
+ }
26
+
27
+ export function insertDescriptionAtCursor(
28
+ controller: SayController,
29
+ intl: IntlService,
30
+ ) {
31
+ const decisionNodeLocation = getDecisionNodeLocation(controller);
32
+ if (!decisionNodeLocation) return;
33
+ controller.doCommand(
34
+ insertDescription({
35
+ placeholderText: intl.t(
36
+ 'besluit-plugin.placeholder.decision-description',
37
+ ),
38
+ decisionLocation: decisionNodeLocation,
39
+ }),
40
+ {
41
+ view: controller.mainEditorView,
42
+ },
43
+ );
44
+ controller.focus();
45
+ }
46
+
47
+ export function insertMotivationAtCursor(
48
+ controller: SayController,
49
+ intl: IntlService,
50
+ ) {
51
+ const decisionNodeLocation = getDecisionNodeLocation(controller);
52
+ if (!decisionNodeLocation) return;
53
+ controller.doCommand(
54
+ insertMotivation({
55
+ intl: intl,
56
+ decisionLocation: decisionNodeLocation,
57
+ }),
58
+ {
59
+ view: controller.mainEditorView,
60
+ },
61
+ );
62
+ controller.focus();
63
+ }
64
+
65
+ export function insertArticleContainerAtCursor(
66
+ controller: SayController,
67
+ intl: IntlService,
68
+ articleUriGenerator?: () => string,
69
+ ) {
70
+ const decisionNodeLocation = getDecisionNodeLocation(controller);
71
+ if (!decisionNodeLocation) return;
72
+ controller.doCommand(
73
+ insertArticleContainer({
74
+ intl: intl,
75
+ decisionUri: decisionNodeLocation?.node.attrs.subject,
76
+ articleUriGenerator: articleUriGenerator,
77
+ }),
78
+ {
79
+ view: controller.mainEditorView,
80
+ },
81
+ );
82
+ }
83
+
84
+ function getDecisionNodeLocation(controller: SayController) {
85
+ const besluitRange = getCurrentBesluitRange(controller);
86
+ if (!besluitRange) return;
87
+ const decisionNodeLocation = {
88
+ pos: besluitRange.from,
89
+ node: besluitRange.node,
90
+ };
91
+ return decisionNodeLocation;
92
+ }
@@ -2,7 +2,12 @@ import factory from '@rdfjs/dataset';
2
2
  import SHACLValidator from 'rdf-validate-shacl';
3
3
  import { Parser as ParserN3 } from 'n3';
4
4
  import { RdfaParser } from 'rdfa-streaming-parser';
5
- import { ProsePlugin, PluginKey, EditorView } from '@lblod/ember-rdfa-editor';
5
+ import {
6
+ ProsePlugin,
7
+ PluginKey,
8
+ EditorView,
9
+ SayController,
10
+ } from '@lblod/ember-rdfa-editor';
6
11
  import removeQuotes from '@lblod/ember-rdfa-editor-lblod-plugins/utils/remove-quotes';
7
12
  import {
8
13
  BlankNode,
@@ -20,6 +25,11 @@ export const documentValidationPluginKey =
20
25
 
21
26
  interface DocumentValidationPluginArgs {
22
27
  documentShape: string;
28
+ actions: {
29
+ shaclRule: string;
30
+ action: (controller: SayController, report: ValidationReport) => void;
31
+ buttonTitle: string;
32
+ }[];
23
33
  }
24
34
 
25
35
  export type ShaclValidationReport = ValidationReport.ValidationReport<
@@ -34,6 +44,7 @@ interface DocumentValidationResult {
34
44
  | {
35
45
  message: string;
36
46
  subject: string | undefined;
47
+ shape: string;
37
48
  }
38
49
  // TODO get rid of this?
39
50
  | undefined
@@ -47,6 +58,11 @@ export interface DocumentValidationPluginState
47
58
  extends DocumentValidationResult {
48
59
  documentShape: string;
49
60
  validationCallback: typeof validationCallback;
61
+ actions: {
62
+ shaclRule: string;
63
+ action: (controller: SayController, report: ValidationReport) => void;
64
+ buttonTitle: string;
65
+ }[];
50
66
  }
51
67
 
52
68
  export const documentValidationPlugin = (
@@ -61,6 +77,7 @@ export const documentValidationPlugin = (
61
77
  documentShape: options.documentShape,
62
78
  propertiesWithoutErrors: [],
63
79
  propertiesWithErrors: [],
80
+ actions: options.actions,
64
81
  };
65
82
  },
66
83
  apply(tr, state) {
@@ -121,17 +138,23 @@ async function validationCallback(view: EditorView, documentHtml: string) {
121
138
  );
122
139
  const propertiesWithErrorsMessages = propertiesWithErrors
123
140
  .map(({ sourceShape, focusNode }) => {
141
+ console.log(sourceShape);
124
142
  const match = shacl.match(sourceShape, errorMessagePred, undefined);
125
143
  const message = [...match][0]?.object.value;
126
144
  return message
127
- ? { message: removeQuotes(message), subject: focusNode?.value }
145
+ ? {
146
+ message: removeQuotes(message),
147
+ subject: focusNode?.value,
148
+ shape: sourceShape.value,
149
+ }
128
150
  : undefined;
129
151
  })
130
152
  .filter((message) => message);
131
153
  const propertiesWithoutErrorsArray = propertyNodes.filter((propertyNode) =>
132
- propertiesWithErrors.some((propertyWithError) => {
133
- return propertyWithError.sourceShape.value !== propertyNode.value;
134
- }),
154
+ propertiesWithErrors.every(
155
+ (propertyWithError) =>
156
+ propertyWithError.sourceShape.value !== propertyNode.value,
157
+ ),
135
158
  );
136
159
 
137
160
  const successMessagePred = sayFactory.namedNode(
@@ -109,13 +109,15 @@ const parseDOM = (config: LocationPluginConfig): TagParseRule[] => {
109
109
  nodeContentsUtils.address.parse(contentContainer.children[0]) ||
110
110
  nodeContentsUtils.place.parse(contentContainer.children[0]) ||
111
111
  nodeContentsUtils.area.parse(contentContainer.children[0]);
112
- // Ignore the properties for now, we handle these ourselves
113
- const properties: OutgoingTriple[] = [];
114
- return {
115
- ...attrs,
116
- properties,
117
- value: location,
118
- };
112
+ if (location) {
113
+ // Ignore the properties for now, we handle these ourselves
114
+ const properties: OutgoingTriple[] = [];
115
+ return {
116
+ ...attrs,
117
+ properties,
118
+ value: location,
119
+ };
120
+ }
119
121
  }
120
122
  return false;
121
123
  },
@@ -114,6 +114,7 @@ export const roadsign_regulation: NodeSpec = {
114
114
  __rdfaId,
115
115
  properties: propertiesFiltered,
116
116
  backlinks,
117
+ subject: resourceUri,
117
118
  label: `Mobiliteitsmaatregel`,
118
119
  };
119
120
  }
@@ -476,7 +476,7 @@ export const emberNodeConfig: (
476
476
  const body = Array.from(rdfaContent.children).find((child) =>
477
477
  SAY('body').matches(child.getAttribute('property') ?? ''),
478
478
  );
479
- return (body && getRdfaContentElement(body)) ?? (node as HTMLElement);
479
+ return (body && getRdfaContentElement(body)) ?? node;
480
480
  },
481
481
  },
482
482
  {
@@ -537,7 +537,7 @@ export const emberNodeConfig: (
537
537
  if (bodyNode) {
538
538
  return getRdfaContentElement(bodyNode);
539
539
  } else {
540
- return node as HTMLElement;
540
+ return node;
541
541
  }
542
542
  },
543
543
  },
@@ -1,5 +1,6 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { SayController } from '@lblod/ember-rdfa-editor';
3
+ import ValidationReport from 'rdf-validate-shacl/src/validation-report';
3
4
  interface Sig {
4
5
  Args: {
5
6
  controller: SayController;
@@ -8,15 +9,34 @@ interface Sig {
8
9
  export default class DocumentValidationPluginCard extends Component<Sig> {
9
10
  get controller(): SayController;
10
11
  get validationState(): import("@lblod/ember-rdfa-editor-lblod-plugins/plugins/document-validation-plugin").DocumentValidationPluginState | undefined;
11
- get documentValidationErrors(): ({
12
- message: string;
13
- subject: string | undefined;
14
- } | undefined)[] | undefined;
12
+ get documentValidationErrors(): {
13
+ action: {
14
+ shaclRule: string;
15
+ action: (controller: SayController, report: ValidationReport) => void;
16
+ buttonTitle: string;
17
+ } | undefined;
18
+ message?: string | undefined;
19
+ subject?: string | undefined;
20
+ shape?: string | undefined;
21
+ }[] | undefined;
15
22
  get propertiesWithoutErrors(): {
16
23
  message: string;
17
24
  }[];
18
25
  goToSubject: (subject: string) => void;
19
26
  get status(): "not-run" | "no-matches" | "valid" | "invalid";
20
27
  get isSuccesslike(): boolean;
28
+ doActionAndTriggerValidation: (action: (controller: SayController, report: ValidationReport) => void) => Promise<void>;
29
+ oldVal: typeof this.documentValidationErrors;
30
+ dedupe: (val: typeof this.documentValidationErrors) => {
31
+ action: {
32
+ shaclRule: string;
33
+ action: (controller: SayController, report: ValidationReport) => void;
34
+ buttonTitle: string;
35
+ } | undefined;
36
+ message?: string | undefined;
37
+ subject?: string | undefined;
38
+ shape?: string | undefined;
39
+ }[] | undefined;
40
+ myCompare: (val1: typeof this.documentValidationErrors, val2: typeof this.documentValidationErrors) => boolean;
21
41
  }
22
42
  export {};
@@ -0,0 +1,6 @@
1
+ import { SayController } from '@lblod/ember-rdfa-editor';
2
+ import IntlService from 'ember-intl/services/intl';
3
+ export declare function insertTitleAtCursor(controller: SayController, intl: IntlService): void;
4
+ export declare function insertDescriptionAtCursor(controller: SayController, intl: IntlService): void;
5
+ export declare function insertMotivationAtCursor(controller: SayController, intl: IntlService): void;
6
+ export declare function insertArticleContainerAtCursor(controller: SayController, intl: IntlService, articleUriGenerator?: () => string): void;
@@ -1,9 +1,14 @@
1
- import { ProsePlugin, PluginKey, EditorView } from '@lblod/ember-rdfa-editor';
1
+ import { ProsePlugin, PluginKey, EditorView, SayController } from '@lblod/ember-rdfa-editor';
2
2
  import { DataFactory, DatasetCore, DatasetCoreFactory, Quad } from '@rdfjs/types';
3
3
  import ValidationReport from 'rdf-validate-shacl/src/validation-report';
4
4
  export declare const documentValidationPluginKey: PluginKey<DocumentValidationPluginState>;
5
5
  interface DocumentValidationPluginArgs {
6
6
  documentShape: string;
7
+ actions: {
8
+ shaclRule: string;
9
+ action: (controller: SayController, report: ValidationReport) => void;
10
+ buttonTitle: string;
11
+ }[];
7
12
  }
8
13
  export type ShaclValidationReport = ValidationReport.ValidationReport<DataFactory<Quad, Quad> & DatasetCoreFactory<Quad, Quad, DatasetCore<Quad, Quad>>>;
9
14
  interface DocumentValidationResult {
@@ -14,6 +19,7 @@ interface DocumentValidationResult {
14
19
  propertiesWithErrors: ({
15
20
  message: string;
16
21
  subject: string | undefined;
22
+ shape: string;
17
23
  } | undefined)[];
18
24
  }
19
25
  export interface DocumentValidationTransactionMeta extends DocumentValidationResult {
@@ -22,6 +28,11 @@ export interface DocumentValidationTransactionMeta extends DocumentValidationRes
22
28
  export interface DocumentValidationPluginState extends DocumentValidationResult {
23
29
  documentShape: string;
24
30
  validationCallback: typeof validationCallback;
31
+ actions: {
32
+ shaclRule: string;
33
+ action: (controller: SayController, report: ValidationReport) => void;
34
+ buttonTitle: string;
35
+ }[];
25
36
  }
26
37
  export declare const documentValidationPlugin: (options: DocumentValidationPluginArgs) => ProsePlugin<DocumentValidationPluginState>;
27
38
  declare function validationCallback(view: EditorView, documentHtml: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lblod/ember-rdfa-editor-lblod-plugins",
3
- "version": "32.5.2",
3
+ "version": "32.5.3-dev.9d68486643ee171b9ead4c9c49cd2fd273fb08d6",
4
4
  "description": "Ember addon providing lblod specific plugins for the ember-rdfa-editor",
5
5
  "keywords": [
6
6
  "ember-addon",
@@ -112,7 +112,7 @@
112
112
  "@glint/template": "^1.5.0",
113
113
  "@graphy/content.ttl.write": "^4.3.7",
114
114
  "@graphy/memory.dataset.fast": "4.3.3",
115
- "@lblod/ember-rdfa-editor": "12.9.0",
115
+ "@lblod/ember-rdfa-editor": "^12.12.1",
116
116
  "@rdfjs/types": "^1.1.0",
117
117
  "@release-it/keep-a-changelog": "^4.0.0",
118
118
  "@tsconfig/ember": "^3.0.8",
@@ -183,7 +183,7 @@
183
183
  "@appuniversum/ember-appuniversum": "^3.12.0",
184
184
  "@ember/string": "3.x",
185
185
  "@glint/template": "^1.4.0",
186
- "@lblod/ember-rdfa-editor": "^12.1.0",
186
+ "@lblod/ember-rdfa-editor": "^12.12.1",
187
187
  "ember-concurrency": "^4.0.2",
188
188
  "ember-element-helper": "^0.8.6",
189
189
  "ember-intl": "^7.0.0",