@umbraco-cms/backoffice 15.3.0-rc → 15.3.0

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.
Files changed (42) hide show
  1. package/dist-cms/assets/lang/da-dk.js +0 -3
  2. package/dist-cms/assets/lang/da-dk.ts +0 -3
  3. package/dist-cms/assets/lang/en-us.js +0 -3
  4. package/dist-cms/assets/lang/en-us.ts +0 -3
  5. package/dist-cms/assets/lang/en.js +0 -5
  6. package/dist-cms/assets/lang/en.ts +0 -6
  7. package/dist-cms/custom-elements.json +0 -29
  8. package/dist-cms/external/backend-api/src/types.gen.d.ts +0 -1
  9. package/dist-cms/libs/context-api/provide/context-provider.controller.js +2 -1
  10. package/dist-cms/packages/block/block/workspace/block-element-manager.js +1 -1
  11. package/dist-cms/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.js +5 -2
  12. package/dist-cms/packages/core/content/workspace/content-detail-workspace-base.js +1 -1
  13. package/dist-cms/packages/core/validation/context/validation-messages.manager.d.ts +3 -0
  14. package/dist-cms/packages/core/validation/context/validation-messages.manager.js +39 -0
  15. package/dist-cms/packages/core/validation/controllers/form-control-validator.controller.js +7 -5
  16. package/dist-cms/packages/core/validation/controllers/validation.controller.d.ts +2 -0
  17. package/dist-cms/packages/core/validation/controllers/validation.controller.js +27 -3
  18. package/dist-cms/packages/core/validation/utils/json-path.function.d.ts +1 -1
  19. package/dist-cms/packages/core/validation/utils/json-path.function.js +53 -37
  20. package/dist-cms/packages/core/workspace/submittable/submittable-workspace-context-base.js +10 -3
  21. package/dist-cms/packages/documents/document-blueprints/repository/detail/document-blueprint-detail.server.data-source.js +1 -1
  22. package/dist-cms/packages/documents/documents/components/input-document/input-document.context.js +1 -1
  23. package/dist-cms/packages/documents/documents/publishing/publish-with-descendants/modal/document-publish-with-descendants-modal.element.js +1 -23
  24. package/dist-cms/packages/documents/documents/publishing/publish-with-descendants/modal/document-publish-with-descendants-modal.token.d.ts +0 -1
  25. package/dist-cms/packages/documents/documents/publishing/repository/document-publishing.repository.d.ts +1 -2
  26. package/dist-cms/packages/documents/documents/publishing/repository/document-publishing.repository.js +2 -3
  27. package/dist-cms/packages/documents/documents/publishing/repository/document-publishing.server.data-source.d.ts +1 -2
  28. package/dist-cms/packages/documents/documents/publishing/repository/document-publishing.server.data-source.js +2 -4
  29. package/dist-cms/packages/documents/documents/publishing/workspace-context/document-publishing.workspace-context.js +1 -1
  30. package/dist-cms/packages/documents/documents/repository/detail/document-detail.server.data-source.js +1 -1
  31. package/dist-cms/packages/media/media/components/input-media/input-media.context.js +1 -1
  32. package/dist-cms/packages/members/member/components/input-member/input-member.context.js +1 -1
  33. package/dist-cms/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.d.ts +2 -2
  34. package/dist-cms/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.js +3 -5
  35. package/dist-cms/packages/property-editors/accepted-types/property-editor-ui-accepted-upload-types.element.js +5 -35
  36. package/dist-cms/packages/property-editors/multiple-text-string/property-editor-ui-multiple-text-string.element.d.ts +2 -6
  37. package/dist-cms/packages/property-editors/multiple-text-string/property-editor-ui-multiple-text-string.element.js +14 -37
  38. package/dist-cms/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.js +1 -1
  39. package/dist-cms/tsconfig.build.tsbuildinfo +1 -1
  40. package/dist-cms/umbraco-package.json +1 -1
  41. package/dist-cms/vscode-html-custom-data.json +3 -17
  42. package/package.json +1 -1
@@ -276,9 +276,6 @@ export default {
276
276
  removeTextBox: 'Fjern denne tekstboks',
277
277
  contentRoot: 'Indholdsrod',
278
278
  includeUnpublished: 'Inkluder ikke-udgivet indhold.',
279
- forceRepublish: 'Udgiv uændrede elementer.',
280
- forceRepublishWarning: 'ADVARSEL: Udgivelse af alle sider under denne i indholdstræet, uanset om de er ændret eller ej, kan være en ressourcekrævende og langvarig proces.',
281
- forceRepublishAdvisory: 'Dette bør ikke være nødvendigt under normale omstændigheder, så fortsæt kun med denne handling, hvis du er sikker på, at det er nødvendigt.',
282
279
  isSensitiveValue: 'Denne værdi er skjult.Hvis du har brug for adgang til at se denne værdi, bedes du\n kontakte din web-administrator.\n ',
283
280
  isSensitiveValue_short: 'Denne værdi er skjult.',
284
281
  languagesToPublish: 'Hvilke sprog vil du gerne udgive?',
@@ -298,9 +298,6 @@ export default {
298
298
  removeTextBox: 'Fjern denne tekstboks',
299
299
  contentRoot: 'Indholdsrod',
300
300
  includeUnpublished: 'Inkluder ikke-udgivet indhold.',
301
- forceRepublish: 'Udgiv uændrede elementer.',
302
- forceRepublishWarning: 'ADVARSEL: Udgivelse af alle sider under denne i indholdstræet, uanset om de er ændret eller ej, kan være en ressourcekrævende og langvarig proces.',
303
- forceRepublishAdvisory: 'Dette bør ikke være nødvendigt under normale omstændigheder, så fortsæt kun med denne handling, hvis du er sikker på, at det er nødvendigt.',
304
301
  isSensitiveValue:
305
302
  'Denne værdi er skjult.Hvis du har brug for adgang til at se denne værdi, bedes du\n kontakte din web-administrator.\n ',
306
303
  isSensitiveValue_short: 'Denne værdi er skjult.',
@@ -296,9 +296,6 @@ export default {
296
296
  removeTextBox: 'Remove this text box',
297
297
  contentRoot: 'Content root',
298
298
  includeUnpublished: 'Include unpublished content items.',
299
- forceRepublish: 'Publish unchanged items.',
300
- forceRepublishWarning: 'WARNING: Publishing all pages below this one in the content tree, whether or not they have changed, can be an expensive and long-running operation.',
301
- forceRepublishAdvisory: 'This should not be necessary in normal circumstances so please only proceed with this option selected if you are certain it is required.',
302
299
  isSensitiveValue: 'This value is hidden. If you need access to view this value please contact your\n website administrator.\n ',
303
300
  isSensitiveValue_short: 'This value is hidden.',
304
301
  languagesToPublish: 'What languages would you like to publish?',
@@ -320,9 +320,6 @@ export default {
320
320
  removeTextBox: 'Remove this text box',
321
321
  contentRoot: 'Content root',
322
322
  includeUnpublished: 'Include unpublished content items.',
323
- forceRepublish: 'Publish unchanged items.',
324
- forceRepublishWarning: 'WARNING: Publishing all pages below this one in the content tree, whether or not they have changed, can be an expensive and long-running operation.',
325
- forceRepublishAdvisory: 'This should not be necessary in normal circumstances so please only proceed with this option selected if you are certain it is required.',
326
323
  isSensitiveValue:
327
324
  'This value is hidden. If you need access to view this value please contact your\n website administrator.\n ',
328
325
  isSensitiveValue_short: 'This value is hidden.',
@@ -293,9 +293,6 @@ export default {
293
293
  removeTextBox: 'Remove this text box',
294
294
  contentRoot: 'Content root',
295
295
  includeUnpublished: 'Include unpublished content items.',
296
- forceRepublish: 'Publish unchanged items.',
297
- forceRepublishWarning: 'WARNING: Publishing all pages below this one in the content tree, whether or not they have changed, can be an expensive and long-running operation.',
298
- forceRepublishAdvisory: 'This should not be necessary in normal circumstances so please only proceed with this option selected if you are certain it is required.',
299
296
  isSensitiveValue: 'This value is hidden. If you need access to view this value please contact your\n website administrator.\n ',
300
297
  isSensitiveValue_short: 'This value is hidden.',
301
298
  languagesToPublish: 'What languages would you like to publish?',
@@ -350,7 +347,6 @@ export default {
350
347
  fileSecurityValidationFailure: 'One or more file security validations have failed',
351
348
  moveToSameFolderFailed: 'Parent and destination folders cannot be the same',
352
349
  uploadNotAllowed: 'Upload is not allowed in this location.',
353
- noticeExtensionsServerOverride: 'Regardless of the allowed file types, the following limitations apply system-wide due to the server configuration:',
354
350
  },
355
351
  member: {
356
352
  '2fa': 'Two-Factor Authentication',
@@ -837,7 +833,6 @@ export default {
837
833
  retrieve: 'Retrieve',
838
834
  retry: 'Retry',
839
835
  rights: 'Permissions',
840
- serverConfiguration: 'Server Configuration',
841
836
  scheduledPublishing: 'Scheduled Publishing',
842
837
  umbracoInfo: 'Umbraco info',
843
838
  search: 'Search',
@@ -317,9 +317,6 @@ export default {
317
317
  removeTextBox: 'Remove this text box',
318
318
  contentRoot: 'Content root',
319
319
  includeUnpublished: 'Include unpublished content items.',
320
- forceRepublish: 'Publish unchanged items.',
321
- forceRepublishWarning: 'WARNING: Publishing all pages below this one in the content tree, whether or not they have changed, can be an expensive and long-running operation.',
322
- forceRepublishAdvisory: 'This should not be necessary in normal circumstances so please only proceed with this option selected if you are certain it is required.',
323
320
  isSensitiveValue:
324
321
  'This value is hidden. If you need access to view this value please contact your\n website administrator.\n ',
325
322
  isSensitiveValue_short: 'This value is hidden.',
@@ -377,8 +374,6 @@ export default {
377
374
  fileSecurityValidationFailure: 'One or more file security validations have failed',
378
375
  moveToSameFolderFailed: 'Parent and destination folders cannot be the same',
379
376
  uploadNotAllowed: 'Upload is not allowed in this location.',
380
- noticeExtensionsServerOverride:
381
- 'Regardless of the allowed file types, the following limitations apply system-wide due to the server configuration:',
382
377
  },
383
378
  member: {
384
379
  '2fa': 'Two-Factor Authentication',
@@ -902,7 +897,6 @@ export default {
902
897
  retrieve: 'Retrieve',
903
898
  retry: 'Retry',
904
899
  rights: 'Permissions',
905
- serverConfiguration: 'Server Configuration',
906
900
  scheduledPublishing: 'Scheduled Publishing',
907
901
  umbracoInfo: 'Umbraco info',
908
902
  search: 'Search',
@@ -10566,11 +10566,6 @@
10566
10566
  "name": "umb-property-editor-ui-multi-url-picker",
10567
10567
  "path": "./../src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts",
10568
10568
  "attributes": [
10569
- {
10570
- "name": "value",
10571
- "type": "UmbLinkPickerLink[]",
10572
- "default": "[]"
10573
- },
10574
10569
  {
10575
10570
  "name": "readonly",
10576
10571
  "description": "Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.",
@@ -10579,12 +10574,6 @@
10579
10574
  }
10580
10575
  ],
10581
10576
  "properties": [
10582
- {
10583
- "name": "value",
10584
- "attribute": "value",
10585
- "type": "UmbLinkPickerLink[]",
10586
- "default": "[]"
10587
- },
10588
10577
  {
10589
10578
  "name": "config"
10590
10579
  },
@@ -10737,10 +10726,6 @@
10737
10726
  "name": "umb-property-editor-ui-accepted-upload-types",
10738
10727
  "path": "./../src/packages/property-editors/accepted-types/property-editor-ui-accepted-upload-types.element.ts",
10739
10728
  "attributes": [
10740
- {
10741
- "name": "value",
10742
- "type": "string[] | undefined"
10743
- },
10744
10729
  {
10745
10730
  "name": "disabled",
10746
10731
  "description": "Disables the Multiple Text String Property Editor UI",
@@ -10766,11 +10751,6 @@
10766
10751
  "type": "array",
10767
10752
  "default": "[null]"
10768
10753
  },
10769
- {
10770
- "name": "value",
10771
- "attribute": "value",
10772
- "type": "string[] | undefined"
10773
- },
10774
10754
  {
10775
10755
  "name": "config"
10776
10756
  },
@@ -11522,10 +11502,6 @@
11522
11502
  "name": "umb-property-editor-ui-multiple-text-string",
11523
11503
  "path": "./../src/packages/property-editors/multiple-text-string/property-editor-ui-multiple-text-string.element.ts",
11524
11504
  "attributes": [
11525
- {
11526
- "name": "value",
11527
- "type": "string[] | undefined"
11528
- },
11529
11505
  {
11530
11506
  "name": "disabled",
11531
11507
  "description": "Disables the Multiple Text String Property Editor UI",
@@ -11546,11 +11522,6 @@
11546
11522
  }
11547
11523
  ],
11548
11524
  "properties": [
11549
- {
11550
- "name": "value",
11551
- "attribute": "value",
11552
- "type": "string[] | undefined"
11553
- },
11554
11525
  {
11555
11526
  "name": "config"
11556
11527
  },
@@ -1753,7 +1753,6 @@ export type PublishDocumentRequestModel = {
1753
1753
  };
1754
1754
  export type PublishDocumentWithDescendantsRequestModel = {
1755
1755
  includeUnpublishedDescendants: boolean;
1756
- forceRepublish: boolean;
1757
1756
  cultures: Array<(string)>;
1758
1757
  };
1759
1758
  export type PublishedDocumentResponseModel = {
@@ -25,8 +25,9 @@ export class UmbContextProviderController extends UmbContextProvider {
25
25
  }
26
26
  destroy() {
27
27
  if (this.#host) {
28
- this.#host.removeUmbController(this);
28
+ const host = this.#host;
29
29
  this.#host = undefined;
30
+ host.removeUmbController(this);
30
31
  }
31
32
  super.destroy();
32
33
  }
@@ -135,7 +135,7 @@ export class UmbBlockElementManager extends UmbControllerBase {
135
135
  if (!editorAlias) {
136
136
  throw new Error(`Editor Alias of "${property.dataType.unique}" not found.`);
137
137
  }
138
- const entry = { ...variantId.toObject(), alias, editorAlias, value };
138
+ const entry = { editorAlias, ...variantId.toObject(), alias, value };
139
139
  const currentData = this.getData();
140
140
  if (currentData) {
141
141
  const values = appendToFrozenArray(currentData.values ?? [], entry, (x) => x.alias === alias && variantId.compare(x));
@@ -119,12 +119,13 @@ let UmbPropertyEditorUIBlockListElement = class UmbPropertyEditorUIBlockListElem
119
119
  });
120
120
  // Observe Blocks and clean up validation messages for content/settings that are not in the block list anymore:
121
121
  this.observe(this.#managerContext.layouts, (layouts) => {
122
+ const validationMessagesToRemove = [];
122
123
  const contentKeys = layouts.map((x) => x.contentKey);
123
124
  this.#validationContext.messages.getMessagesOfPathAndDescendant('$.contentData').forEach((message) => {
124
125
  // get the KEY from this string: $.contentData[?(@.key == 'KEY')]
125
126
  const key = extractJsonQueryProps(message.path).key;
126
127
  if (key && contentKeys.indexOf(key) === -1) {
127
- this.#validationContext.messages.removeMessageByKey(message.key);
128
+ validationMessagesToRemove.push(message.key);
128
129
  }
129
130
  });
130
131
  const settingsKeys = layouts.map((x) => x.settingsKey).filter((x) => x !== undefined);
@@ -132,9 +133,11 @@ let UmbPropertyEditorUIBlockListElement = class UmbPropertyEditorUIBlockListElem
132
133
  // get the key from this string: $.settingsData[?(@.key == 'KEY')]
133
134
  const key = extractJsonQueryProps(message.path).key;
134
135
  if (key && settingsKeys.indexOf(key) === -1) {
135
- this.#validationContext.messages.removeMessageByKey(message.key);
136
+ validationMessagesToRemove.push(message.key);
136
137
  }
137
138
  });
139
+ // Remove the messages after the loop to prevent changing the array while iterating over it.
140
+ this.#validationContext.messages.removeMessageByKeys(validationMessagesToRemove);
138
141
  }, null);
139
142
  this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (context) => {
140
143
  this.#managerContext.setVariantId(context.getVariantId());
@@ -332,7 +332,7 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
332
332
  throw new Error(`Editor Alias of "${property.dataType.unique}" not found.`);
333
333
  }
334
334
  // Notice the order of the properties is important for our JSON String Compare function. [NL]
335
- const entry = { editorAlias, alias, ...variantId.toObject(), value };
335
+ const entry = { editorAlias, ...variantId.toObject(), alias, value };
336
336
  const currentData = this.getData();
337
337
  if (currentData) {
338
338
  const values = appendToFrozenArray(currentData.values ?? [], entry, (x) => x.alias === alias && variantId.compare(x));
@@ -11,6 +11,9 @@ export declare class UmbValidationMessagesManager {
11
11
  #private;
12
12
  messages: Observable<UmbValidationMessage[]>;
13
13
  debug(logName: string): void;
14
+ getMessages(): Array<UmbValidationMessage>;
15
+ initiateChange(): void;
16
+ finishChange(): void;
14
17
  getHasAnyMessages(): boolean;
15
18
  getMessagesOfPathAndDescendant(path: string): Array<UmbValidationMessage>;
16
19
  messagesOfPathAndDescendant(path: string): Observable<Array<UmbValidationMessage>>;
@@ -15,12 +15,31 @@ export class UmbValidationMessagesManager {
15
15
  constructor() {
16
16
  this.#messages = new UmbArrayState([], (x) => x.key);
17
17
  this.messages = this.#messages.asObservable();
18
+ this.#updateLock = 0;
18
19
  this.#translators = [];
19
20
  }
20
21
  #messages;
21
22
  debug(logName) {
22
23
  this.messages.subscribe((x) => console.log(logName, x));
23
24
  }
25
+ getMessages() {
26
+ return this.#messages.getValue();
27
+ }
28
+ #updateLock;
29
+ initiateChange() {
30
+ this.#updateLock++;
31
+ this.#messages.mute();
32
+ // TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL]
33
+ /*return {
34
+ [Symbol.dispose]: this.finishPropertyValueChange,
35
+ };*/
36
+ }
37
+ finishChange() {
38
+ this.#updateLock--;
39
+ if (this.#updateLock === 0) {
40
+ this.#messages.unmute();
41
+ }
42
+ }
24
43
  getHasAnyMessages() {
25
44
  return this.#messages.getValue().length !== 0;
26
45
  }
@@ -55,7 +74,9 @@ export class UmbValidationMessagesManager {
55
74
  if (this.#messages.getValue().find((x) => x.type === type && x.path === path && x.body === body)) {
56
75
  return;
57
76
  }
77
+ this.initiateChange();
58
78
  this.#messages.appendOne({ type, key, path, body: body });
79
+ this.finishChange();
59
80
  }
60
81
  addMessages(type, path, bodies) {
61
82
  //path = this.#translatePath(path.toLowerCase()) ?? path.toLowerCase();
@@ -63,26 +84,42 @@ export class UmbValidationMessagesManager {
63
84
  // filter out existing messages with the same path and type, and append the new messages: [NL]
64
85
  const existingMessages = this.#messages.getValue();
65
86
  const newBodies = bodies.filter((message) => existingMessages.find((x) => x.type === type && x.path === path && x.body === message) === undefined);
87
+ this.initiateChange();
66
88
  this.#messages.append(newBodies.map((body) => ({ type, key: UmbId.new(), path, body })));
89
+ this.finishChange();
67
90
  }
68
91
  removeMessageByKey(key) {
92
+ this.initiateChange();
69
93
  this.#messages.removeOne(key);
94
+ this.finishChange();
70
95
  }
71
96
  removeMessageByKeys(keys) {
97
+ if (keys.length === 0)
98
+ return;
99
+ this.initiateChange();
72
100
  this.#messages.filter((x) => keys.indexOf(x.key) === -1);
101
+ this.finishChange();
73
102
  }
74
103
  removeMessagesByType(type) {
104
+ this.initiateChange();
75
105
  this.#messages.filter((x) => x.type !== type);
106
+ this.finishChange();
76
107
  }
77
108
  removeMessagesByPath(path) {
109
+ this.initiateChange();
78
110
  this.#messages.filter((x) => x.path !== path);
111
+ this.finishChange();
79
112
  }
80
113
  removeMessagesAndDescendantsByPath(path) {
114
+ this.initiateChange();
81
115
  this.#messages.filter((x) => MatchPathOrDescendantPath(x.path, path));
116
+ this.finishChange();
82
117
  }
83
118
  removeMessagesByTypeAndPath(type, path) {
84
119
  //path = path.toLowerCase();
120
+ this.initiateChange();
85
121
  this.#messages.filter((x) => !(x.type === type && x.path === path));
122
+ this.finishChange();
86
123
  }
87
124
  #translatePath(path) {
88
125
  //path = path.toLowerCase();
@@ -98,6 +135,7 @@ export class UmbValidationMessagesManager {
98
135
  }
99
136
  #translators;
100
137
  addTranslator(translator) {
138
+ this.initiateChange();
101
139
  if (this.#translators.indexOf(translator) === -1) {
102
140
  this.#translators.push(translator);
103
141
  }
@@ -111,6 +149,7 @@ export class UmbValidationMessagesManager {
111
149
  this.#messages.updateOne(msg.key, { path: newPath });
112
150
  }
113
151
  }
152
+ this.finishChange();
114
153
  }
115
154
  removeTranslator(translator) {
116
155
  const index = this.#translators.indexOf(translator);
@@ -27,8 +27,6 @@ export class UmbFormControlValidator extends UmbControllerBase {
27
27
  }
28
28
  });
29
29
  this.#control = formControl;
30
- this.#control.addEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
31
- this.#control.addEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
32
30
  }
33
31
  get isValid() {
34
32
  return this.#isValid;
@@ -68,12 +66,18 @@ export class UmbFormControlValidator extends UmbControllerBase {
68
66
  }
69
67
  hostConnected() {
70
68
  super.hostConnected();
69
+ this.#control.addEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
70
+ this.#control.addEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
71
71
  if (this.#context) {
72
72
  this.#context.addValidator(this);
73
73
  }
74
74
  }
75
75
  hostDisconnected() {
76
76
  super.hostDisconnected();
77
+ if (this.#control) {
78
+ this.#control.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
79
+ this.#control.removeEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
80
+ }
77
81
  if (this.#context) {
78
82
  this.#context.removeValidator(this);
79
83
  // Remove any messages that this validator has added:
@@ -84,11 +88,9 @@ export class UmbFormControlValidator extends UmbControllerBase {
84
88
  }
85
89
  }
86
90
  destroy() {
91
+ super.destroy();
87
92
  if (this.#control) {
88
- this.#control.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#setInvalid);
89
- this.#control.removeEventListener(UmbValidationValidEvent.TYPE, this.#setValid);
90
93
  this.#control = undefined;
91
94
  }
92
- super.destroy();
93
95
  }
94
96
  }
@@ -48,6 +48,8 @@ export declare class UmbValidationController extends UmbControllerBase implement
48
48
  * A message with the path: '$.values[?(@.alias == 'my-property')].value.innerProperty', will for above example become '$.innerProperty' for the local Validation Context.
49
49
  */
50
50
  setDataPath(dataPath: string): void;
51
+ hostConnected(): void;
52
+ hostDisconnected(): void;
51
53
  /**
52
54
  * Get if this context is valid.
53
55
  * Notice this does not verify the validity.
@@ -106,11 +106,12 @@ export class UmbValidationController extends UmbControllerBase {
106
106
  this.setTranslationData(data);
107
107
  });
108
108
  this.observe(parent.messages.messagesOfPathAndDescendant(dataPath), (msgs) => {
109
+ this.messages.initiateChange();
109
110
  //this.messages.appendMessages(msgs);
110
111
  if (this.#parentMessages) {
111
112
  // Remove the local messages that does not exist in the parent anymore:
112
113
  const toRemove = this.#parentMessages.filter((msg) => !msgs.find((m) => m.key === msg.key));
113
- this.#parent.messages.removeMessageByKeys(toRemove.map((msg) => msg.key));
114
+ this.messages.removeMessageByKeys(toRemove.map((msg) => msg.key));
114
115
  }
115
116
  this.#parentMessages = msgs;
116
117
  msgs.forEach((msg) => {
@@ -121,10 +122,12 @@ export class UmbValidationController extends UmbControllerBase {
121
122
  // Notice, the local message uses the same key. [NL]
122
123
  this.messages.addMessage(msg.type, path, msg.body, msg.key);
123
124
  });
125
+ this.messages.finishChange();
124
126
  }, 'observeParentMessages');
125
127
  this.observe(this.messages.messages, (msgs) => {
126
128
  if (!this.#parent)
127
129
  return;
130
+ this.#parent.messages.initiateChange();
128
131
  //this.messages.appendMessages(msgs);
129
132
  if (this.#localMessages) {
130
133
  // Remove the parent messages that does not exist locally anymore:
@@ -141,10 +144,23 @@ export class UmbValidationController extends UmbControllerBase {
141
144
  // Notice, the parent message uses the same key. [NL]
142
145
  this.#parent.messages.addMessage(msg.type, path, msg.body, msg.key);
143
146
  });
147
+ this.#parent.messages.finishChange();
144
148
  }, 'observeLocalMessages');
145
149
  }).skipHost();
146
150
  // Notice skipHost ^^, this is because we do not want it to consume it self, as this would be a match for this consumption, instead we will look at the parent and above. [NL]
147
151
  }
152
+ hostConnected() {
153
+ super.hostConnected();
154
+ if (this.#parent) {
155
+ this.#parent.addValidator(this);
156
+ }
157
+ }
158
+ hostDisconnected() {
159
+ super.hostDisconnected();
160
+ if (this.#parent) {
161
+ this.#parent.removeValidator(this);
162
+ }
163
+ }
148
164
  /**
149
165
  * Get if this context is valid.
150
166
  * Notice this does not verify the validity.
@@ -195,15 +211,22 @@ export class UmbValidationController extends UmbControllerBase {
195
211
  // TODO: clear server messages here?, well maybe only if we know we will get new server messages? Do the server messages hook into the system like another validator?
196
212
  this.#validationMode = true;
197
213
  const resultsStatus = await Promise.all(this.#validators.map((v) => v.validate())).then(() => true, () => false);
198
- if (!this.messages) {
214
+ /*if (this.#validators.length === 0 && resultsStatus === false) {
215
+ throw new Error('No validators to validate, but validation failed');
216
+ }*/
217
+ if (this.messages === undefined) {
199
218
  // This Context has been destroyed while is was validating, so we should not continue.
200
219
  return Promise.reject();
201
220
  }
221
+ const hasMessages = this.messages.getHasAnyMessages();
202
222
  // If we have any messages then we are not valid, otherwise lets check the validation results: [NL]
203
223
  // This enables us to keep client validations though UI is not present anymore — because the client validations got defined as messages. [NL]
204
- const isValid = this.messages.getHasAnyMessages() ? false : resultsStatus;
224
+ const isValid = hasMessages ? false : resultsStatus;
205
225
  this.#isValid = isValid;
206
226
  if (isValid === false) {
227
+ /*if (hasMessages === false && resultsStatus === false) {
228
+ throw new Error('Missing validation messages to represent why a child validation context is invalid.');
229
+ }*/
207
230
  // Focus first invalid element:
208
231
  this.focusFirstInvalidElement();
209
232
  return Promise.reject();
@@ -237,6 +260,7 @@ export class UmbValidationController extends UmbControllerBase {
237
260
  this.#validators = [];
238
261
  }
239
262
  destroy() {
263
+ this.#providerCtrl?.destroy();
240
264
  this.#providerCtrl = undefined;
241
265
  if (this.#parent) {
242
266
  this.#parent.removeValidator(this);
@@ -4,4 +4,4 @@
4
4
  * @param {string} path - the JSON path to the value that should be found
5
5
  * @returns {unknown} - the found value.
6
6
  */
7
- export declare function GetValueByJsonPath(data: unknown, path: string): unknown;
7
+ export declare function GetValueByJsonPath<ReturnType = unknown>(data: unknown, path: string): ReturnType | undefined;
@@ -5,7 +5,12 @@
5
5
  * @returns {unknown} - the found value.
6
6
  */
7
7
  export function GetValueByJsonPath(data, path) {
8
+ if (path === '$')
9
+ return data;
8
10
  // strip $ from the path:
11
+ if (path.startsWith('$[')) {
12
+ return _GetNextArrayEntryFromPath(data, path.slice(2));
13
+ }
9
14
  const strippedPath = path.startsWith('$.') ? path.slice(2) : path;
10
15
  // get value from the path:
11
16
  return GetNextPropertyValueFromPath(data, strippedPath);
@@ -36,48 +41,59 @@ function GetNextPropertyValueFromPath(data, path) {
36
41
  return value;
37
42
  // if the value is an array, get the value at the index:
38
43
  if (Array.isArray(value)) {
39
- // get the value until the next ']', the value can be anything in between the brackets:
40
- const lookupEnd = rest.match(/\]/);
41
- if (!lookupEnd)
44
+ return _GetNextArrayEntryFromPath(value, rest);
45
+ }
46
+ else {
47
+ // continue with the rest of the path:
48
+ return GetNextPropertyValueFromPath(value, rest);
49
+ }
50
+ }
51
+ /**
52
+ * @private
53
+ * @param {object} array - object to traverse for the value.
54
+ * @param {string} path - the JSON path to the value that should be found, notice without the starting '['
55
+ * @returns {unknown} - the found value.
56
+ */
57
+ function _GetNextArrayEntryFromPath(array, path) {
58
+ if (!array)
59
+ return undefined;
60
+ // get the value until the next ']', the value can be anything in between the brackets:
61
+ const lookupEnd = path.match(/\]/);
62
+ if (!lookupEnd)
63
+ return undefined;
64
+ // get everything before the match:
65
+ const entryPointer = path.slice(0, lookupEnd.index);
66
+ // check if the entryPointer is a JSON Path Filter ( starting with ?( and ending with ) ):
67
+ if (entryPointer.startsWith('?(') && entryPointer.endsWith(')')) {
68
+ // get the filter from the entryPointer:
69
+ // get the filter as a function:
70
+ const jsFilter = JsFilterFromJsonPathFilter(entryPointer);
71
+ // find the index of the value that matches the filter:
72
+ const index = array.findIndex(jsFilter[0]);
73
+ // if the index is -1, return undefined:
74
+ if (index === -1)
42
75
  return undefined;
43
- // get everything before the match:
44
- const entryPointer = rest.slice(0, lookupEnd.index);
45
- // check if the entryPointer is a JSON Path Filter ( starting with ?( and ending with ) ):
46
- if (entryPointer.startsWith('?(') && entryPointer.endsWith(')')) {
47
- // get the filter from the entryPointer:
48
- // get the filter as a function:
49
- const jsFilter = JsFilterFromJsonPathFilter(entryPointer);
50
- // find the index of the value that matches the filter:
51
- const index = value.findIndex(jsFilter[0]);
52
- // if the index is -1, return undefined:
53
- if (index === -1)
54
- return undefined;
55
- // get the value at the index:
56
- const data = value[index];
57
- // Check for safety:
58
- if (lookupEnd.index === undefined || lookupEnd.index + 1 >= rest.length) {
59
- return data;
60
- }
61
- // continue with the rest of the path:
62
- return GetNextPropertyValueFromPath(data, rest.slice(lookupEnd.index + 2)) ?? data;
63
- }
64
- else {
65
- // get the value at the index:
66
- const indexAsNumber = parseInt(entryPointer);
67
- if (isNaN(indexAsNumber))
68
- return undefined;
69
- const data = value[indexAsNumber];
70
- // Check for safety:
71
- if (lookupEnd.index === undefined || lookupEnd.index + 1 >= rest.length) {
72
- return data;
73
- }
74
- // continue with the rest of the path:
75
- return GetNextPropertyValueFromPath(data, rest.slice(lookupEnd.index + 2)) ?? data;
76
+ // get the value at the index:
77
+ const entryData = array[index];
78
+ // Check for safety:
79
+ if (lookupEnd.index === undefined || lookupEnd.index + 1 >= path.length) {
80
+ return entryData;
76
81
  }
82
+ // continue with the rest of the path:
83
+ return GetNextPropertyValueFromPath(entryData, path.slice(lookupEnd.index + 2)) ?? entryData;
77
84
  }
78
85
  else {
86
+ // get the value at the index:
87
+ const indexAsNumber = parseInt(entryPointer);
88
+ if (isNaN(indexAsNumber))
89
+ return undefined;
90
+ const entryData = array[indexAsNumber];
91
+ // Check for safety:
92
+ if (lookupEnd.index === undefined || lookupEnd.index + 1 >= path.length) {
93
+ return entryData;
94
+ }
79
95
  // continue with the rest of the path:
80
- return GetNextPropertyValueFromPath(value, rest);
96
+ return GetNextPropertyValueFromPath(entryData, path.slice(lookupEnd.index + 2)) ?? entryData;
81
97
  }
82
98
  }
83
99
  /**
@@ -29,7 +29,10 @@ export class UmbSubmittableWorkspaceContextBase extends UmbContextBase {
29
29
  this.#isNew = new UmbBooleanState(undefined);
30
30
  this.isNew = this.#isNew.asObservable();
31
31
  this.routes = new UmbWorkspaceRouteManager(this);
32
- this.#rejectSubmit = () => {
32
+ this.#rejectSubmit = ( /*error: any*/) => {
33
+ /*if (error) {
34
+ throw new Error(error);
35
+ }*/
33
36
  if (this.#submitPromise) {
34
37
  // TODO: Capture the validation contexts messages on open, and then reset to them in this case. [NL]
35
38
  this.#submitReject?.();
@@ -73,7 +76,6 @@ export class UmbSubmittableWorkspaceContextBase extends UmbContextBase {
73
76
  * @returns Promise that resolves to void when the validation is complete.
74
77
  */
75
78
  async validate() {
76
- //return this.validation.validate();
77
79
  return Promise.all(this.#validationContexts.map((context) => context.validate()));
78
80
  }
79
81
  async requestSubmit() {
@@ -89,7 +91,12 @@ export class UmbSubmittableWorkspaceContextBase extends UmbContextBase {
89
91
  });
90
92
  this.validate().then(async () => {
91
93
  onValid().then(this.#completeSubmit, this.#rejectSubmit);
92
- }, async () => {
94
+ }, async ( /*error*/) => {
95
+ /*if (error) {
96
+ throw new Error(error);
97
+ }*/
98
+ // TODO: Implement developer-mode logging here. [NL]
99
+ console.warn('Validation failed because of these validation messages still begin present: ', this.#validationContexts.flatMap((x) => x.messages.getMessages()));
93
100
  onInvalid().then(this.#resolveSubmit, this.#rejectSubmit);
94
101
  });
95
102
  return this.#submitPromise;
@@ -75,9 +75,9 @@ export class UmbDocumentBlueprintServerDataSource {
75
75
  values: data.values.map((value) => {
76
76
  return {
77
77
  editorAlias: value.editorAlias,
78
- alias: value.alias,
79
78
  culture: value.culture || null,
80
79
  segment: value.segment || null,
80
+ alias: value.alias,
81
81
  value: value.value,
82
82
  };
83
83
  }),