From 8d16f8711c35092a3fd3563395187044b8b49743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sun, 25 Feb 2024 19:28:54 +0100 Subject: [PATCH 001/152] validation api package --- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/libs/validation-api/form-control.mixin.ts | 3 +++ src/Umbraco.Web.UI.Client/src/libs/validation-api/index.ts | 1 + src/Umbraco.Web.UI.Client/tsconfig.json | 1 + 4 files changed, 6 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/libs/validation-api/form-control.mixin.ts create mode 100644 src/Umbraco.Web.UI.Client/src/libs/validation-api/index.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index e8e232ac2d..b085a88834 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -13,6 +13,7 @@ "./extension-api": "./dist-cms/libs/extension-api/index.js", "./localization-api": "./dist-cms/libs/localization-api/index.js", "./observable-api": "./dist-cms/libs/observable-api/index.js", + "./validation-api": "./dist-cms/libs/validation-api/index.js", "./action": "./dist-cms/packages/core/action/index.js", "./audit-log": "./dist-cms/packages/audit-log/index.js", "./auth": "./dist-cms/packages/core/auth/index.js", diff --git a/src/Umbraco.Web.UI.Client/src/libs/validation-api/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/validation-api/form-control.mixin.ts new file mode 100644 index 0000000000..8053c60b94 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/validation-api/form-control.mixin.ts @@ -0,0 +1,3 @@ +import { FormControlMixin } from "@umbraco-cms/backoffice/external/uui"; + +export const UmbFormControlMixin = FormControlMixin; diff --git a/src/Umbraco.Web.UI.Client/src/libs/validation-api/index.ts b/src/Umbraco.Web.UI.Client/src/libs/validation-api/index.ts new file mode 100644 index 0000000000..0635d528ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/validation-api/index.ts @@ -0,0 +1 @@ +export * from './form-control.mixin.js'; diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index d685b5fa3d..c3d86f8901 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -31,6 +31,7 @@ "@umbraco-cms/backoffice/extension-api": ["./src/libs/extension-api/index.ts"], "@umbraco-cms/backoffice/localization-api": ["./src/libs/localization-api/index.ts"], "@umbraco-cms/backoffice/observable-api": ["./src/libs/observable-api/index.ts"], + "@umbraco-cms/backoffice/validation-api": ["./src/libs/validation-api/index.ts"], "@umbraco-cms/backoffice/action": ["./src/packages/core/action/index.ts"], "@umbraco-cms/backoffice/audit-log": ["./src/packages/audit-log/index.ts"], "@umbraco-cms/backoffice/auth": ["./src/packages/core/auth/index.ts"], From a1179ed7afa96d67b603e130280201d548083408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sun, 25 Feb 2024 19:29:16 +0100 Subject: [PATCH 002/152] list min max validation --- .../property-editor-ui-block-list.element.ts | 32 ++++++++++++------- .../block/context/block-entries.context.ts | 4 +++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index c6f17862c9..c00d65b091 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -1,3 +1,4 @@ +import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation-api'; import { UmbBlockListManagerContext } from '../../context/block-list-manager.context.js'; import '../../components/block-list-entry/index.js'; import type { UmbBlockListEntryElement } from '../../components/block-list-entry/index.js'; @@ -33,7 +34,10 @@ const SORTER_CONFIG: UmbSorterConfig(this, { ...SORTER_CONFIG, @@ -117,17 +121,17 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement constructor() { super(); - /* - this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { - this.observe( - propertyContext?.alias, - (alias) => { - this.#catalogueModal.setUniquePathValue('propertyAlias', alias); - }, - 'observePropertyAlias', - ); - }); - */ + this.addValidator( + 'rangeUnderflow', + () => this.localize.term('validation_entriesShort'), + () => !!this._limitMin && this.#entriesContext.getLength() < this._limitMin, + ); + + this.addValidator( + 'rangeOverflow', + () => this.localize.term('validation_entriesExceed'), + () => !!this._limitMax && this.#entriesContext.getLength() > this._limitMax, + ); // TODO: Prevent initial notification from these observes: this.observe(this.#managerContext.layouts, (layouts) => { @@ -185,6 +189,10 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement */ } + protected getFormElement() { + return undefined; + } + render() { let createPath: string | undefined; if (this._blocks?.length === 1) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts index 6ff493d5a0..6dce59daf6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts @@ -33,6 +33,10 @@ export abstract class UmbBlockEntriesContext< readonly layoutEntries = this._layoutEntries.asObservable(); readonly layoutEntriesLength = this._layoutEntries.asObservablePart((x) => x.length); + getLength() { + return this._layoutEntries.getValue().length; + } + constructor(host: UmbControllerHost, blockManagerContextToken: BlockManagerContextTokenType) { super(host, UMB_BLOCK_ENTRIES_CONTEXT.toString()); From 5f8c336d0949d1069b764d77866526f9a7ebe9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 26 Feb 2024 09:24:57 +0100 Subject: [PATCH 003/152] min max for block grid editor --- .../block-grid-entries.element.ts | 25 ++++++++++++++++--- .../context/block-grid-entries.context.ts | 14 +++++++++++ .../context/block-grid-manager.context.ts | 11 ++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts index 62dfa9aff5..c9172eecad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts @@ -1,10 +1,11 @@ +import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation-api'; +import { UmbBlockGridEntriesContext } from '../../context/block-grid-entries.context.js'; +import type { UmbBlockGridEntryElement } from '../block-grid-entry/index.js'; import { getAccumulatedValueOfIndex, getInterpolatedIndexOfPositionInWeightMap, isWithinRect, } from '@umbraco-cms/backoffice/utils'; -import { UmbBlockGridEntriesContext } from '../../context/block-grid-entries.context.js'; -import type { UmbBlockGridEntryElement } from '../block-grid-entry/index.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid'; import { html, customElement, state, repeat, css, property } from '@umbraco-cms/backoffice/external/lit'; @@ -104,7 +105,7 @@ const SORTER_CONFIG: UmbSorterConfig this.localize.term('validation_entriesShort'), + () => !!this.#context.getMinAllowed() && this.#context.getLength() < this.#context.getMinAllowed(), + ); + + this.addValidator( + 'rangeOverflow', + () => this.localize.term('validation_entriesExceed'), + () => !!this.#context.getMaxAllowed() && this.#context.getLength() > this.#context.getMaxAllowed(), + ); + this.observe(this.#context.layoutEntries, (layoutEntries) => { const oldValue = this._layoutEntries; this.#sorter.setModel(layoutEntries); this._layoutEntries = layoutEntries; + this._runValidators(); this.requestUpdate('layoutEntries', oldValue); }); @@ -182,6 +197,10 @@ export class UmbBlockGridEntriesElement extends UmbLitElement { }); } + protected getFormElement(): HTMLElement | undefined { + return undefined; + } + // TODO: Missing ability to jump directly to creating a Block, when there is only one Block Type. render() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts index 039d16f8e9..4872213106 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts @@ -49,6 +49,20 @@ export class UmbBlockGridEntriesContext return this.#layoutColumns.getValue(); } + getMinAllowed() { + if (this.#areaKey) { + return this.#areaType?.minAllowed ?? 0; + } + return this._manager?.getMinAllowed() ?? 0; + } + + getMaxAllowed() { + if (this.#areaKey) { + return this.#areaType?.minAllowed ?? Infinity; + } + return this._manager?.getMinAllowed() ?? Infinity; + } + getLayoutContainerElement() { return this.getHostElement().shadowRoot?.querySelector('.umb-block-grid__layout-container') as | HTMLElement diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index f5f102904c..20e8c66f43 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -4,6 +4,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; import { type UmbBlockDataType, UmbBlockManagerContext } from '@umbraco-cms/backoffice/block'; import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type'; +import type { NumberRangeValueType } from '@umbraco-cms/backoffice/models'; export const UMB_BLOCK_GRID_DEFAULT_LAYOUT_STYLESHEET = '/umbraco/backoffice/css/umbraco-blockgridlayout.css'; @@ -32,6 +33,16 @@ export class UmbBlockGridManagerContext< return this.#blockGroups.value; } + getMinAllowed() { + return this._editorConfiguration.getValue()?.getValueByAlias('validationLimit')?.min ?? 0; + } + + getMaxAllowed() { + return ( + this._editorConfiguration.getValue()?.getValueByAlias('validationLimit')?.max ?? Infinity + ); + } + create( contentElementTypeKey: string, partialLayoutEntry?: Omit, From fffc2e3b82eb4b11b0764d5dbdb26bdde5741b87 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 2 Jul 2024 11:42:29 +0200 Subject: [PATCH 004/152] wip: implement search in member picker --- .../member-picker-modal.element.ts | 87 ++++++++++++++++--- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index e7d0046b8e..74e7cc07a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -1,9 +1,13 @@ import { UmbMemberCollectionRepository } from '../../collection/index.js'; import type { UmbMemberDetailModel } from '../../types.js'; +import { UmbMemberSearchProvider } from '../../search/member.search-provider.js'; +import type { UmbMemberItemModel } from '../../repository/index.js'; import type { UmbMemberPickerModalValue, UmbMemberPickerModalData } from './member-picker-modal.token.js'; -import { html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; +import { html, customElement, state, repeat, css } from '@umbraco-cms/backoffice/external/lit'; +import { UmbSelectionManager, debounce } from '@umbraco-cms/backoffice/utils'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @customElement('umb-member-picker-modal') export class UmbMemberPickerModalElement extends UmbModalBaseElement< @@ -13,8 +17,15 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< @state() private _members: Array = []; + @state() + private _searchQuery: string = ''; + + @state() + private _searchResult: Array = []; + #collectionRepository = new UmbMemberCollectionRepository(this); #selectionManager = new UmbSelectionManager(this); + #searchProvider = new UmbMemberSearchProvider(this); override connectedCallback(): void { super.connectedCallback(); @@ -36,6 +47,19 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< } } + #onSearchInput(event: UUIInputEvent) { + const value = event.target.value as string; + this._searchQuery = value; + this.#debouncedSearch(); + } + + #debouncedSearch = debounce(this.#search, 300); + + async #search() { + const { data } = await this.#searchProvider.search({ query: this._searchQuery }); + this._searchResult = data?.items ?? []; + } + #submit() { this.value = { selection: this.#selectionManager.getSelection() }; this.modalContext?.submit(); @@ -48,21 +72,14 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< override render() { return html` + ${this.#renderSearch()} ${repeat( this.#filteredMembers, (item) => item.unique, - (item) => html` - this.#selectionManager.select(item.unique)} - @deselected=${() => this.#selectionManager.deselect(item.unique)} - ?selected=${this.#selectionManager.isSelected(item.unique)}> - - - `, + (item) => this.#renderMemberItem(item), )} +
`; } + + #renderSearch() { + return html` + + `; + } + + #renderSearchResult() { + return html` + ${repeat( + this._searchResult, + (item) => item.unique, + (item) => this.#renderMemberItem(item), + )} + `; + } + + #renderMemberItem(item: UmbMemberItemModel | UmbMemberDetailModel) { + return html` + this.#selectionManager.select(item.unique)} + @deselected=${() => this.#selectionManager.deselect(item.unique)} + ?selected=${this.#selectionManager.isSelected(item.unique)}> + + + `; + } + + static override styles = [ + UmbTextStyles, + css` + #search { + padding-bottom: var(--uui-size-space-5); + border-bottom: 1px solid var(--uui-color-divider); + } + + #search-input { + width: 100%; + } + `, + ]; } export default UmbMemberPickerModalElement; From a27459d0fd16cb097ab9f9b7c97f32dcf657d0fc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 2 Jul 2024 12:17:15 +0200 Subject: [PATCH 005/152] hide items when searching --- .../member-picker-modal.element.ts | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index 74e7cc07a5..c6f6479671 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -3,7 +3,7 @@ import type { UmbMemberDetailModel } from '../../types.js'; import { UmbMemberSearchProvider } from '../../search/member.search-provider.js'; import type { UmbMemberItemModel } from '../../repository/index.js'; import type { UmbMemberPickerModalValue, UmbMemberPickerModalData } from './member-picker-modal.token.js'; -import { html, customElement, state, repeat, css } from '@umbraco-cms/backoffice/external/lit'; +import { html, customElement, state, repeat, css, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbSelectionManager, debounce } from '@umbraco-cms/backoffice/utils'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; @@ -56,6 +56,11 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #debouncedSearch = debounce(this.#search, 300); async #search() { + if (!this._searchQuery) { + this._searchResult = []; + return; + } + const { data } = await this.#searchProvider.search({ query: this._searchQuery }); this._searchResult = data?.items ?? []; } @@ -71,14 +76,7 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< override render() { return html` - - ${this.#renderSearch()} - ${repeat( - this.#filteredMembers, - (item) => item.unique, - (item) => this.#renderMemberItem(item), - )} - + ${this.#renderSearch()} ${this.#renderItems()}
@@ -91,12 +89,22 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< `; } + #renderItems() { + if (this._searchQuery) return nothing; + return html` + ${repeat( + this.#filteredMembers, + (item) => item.unique, + (item) => this.#renderMemberItem(item), + )} + `; + } + #renderSearch() { return html` - + +
+ ${this.#renderSearchResult()} `; } @@ -126,14 +134,17 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< static override styles = [ UmbTextStyles, css` - #search { - padding-bottom: var(--uui-size-space-5); - border-bottom: 1px solid var(--uui-color-divider); - } - #search-input { width: 100%; } + + #search-divider { + width: 100%; + height: 1px; + background-color: var(--uui-color-divider); + margin-top: var(--uui-size-space-5); + margin-bottom: var(--uui-size-space-3); + } `, ]; } From 6f9d6733f9dbcf59ec9941a95bc387dec10e731d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 2 Jul 2024 12:20:10 +0200 Subject: [PATCH 006/152] add search icon --- .../member-picker-modal/member-picker-modal.element.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index c6f6479671..11018a788b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -102,7 +102,11 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #renderSearch() { return html` - + +
+ +
+
${this.#renderSearchResult()} `; From 6fa049b4cfac775628c72f016680e9b94d960bc2 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 2 Jul 2024 16:35:43 +0200 Subject: [PATCH 007/152] show load indicator --- .../member-picker-modal.element.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index 11018a788b..211b3facbd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -23,6 +23,9 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< @state() private _searchResult: Array = []; + @state() + private _searching = false; + #collectionRepository = new UmbMemberCollectionRepository(this); #selectionManager = new UmbSelectionManager(this); #searchProvider = new UmbMemberSearchProvider(this); @@ -50,6 +53,7 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #onSearchInput(event: UUIInputEvent) { const value = event.target.value as string; this._searchQuery = value; + this._searching = true; this.#debouncedSearch(); } @@ -58,11 +62,13 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< async #search() { if (!this._searchQuery) { this._searchResult = []; + this._searching = false; return; } const { data } = await this.#searchProvider.search({ query: this._searchQuery }); this._searchResult = data?.items ?? []; + this._searching = false; } #submit() { @@ -104,7 +110,9 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< return html`
- + ${this._searching + ? html`` + : html``}
@@ -149,6 +157,11 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< margin-top: var(--uui-size-space-5); margin-bottom: var(--uui-size-space-3); } + + #search-indicator { + margin-left: 7px; + margin-top: 4px; + } `, ]; } From 1bae82758ea29c21512aa90da671679c6ae49330 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 2 Jul 2024 19:12:05 +0200 Subject: [PATCH 008/152] add clear search button --- .../member-picker-modal.element.ts | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index 211b3facbd..5ed953118a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -53,6 +53,13 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #onSearchInput(event: UUIInputEvent) { const value = event.target.value as string; this._searchQuery = value; + + if (!this._searchQuery) { + this._searchResult = []; + this._searching = false; + return; + } + this._searching = true; this.#debouncedSearch(); } @@ -60,12 +67,6 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #debouncedSearch = debounce(this.#search, 300); async #search() { - if (!this._searchQuery) { - this._searchResult = []; - this._searching = false; - return; - } - const { data } = await this.#searchProvider.search({ query: this._searchQuery }); this._searchResult = data?.items ?? []; this._searching = false; @@ -80,6 +81,11 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< this.modalContext?.reject(); } + #onSearchClear() { + this._searchQuery = ''; + this._searchResult = []; + } + override render() { return html` ${this.#renderSearch()} ${this.#renderItems()} @@ -108,12 +114,18 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #renderSearch() { return html` - +
${this._searching ? html`` : html``}
+ +
+ + + +
${this.#renderSearchResult()} From c95f0a6640d95f9ddf62a266620aa8d40edee648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 6 Aug 2024 14:34:57 +0200 Subject: [PATCH 009/152] comments --- .../documents/documents/workspace/document-workspace.context.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 24eb235fce..6aded1e7e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -571,6 +571,7 @@ export class UmbDocumentWorkspaceContext async #performSaveOrCreate(saveData: UmbDocumentDetailModel): Promise { if (this.getIsNew()) { + // Create: const parent = this.#parent.getValue(); if (!parent) throw new Error('Parent is not set'); @@ -591,6 +592,7 @@ export class UmbDocumentWorkspaceContext }); eventContext.dispatchEvent(event); } else { + // Save: const { data, error } = await this.repository.save(saveData); if (!data || error) { console.error('Error saving document', error); From 9a71981fd5aa6baf18d5426c4f90ade79ba884a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 6 Aug 2024 14:35:45 +0200 Subject: [PATCH 010/152] align order of arguments --- .../documents/documents/workspace/document-workspace.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 6aded1e7e9..7fb8d724d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -604,8 +604,8 @@ export class UmbDocumentWorkspaceContext const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); const event = new UmbRequestReloadStructureForEntityEvent({ - unique: this.getUnique()!, entityType: this.getEntityType(), + unique: this.getUnique()!, }); eventContext.dispatchEvent(event); From 8a683fc93062e6ff20087980230854e6d3bcf95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 6 Aug 2024 14:45:18 +0200 Subject: [PATCH 011/152] focus method --- .../text-box/property-editor-ui-text-box.element.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 2e3397c100..6de904429a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -50,6 +50,10 @@ export class UmbPropertyEditorUITextBoxElement this.addFormControlElement(this.shadowRoot!.querySelector('uui-input')!); } + override focus() { + return this.shadowRoot?.querySelector('uui-input')?.focus(); + } + #onInput(e: InputEvent) { const newValue = (e.target as HTMLInputElement).value; if (newValue === this.value) return; From b54a08c237654871d1b886a57ee47e916ed5f28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 6 Aug 2024 15:44:33 +0200 Subject: [PATCH 012/152] data-path as attribute --- .../workspace/views/edit/content-editor-properties.element.ts | 2 +- .../src/packages/core/property/property/property.element.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts index 18173790f5..a52eaf06d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts @@ -74,7 +74,7 @@ export class UmbContentWorkspaceViewEditPropertiesElement extends UmbLitElement (property, index) => html` `, ) : ''; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts index 2eda498629..ba189fbf2a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts @@ -133,7 +133,7 @@ export class UmbPropertyElement extends UmbLitElement { * @attr * @default '' */ - @property({ type: String, attribute: false }) + @property({ type: String, attribute: 'data-path' }) public set dataPath(dataPath: string | undefined) { this.#dataPath = dataPath; new UmbObserveValidationStateController(this, dataPath, (invalid) => { From d3f58e92edfaf70dfb2d6811163c05d1cf1093ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 6 Aug 2024 20:08:14 +0200 Subject: [PATCH 013/152] print data-path for debugging --- .../property-editor-config/property-editor-config.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts index 46a9136d23..0466721c24 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts @@ -46,7 +46,7 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement { (property) => property.alias, (property) => html` Date: Tue, 6 Aug 2024 21:40:37 +0200 Subject: [PATCH 014/152] impl translator --- .../documents/workspace/document-workspace.context.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 7fb8d724d7..2e833e2f84 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -52,7 +52,10 @@ import { UmbRequestReloadStructureForEntityEvent, } from '@umbraco-cms/backoffice/entity-action'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import { UmbServerModelValidationContext } from '@umbraco-cms/backoffice/validation'; +import { + UmbServerModelValidationContext, + UmbVariantValuesValidationMessageTranslator, +} from '@umbraco-cms/backoffice/validation'; import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbContentWorkspaceContext } from '@umbraco-cms/backoffice/content'; @@ -159,6 +162,8 @@ export class UmbDocumentWorkspaceContext constructor(host: UmbControllerHost) { super(host, UMB_DOCUMENT_WORKSPACE_ALIAS); + new UmbVariantValuesValidationMessageTranslator(this, this.#serverValidation); + this.observe(this.contentTypeUnique, (unique) => this.structure.loadType(unique)); this.observe(this.varies, (varies) => (this.#varies = varies)); From 3f7b9ed74b2efcbf92e284db17ea145db76a75bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 6 Aug 2024 21:40:58 +0200 Subject: [PATCH 015/152] outcommented concept --- .../src/packages/core/validation/mixins/form-control.mixin.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index 2fbeb54a11..2d44ebb916 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -131,6 +131,10 @@ export function UmbFormControlMixin< public set pristine(value: boolean) { if (this._pristine !== value) { this._pristine = value; + // Concept: maybe needed? Set all inner form controls to be 'touched' as well. [NL] + /*this.#formCtrlElements.forEach((el) => { + (el as any).pristine = value; + });*/ this.#dispatchValidationState(); } } From 267604c1394deade27cf076bdb4a452920305c0a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 7 Aug 2024 08:52:54 +0200 Subject: [PATCH 016/152] add build setup --- src/Umbraco.Web.UI.Client/package-lock.json | 1 + src/Umbraco.Web.UI.Client/package.json | 1 + .../src/packages/packages/package.json | 8 ++++++++ .../src/packages/packages/vite.config.ts | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/packages/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/packages/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index bfe157ce3a..408e9634c3 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -21,6 +21,7 @@ "./src/packages/media", "./src/packages/members", "./src/packages/multi-url-picker", + "./src/packages/package", "./src/packages/property-editors", "./src/packages/tags", "./src/packages/templating", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 766ac0c7bb..57ea897df1 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -140,6 +140,7 @@ "./src/packages/media", "./src/packages/members", "./src/packages/multi-url-picker", + "./src/packages/package", "./src/packages/property-editors", "./src/packages/tags", "./src/packages/templating", diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package.json b/src/Umbraco.Web.UI.Client/src/packages/packages/package.json new file mode 100644 index 0000000000..4f95a8575b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/package", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/vite.config.ts new file mode 100644 index 0000000000..859f550e82 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/packages'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ + dist, + entry: { + 'package/index': 'package/index.ts', + manifests: 'manifests.ts', + 'umbraco-package': 'umbraco-package.ts', + }, + }), +}); From 1e5dd1c0109643d0cceb578e5aaa334f33102f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 7 Aug 2024 11:15:45 +0200 Subject: [PATCH 017/152] rename server validation binder --- .../packages/core/property/property/property.element.ts | 6 +++--- ... bind-server-validation-to-form-control.controller.ts} | 8 +++++++- .../src/packages/core/validation/controllers/index.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/{bind-validation-message-to-form-control.controller.ts => bind-server-validation-to-form-control.controller.ts} (90%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts index ba189fbf2a..b38996f3f3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts @@ -5,7 +5,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { - UmbBindValidationMessageToFormControl, + UmbBindServerValidationToFormControl, UmbFormControlValidator, UmbObserveValidationStateController, } from '@umbraco-cms/backoffice/validation'; @@ -172,7 +172,7 @@ export class UmbPropertyElement extends UmbLitElement { #propertyContext = new UmbPropertyContext(this); #controlValidator?: UmbFormControlValidator; - #validationMessageBinder?: UmbBindValidationMessageToFormControl; + #validationMessageBinder?: UmbBindServerValidationToFormControl; #valueObserver?: UmbObserverController; #configObserver?: UmbObserverController; @@ -308,7 +308,7 @@ export class UmbPropertyElement extends UmbLitElement { this.#controlValidator = new UmbFormControlValidator(this, this._element as any, this.#dataPath); // We trust blindly that the dataPath is available at this stage. [NL] if (this.#dataPath) { - this.#validationMessageBinder = new UmbBindValidationMessageToFormControl( + this.#validationMessageBinder = new UmbBindServerValidationToFormControl( this, this._element as any, this.#dataPath, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-validation-message-to-form-control.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts similarity index 90% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-validation-message-to-form-control.controller.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts index 2bfc4e49c2..f0abe49189 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-validation-message-to-form-control.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts @@ -8,7 +8,11 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; const ctrlSymbol = Symbol(); const observeSymbol = Symbol(); -export class UmbBindValidationMessageToFormControl extends UmbControllerBase { +/** + * Binds server validation to a form control. + * This controller will add a custom error to the form control if the validation context has any messages for the specified data path. + */ +export class UmbBindServerValidationToFormControl extends UmbControllerBase { #context?: typeof UMB_VALIDATION_CONTEXT.TYPE; #control: UmbFormControlMixinInterface; @@ -26,6 +30,7 @@ export class UmbBindValidationMessageToFormControl extends UmbControllerBase { // If not valid lets see if we should remove server validation [NL] if (!jsonStringComparison(this.#value, value)) { this.#value = value; + console.log('remove messages'); // Only remove server validations from validation context [NL] this.#messages.forEach((message) => { if (message.type === 'server') { @@ -46,6 +51,7 @@ export class UmbBindValidationMessageToFormControl extends UmbControllerBase { context.messages.messagesOfTypeAndPath('server', dataPath), (messages) => { this.#messages = messages; + console.log('messages', messages); this.#isValid = messages.length === 0; if (!this.#isValid) { this.#setup(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts index e261d92414..35003793f3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts @@ -1,3 +1,3 @@ -export * from './bind-validation-message-to-form-control.controller.js'; +export * from './bind-server-validation-to-form-control.controller.js'; export * from './observe-validation-state.controller.js'; export * from './form-control-validator.controller.js'; From ece79783f78dd9cbc4aac6c467a0f3915f1f30db Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 7 Aug 2024 11:16:10 +0200 Subject: [PATCH 018/152] Update package.json --- src/Umbraco.Web.UI.Client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 57ea897df1..bfb25020ef 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -140,7 +140,7 @@ "./src/packages/media", "./src/packages/members", "./src/packages/multi-url-picker", - "./src/packages/package", + "./src/packages/packages", "./src/packages/property-editors", "./src/packages/tags", "./src/packages/templating", From b52b3cf8227c909e1027d4b4ece971c97835cb69 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 7 Aug 2024 11:18:24 +0200 Subject: [PATCH 019/152] clean up --- .../packages/packages/package-builder/manifests.ts | 12 ++---------- .../src/packages/packages/package-repo/manifests.ts | 12 ++---------- .../packages/package/repository/constants.ts | 2 ++ .../packages/package/repository/manifests.ts | 5 +---- 4 files changed, 7 insertions(+), 24 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts index d2d5da69af..40c3db214d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts @@ -1,9 +1,4 @@ -import type { - ManifestTypes, - ManifestWorkspace, - ManifestWorkspaceActions, - ManifestWorkspaceView, -} from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestTypes, ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; const workspace: ManifestWorkspace = { type: 'workspace', @@ -15,7 +10,4 @@ const workspace: ManifestWorkspace = { }, }; -const workspaceViews: Array = []; -const workspaceActions: Array = []; - -export const manifests: Array = [workspace, ...workspaceViews, ...workspaceActions]; +export const manifests: Array = [workspace]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts index ca3f976d04..9ee6b2ffe5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts @@ -1,9 +1,4 @@ -import type { - ManifestTypes, - ManifestWorkspace, - ManifestWorkspaceActions, - ManifestWorkspaceView, -} from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestTypes, ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; const workspace: ManifestWorkspace = { type: 'workspace', @@ -15,7 +10,4 @@ const workspace: ManifestWorkspace = { }, }; -const workspaceViews: Array = []; -const workspaceActions: Array = []; - -export const manifests: Array = [workspace, ...workspaceViews, ...workspaceActions]; +export const manifests: Array = [workspace]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/constants.ts new file mode 100644 index 0000000000..a73f790346 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/constants.ts @@ -0,0 +1,2 @@ +export const UMB_PACKAGE_REPOSITORY_ALIAS = 'Umb.Repository.Package'; +export const UMB_PACKAGE_STORE_ALIAS = 'Umb.Store.Package'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/manifests.ts index 4825a19fc6..72b80c8342 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/manifests.ts @@ -1,7 +1,6 @@ +import { UMB_PACKAGE_REPOSITORY_ALIAS, UMB_PACKAGE_STORE_ALIAS } from './constants.js'; import type { ManifestStore, ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_PACKAGE_REPOSITORY_ALIAS = 'Umb.Repository.Package'; - const repository: ManifestRepository = { type: 'repository', alias: UMB_PACKAGE_REPOSITORY_ALIAS, @@ -9,8 +8,6 @@ const repository: ManifestRepository = { api: () => import('./package.repository.js'), }; -export const UMB_PACKAGE_STORE_ALIAS = 'Umb.Store.Package'; - const store: ManifestStore = { type: 'store', alias: UMB_PACKAGE_STORE_ALIAS, From 162db5105af3ef5e4127c2c2f935e1e65575f51e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 7 Aug 2024 11:50:11 +0200 Subject: [PATCH 020/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index e0e60938d3..ef46e7a6f4 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -21,7 +21,7 @@ "./src/packages/media", "./src/packages/members", "./src/packages/multi-url-picker", - "./src/packages/package", + "./src/packages/packages", "./src/packages/property-editors", "./src/packages/tags", "./src/packages/templating", @@ -7417,6 +7417,10 @@ "resolved": "src/packages/multi-url-picker", "link": true }, + "node_modules/@umbraco-backoffice/package": { + "resolved": "src/packages/packages", + "link": true + }, "node_modules/@umbraco-backoffice/property-editors": { "resolved": "src/packages/property-editors", "link": true @@ -21880,6 +21884,7 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, + "src/packages/packages": {}, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, From 240ccdd74674f4c9c3ca06f4000b939f83d3f3ce Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 7 Aug 2024 15:09:12 +0200 Subject: [PATCH 021/152] bundle log viewer package --- src/Umbraco.Web.UI.Client/package-lock.json | 6 ++++++ src/Umbraco.Web.UI.Client/package.json | 1 + .../src/packages/log-viewer/package.json | 8 ++++++++ .../src/packages/log-viewer/vite.config.ts | 12 ++++++++++++ 4 files changed, 27 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/log-viewer/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/log-viewer/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 3b86f9748e..a64191c00e 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -18,6 +18,7 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", + "./src/packages/log-viewer", "./src/packages/media", "./src/packages/members", "./src/packages/models-builder", @@ -7506,6 +7507,10 @@ "resolved": "src/packages/language", "link": true }, + "node_modules/@umbraco-backoffice/log-viewer": { + "resolved": "src/packages/log-viewer", + "link": true + }, "node_modules/@umbraco-backoffice/media": { "resolved": "src/packages/media", "link": true @@ -22749,6 +22754,7 @@ "src/packages/language": { "name": "@umbraco-backoffice/language" }, + "src/packages/log-viewer": {}, "src/packages/media": { "name": "@umbraco-backoffice/media" }, diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 5926e95761..3b053e41ac 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -137,6 +137,7 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", + "./src/packages/log-viewer", "./src/packages/media", "./src/packages/members", "./src/packages/models-builder", diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/package.json b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/package.json new file mode 100644 index 0000000000..b9eeb6c15c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/log-viewer", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/vite.config.ts new file mode 100644 index 0000000000..f7e77cd976 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/log-viewer'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); From 082683a8ac9233ae49ec8ecea8df3251a797381d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 7 Aug 2024 15:38:29 +0200 Subject: [PATCH 022/152] remove unnesecary comments --- ...variant-values-validation-message-translator.controller.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts index 7b10823bb4..71487cf29d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts @@ -19,21 +19,19 @@ export class UmbVariantValuesValidationMessageTranslator translate(path: string) { if (path.indexOf('$.values[') !== 0) { - // No translation anyway. return; } const pathEnd = path.indexOf(']'); if (pathEnd === -1) { - // No translation anyway. return; } // retrieve the number from the message values index: [NL] const index = parseInt(path.substring(9, pathEnd)); if (isNaN(index)) { - // No translation anyway. return; } + // Get the data from the validation request, the context holds that for us: [NL] const data = this.#context.getData(); From 9d4a5c09f9c6dcff4709070c0ba3764e56fd21fc Mon Sep 17 00:00:00 2001 From: mattbrailsford Date: Wed, 7 Aug 2024 13:14:48 +0100 Subject: [PATCH 023/152] Align table icons centrally --- .../src/packages/core/components/table/table.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts index ebb1da1e6f..203cfbcbbc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts @@ -204,7 +204,7 @@ export class UmbTableElement extends LitElement { if (this.config.hideIcon && !this.config.allowSelection) return; return html` - + ${when( this.config.allowSelection, () => @@ -236,7 +236,7 @@ export class UmbTableElement extends LitElement { if (this.config.hideIcon && !this.config.allowSelection) return; return html` - + ${when(!this.config.hideIcon, () => html``)} ${when( this.config.allowSelection, From 95a61c503d32583b55379ae06297fffb3ece7ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 7 Aug 2024 17:14:51 +0200 Subject: [PATCH 024/152] POC of validation translators --- .../server-model-validation.context.ts | 80 +++++++++++++++---- .../context/validation-messages.manager.ts | 6 +- .../form-control-validator.controller.ts | 4 + ...validation-message-translator.interface.ts | 7 +- ...alidation-message-translator.controller.ts | 9 ++- 5 files changed, 82 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts index f969b3d4eb..9fd0db8735 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts @@ -1,5 +1,6 @@ import type { UmbValidationMessageTranslator } from '../translators/validation-message-translator.interface.js'; import type { UmbValidator } from '../interfaces/validator.interface.js'; +import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from './server-model-validation.context-token.js'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; @@ -8,6 +9,17 @@ import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; type ServerFeedbackEntry = { path: string; messages: Array }; +/** This should ideally be generated by the server, but we currently don't generate error-modal-types. */ +interface ValidateErrorResponseBodyModel { + detail: string; + errors: Record>; + missingProperties: Array; + operationStatus: string; + status: number; + title: string; + type: string; +} + export class UmbServerModelValidationContext extends UmbContextBase implements UmbValidator @@ -24,9 +36,6 @@ export class UmbServerModelValidationContext } #translators: Array = []; - // Hold server feedback... - #serverFeedback: Array = []; - constructor(host: UmbControllerHost) { super(host, UMB_SERVER_MODEL_VALIDATION_CONTEXT); this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { @@ -43,7 +52,7 @@ export class UmbServerModelValidationContext async askServerForValidation(data: unknown, requestPromise: Promise>): Promise { this.#context?.messages.removeMessagesByType('server'); - this.#serverFeedback = []; + const serverFeedback: Array = []; this.#isValid = false; //this.#validatePromiseReject?.(); this.#validatePromise = new Promise((resolve) => { @@ -59,11 +68,29 @@ export class UmbServerModelValidationContext if (!this.#isValid) { // We are missing some typing here, but we will just go wild with 'as any': [NL] - const readErrorBody = (error as any).body; + const errorBody = (error as any).body as ValidateErrorResponseBodyModel; // Check if there are validation errors, since the error might be a generic ApiError - if (readErrorBody?.errors) { - Object.keys(readErrorBody.errors).forEach((path) => { - this.#serverFeedback.push({ path, messages: readErrorBody.errors[path] }); + if (errorBody?.errors) { + Object.keys(errorBody.errors).forEach((path) => { + serverFeedback.push({ path, messages: errorBody.errors[path] }); + }); + } + // Check if there are missing properties: + console.log('send data:', this.#data); + console.log('error response:', errorBody); + if (errorBody?.missingProperties) { + // Retrieve the variants of he send data, as those are the once we will declare as missing properties: + errorBody.missingProperties.forEach((alias) => { + this.#data.variants.forEach((variant: any) => { + serverFeedback.push({ + path: `$.values[${UmbDataPathPropertyValueFilter({ + alias: alias, + culture: variant.culture, + segment: variant.segment, + })}].value`, + messages: ['#validation_invalidEmpty'], + }); + }); }); } } @@ -72,26 +99,45 @@ export class UmbServerModelValidationContext this.#validatePromiseResolve = undefined; // Translate feedback: - this.#serverFeedback = this.#serverFeedback.flatMap(this.#executeTranslatorsOnFeedback); + serverFeedback.forEach(this.#executeTranslatorsOnFeedback); } #executeTranslatorsOnFeedback = (feedback: ServerFeedbackEntry) => { - return this.#translators.flatMap((translator) => { - let newPath: string | undefined; - if ((newPath = translator.translate(feedback.path))) { + if (!this.#context) { + throw new Error('No context available for translation.'); + } + const foundTranslator = this.#translators.find((translator) => { + const newPath = translator.translate(feedback.path); + if (newPath !== false) { // TODO: I might need to restructure this part for adjusting existing feedback with a part-translation. // Detect if some part is unhandled? // If so only make a partial translation on the feedback, add a message for the handled part. // then return [ of the partial translated feedback, and the partial handled part. ]; // TODO:Check if there was any temporary messages base on this path, like if it was partial-translated at one point.. - - this.#context?.messages.addMessages('server', newPath, feedback.messages); - // by not returning anything this feedback gets removed from server feedback.. - return []; + if (newPath !== undefined) { + this.#context!.messages.addMessages('server', newPath, feedback.messages); + return true; + } else { + // If the newPath is undefined, then there was a translation error and we should not keep the message. + throw new Error( + 'Validation Translator returned undefined, which means it failed to translate the path. Translator: ' + + translator.constructor.name + + ', Path: ' + + feedback.path, + ); + } } - return feedback; + return false; }); + if (foundTranslator) { + // Because this was successfully translated, we can remove the feedback from the server: + return []; + } else { + // Because this was not translated, we will carry over the message as is: + this.#context.messages.addMessages('server', feedback.path, feedback.messages); + return []; + } }; addTranslator(translator: UmbValidationMessageTranslator): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 252f716e92..633c0d523a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -13,9 +13,9 @@ export interface UmbValidationMessage { export class UmbValidationMessagesManager { #messages = new UmbArrayState([], (x) => x.key); - /*constructor() { - this.#messages.asObservable().subscribe((x) => console.log('messages:', x)); - }*/ + constructor() { + this.#messages.asObservable().subscribe((x) => console.log('all messages:', x)); + } /* serializeMessages(fromPath: string, toPath: string): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/form-control-validator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/form-control-validator.controller.ts index 244025e09d..743b94bc41 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/form-control-validator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/form-control-validator.controller.ts @@ -6,6 +6,10 @@ import { UmbValidationValidEvent } from '../events/validation-valid.event.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +/** + * Bind a Form Controls validation state to the validation context. + * This validator will validate the form control and add messages to the validation context if the form control is invalid. + */ export class UmbFormControlValidator extends UmbControllerBase implements UmbValidator { // The path to the data that this validator is validating. readonly #dataPath?: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-translator.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-translator.interface.ts index 80b66fa608..9d7509e327 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-translator.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-translator.interface.ts @@ -1,3 +1,8 @@ export interface UmbValidationMessageTranslator { - translate(message: string): undefined | string; + /** + * + * @param path - The path to translate + * @returns {false | undefined | string} - Returns false if the path is not handled by this translator, undefined if the path is invalid, or the translated path as a string. + */ + translate(path: string): false | undefined | string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts index 71487cf29d..531806006c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts @@ -19,17 +19,20 @@ export class UmbVariantValuesValidationMessageTranslator translate(path: string) { if (path.indexOf('$.values[') !== 0) { - return; + // We do not handle this path. + return false; } const pathEnd = path.indexOf(']'); if (pathEnd === -1) { - return; + // We do not handle this path. + return false; } // retrieve the number from the message values index: [NL] const index = parseInt(path.substring(9, pathEnd)); if (isNaN(index)) { - return; + // index is not a number, this means its not a path we want to translate. [NL] + return false; } // Get the data from the validation request, the context holds that for us: [NL] From 3898266929d103520d32bd136643da2169171140 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 7 Aug 2024 16:34:25 +0100 Subject: [PATCH 025/152] Correct property-editor UI groups to align with v13. --- .../src/packages/code-editor/property-editor/manifests.ts | 4 ++-- .../documents/property-editors/document-picker/manifests.ts | 2 +- .../property-editors/markdown-editor/manifests.ts | 2 +- .../media/media/property-editors/image-cropper/manifests.ts | 2 +- .../media/media/property-editors/media-picker/manifests.ts | 2 +- .../media/media/property-editors/upload-field/manifests.ts | 2 +- .../src/packages/property-editors/dropdown/manifests.ts | 2 +- .../src/packages/property-editors/label/manifests.ts | 2 +- .../packages/tiny-mce/property-editors/tiny-mce/manifests.ts | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts index 5e6142daa1..e31e338113 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts @@ -8,8 +8,8 @@ export const manifest: ManifestPropertyEditorUi = { meta: { label: 'Code Editor', propertyEditorSchemaAlias: 'Umbraco.Plain.String', - icon: 'icon-code', - group: 'common', + icon: 'icon-brackets', + group: 'richContent', settings: { properties: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts index 0bcb58633f..cfff881f56 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts @@ -11,7 +11,7 @@ export const manifests: Array = [ label: 'Document Picker', propertyEditorSchemaAlias: 'Umbraco.ContentPicker', icon: 'icon-document', - group: 'common', + group: 'pickers', settings: { properties: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts index 3e7ac5cd37..ee9dc738f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts @@ -10,7 +10,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Markdown Editor', propertyEditorSchemaAlias: 'Umbraco.MarkdownEditor', icon: 'icon-code', - group: 'pickers', + group: 'richContent', settings: { properties: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts index 2a2b5f688e..dfd31736c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts @@ -9,7 +9,7 @@ const manifest: ManifestPropertyEditorUi = { meta: { label: 'Image Cropper', icon: 'icon-crop', - group: 'pickers', + group: 'media', propertyEditorSchemaAlias: 'Umbraco.ImageCropper', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts index 8db76a1bcc..669c8977f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts @@ -10,7 +10,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Media Picker', propertyEditorSchemaAlias: 'Umbraco.MediaPicker3', icon: 'icon-picture', - group: 'pickers', + group: 'media', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts index f7da784d05..311f8d61b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts @@ -10,7 +10,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Upload Field', propertyEditorSchemaAlias: 'Umbraco.UploadField', icon: 'icon-download-alt', - group: 'common', + group: 'media', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts index 6635738b2d..e0be2a8a08 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts @@ -11,7 +11,7 @@ export const manifests: Array = [ label: 'Dropdown', propertyEditorSchemaAlias: 'Umbraco.DropDown.Flexible', icon: 'icon-list', - group: 'pickers', + group: 'lists', }, }, schemaManifest, diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts index 34341e6edb..942843cf4a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts @@ -10,7 +10,7 @@ export const manifests: Array = [ meta: { label: 'Label', icon: 'icon-readonly', - group: 'pickers', + group: 'common', propertyEditorSchemaAlias: 'Umbraco.Label', }, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts index aaa3f6880e..c4c40d5be4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts @@ -13,7 +13,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Rich Text Editor', propertyEditorSchemaAlias: UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS, icon: 'icon-browser-window', - group: 'richText', + group: 'richContent', settings: { properties: [ { From a2158a7ca390221ba58406e4e3c338c8d7108cbe Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 7 Aug 2024 16:40:17 +0100 Subject: [PATCH 026/152] Removed "Submit" button from property-editor UI picker modal as multiple selection wasn't intended, and for consistency with the content-type/data-type configuration picker modal. Removed the `submitLabel` from `UmbPropertyEditorUIPickerModalData` interface, as was unused across the core codebase. --- .../property-editor-ui-picker-modal.token.ts | 4 +--- ...property-editor-ui-picker-modal.element.ts | 20 +++---------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts index 7a96e4c8ce..bf682ced50 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts @@ -1,8 +1,6 @@ import { UmbModalToken } from './modal-token.js'; -export interface UmbPropertyEditorUIPickerModalData { - submitLabel?: string; -} +export interface UmbPropertyEditorUIPickerModalData {} export type UmbPropertyEditorUIPickerModalValue = { selection: Array; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts index a8ea26c931..562edc2e85 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts @@ -24,15 +24,9 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< @state() private _propertyEditorUIs: Array = []; - @state() - private _submitLabel = 'Select'; - override connectedCallback(): void { super.connectedCallback(); - // TODO: We never parse on a submit label, so this seem weird as we don't enable this of other places. - //this._submitLabel = this.data?.submitLabel ?? this._submitLabel; - this.#usePropertyEditorUIs(); } @@ -54,11 +48,8 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< } #handleClick(propertyEditorUi: ManifestPropertyEditorUi) { - this.#select(propertyEditorUi.alias); - } - - #select(alias: string) { - this.value = { selection: [alias] }; + this.value = { selection: [propertyEditorUi.alias] }; + this._submitModal(); } #handleFilterInput(event: UUIInputEvent) { @@ -87,12 +78,7 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< ${this._renderFilter()} ${this._renderGrid()}
- - +
`; From 7a83c8e85f4750e5cb271a529dbce9b9aa5f2e96 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 7 Aug 2024 16:42:08 +0100 Subject: [PATCH 027/152] Refactored property-editor UI picker modal Refactored the grouping code, to sort items alphabetically and `fromCamelCase` the group labels. --- ...property-editor-ui-picker-modal.element.ts | 131 +++++++++--------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts index 562edc2e85..047800054c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts @@ -1,25 +1,22 @@ -import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; +import { css, customElement, html, repeat, state } from '@umbraco-cms/backoffice/external/lit'; +import { fromCamelCase } from '@umbraco-cms/backoffice/utils'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalValue, } from '@umbraco-cms/backoffice/modal'; -import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; +import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -interface GroupedPropertyEditorUIs { - [key: string]: Array; -} @customElement('umb-property-editor-ui-picker-modal') export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalValue > { @state() - private _groupedPropertyEditorUIs: GroupedPropertyEditorUIs = {}; + private _groupedPropertyEditorUIs: Array<{ key: string; items: Array }> = []; @state() private _propertyEditorUIs: Array = []; @@ -33,17 +30,11 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< #usePropertyEditorUIs() { this.observe(umbExtensionsRegistry.byType('propertyEditorUi'), (propertyEditorUIs) => { // Only include Property Editor UIs which has Property Editor Schema Alias - this._propertyEditorUIs = propertyEditorUIs.filter( - (propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias, - ); + this._propertyEditorUIs = propertyEditorUIs + .filter((propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias) + .sort((a, b) => a.meta.label.localeCompare(b.meta.label)); - // TODO: groupBy is not known by TS yet - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - this._groupedPropertyEditorUIs = Object.groupBy( - this._propertyEditorUIs, - (propertyEditorUi: ManifestPropertyEditorUi) => propertyEditorUi.meta.group, - ); + this.#groupPropertyEditorUIs(this._propertyEditorUIs); }); } @@ -53,30 +44,35 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< } #handleFilterInput(event: UUIInputEvent) { - let query = (event.target.value as string) || ''; - query = query.toLowerCase(); + const query = ((event.target.value as string) || '').toLowerCase(); const result = !query ? this._propertyEditorUIs - : this._propertyEditorUIs.filter((propertyEditorUI) => { - return ( - propertyEditorUI.name.toLowerCase().includes(query) || propertyEditorUI.alias.toLowerCase().includes(query) - ); - }); + : this._propertyEditorUIs.filter( + (propertyEditorUI) => + propertyEditorUI.name.toLowerCase().includes(query) || propertyEditorUI.alias.toLowerCase().includes(query), + ); + this.#groupPropertyEditorUIs(result); + } + + #groupPropertyEditorUIs(items: Array) { // TODO: groupBy is not known by TS yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - this._groupedPropertyEditorUIs = Object.groupBy( - result, - (propertyEditorUI: ManifestPropertyEditorUi) => propertyEditorUI.meta.group, + const grouped = Object.groupBy(items, (propertyEditorUi: ManifestPropertyEditorUi) => + fromCamelCase(propertyEditorUi.meta.group), ); + + this._groupedPropertyEditorUIs = Object.keys(grouped) + .sort() + .map((key) => ({ key, items: grouped[key] })); } override render() { return html` - ${this._renderFilter()} ${this._renderGrid()} + ${this.#renderFilter()} ${this.#renderGrid()}
@@ -84,44 +80,53 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< `; } - private _renderFilter() { - return html` - - `; + #renderFilter() { + return html` + + + + `; } - private _renderGrid() { - return html` ${Object.entries(this._groupedPropertyEditorUIs).map( - ([key, value]) => - html`

${key}

- ${this._renderGroupItems(value)}`, - )}`; - } - - private _renderGroupItems(groupItems: Array) { - return html`
    + #renderGrid() { + return html` ${repeat( - groupItems, - (propertyEditorUI) => propertyEditorUI.alias, - (propertyEditorUI) => - html`
  • - -
  • `, + this._groupedPropertyEditorUIs, + (group) => group.key, + (group) => html` +

    ${group.key}

    + ${this.#renderGroupItems(group.items)} + `, )} -
`; + `; + } + + #renderGroupItems(groupItems: Array) { + return html` +
    + ${repeat( + groupItems, + (propertyEditorUI) => propertyEditorUI.alias, + (propertyEditorUI) => html` +
  • + +
  • + `, + )} +
+ `; } static override styles = [ - UmbTextStyles, css` #filter { width: 100%; From 4b5ba56cef0d17c81292bf7a5f6057fba0a0a1a9 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 7 Aug 2024 16:44:02 +0100 Subject: [PATCH 028/152] Removed `submitLabel` from data-type flow input as it wasn't being used, (there was no Submit button) --- .../data-type-flow-input/data-type-flow-input.element.ts | 4 +--- .../data-type-picker-flow-modal.element.ts | 4 ---- .../data-type-picker-flow-modal.token.ts | 4 +--- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index 62700d299f..0b433b76ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -52,9 +52,7 @@ export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL) .onSetup(() => { return { - data: { - submitLabel: 'Submit', - }, + data: {}, value: { selection: this._ids ?? [] }, }; }) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index a898f756b9..99ec2f2155 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -32,7 +32,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< public override set data(value: UmbDataTypePickerFlowModalData) { super.data = value; - this._submitLabel = this.data?.submitLabel ?? this._submitLabel; } @state() @@ -41,9 +40,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< @state() private _groupedPropertyEditorUIs: GroupedItems = {}; - @state() - private _submitLabel = 'Select'; - @state() private _currentPage = 1; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts index d6acec2fda..ebad5be051 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts @@ -1,8 +1,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export interface UmbDataTypePickerFlowModalData { - submitLabel?: string; -} +export interface UmbDataTypePickerFlowModalData {} export type UmbDataTypePickerFlowModalValue = { selection: Array; From cd41c72ee5c863bc804d8851e59e01cce83cacb7 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 7 Aug 2024 16:47:22 +0100 Subject: [PATCH 029/152] Refactored data-type picker flow modal Refactored the grouping code, to sort items alphabetically and `fromCamelCase` the group labels. Added grouping when filtering data-types. Added missing localization keys. --- ...ker-flow-data-type-picker-modal.element.ts | 77 +++-- .../data-type-picker-flow-modal.element.ts | 285 ++++++++++-------- 2 files changed, 188 insertions(+), 174 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts index 09b740d180..aaf4b0dfdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts @@ -3,9 +3,9 @@ import type { UmbDataTypePickerFlowDataTypePickerModalData, UmbDataTypePickerFlowDataTypePickerModalValue, } from './data-type-picker-flow-data-type-picker-modal.token.js'; -import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { css, customElement, html, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type'; @customElement('umb-data-type-picker-flow-data-type-picker-modal') @@ -25,10 +25,10 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas this._propertyEditorUiAlias = this.data.propertyEditorUiAlias; - this._observeDataTypesOf(this._propertyEditorUiAlias); + this.#observeDataTypesOf(this._propertyEditorUiAlias); } - private async _observeDataTypesOf(propertyEditorUiAlias: string) { + async #observeDataTypesOf(propertyEditorUiAlias: string) { if (!this.data) return; const dataTypeCollectionRepository = new UmbDataTypeCollectionRepository(this); @@ -40,64 +40,65 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas }); this.observe(collection.asObservable(), (dataTypes) => { - this._dataTypes = dataTypes; + this._dataTypes = dataTypes.sort((a, b) => a.name.localeCompare(b.name)); }); } - private _handleClick(dataType: UmbDataTypeItemModel) { + #handleClick(dataType: UmbDataTypeItemModel) { if (dataType.unique) { this.value = { dataTypeId: dataType.unique }; this.modalContext?.submit(); } } - private _handleCreate() { + #handleCreate() { this.value = { createNewWithPropertyEditorUiAlias: this._propertyEditorUiAlias }; this.modalContext?.submit(); } - private _close() { + #close() { this.modalContext?.reject(); } override render() { return html` - - ${this._renderDataTypes()} ${this._renderCreate()} + + ${this.#renderDataTypes()} ${this.#renderCreate()}
- +
`; } - private _renderDataTypes() { - return this._dataTypes && this._dataTypes.length > 0 - ? html`
    - ${repeat( - this._dataTypes!, - (dataType) => dataType.unique, - (dataType) => - dataType.unique - ? html`
  • - -
    - - ${dataType.name} -
    -
    -
  • ` - : '', - )} -
` - : ''; - } - private _renderCreate() { + #renderDataTypes() { + if (!this._dataTypes?.length) return; return html` - +
    + ${repeat( + this._dataTypes, + (dataType) => dataType.unique, + (dataType) => html` +
  • + this.#handleClick(dataType)}> +
    + + ${dataType.name} +
    +
    +
  • + `, + )} +
+ `; + } + + #renderCreate() { + return html` +
- Create new + Create new
`; @@ -174,12 +175,6 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas margin: auto; } - #category-name { - text-align: center; - display: block; - text-transform: capitalize; - font-size: 1.2rem; - } #create-button { max-width: 100px; --uui-button-padding-left-factor: 0; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index 99ec2f2155..94255693c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -6,23 +6,20 @@ import type { UmbDataTypePickerFlowModalData, UmbDataTypePickerFlowModalValue, } from './data-type-picker-flow-modal.token.js'; -import { css, html, repeat, customElement, state, when, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, nothing, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; +import { UmbPaginationManager, debounce, fromCamelCase } from '@umbraco-cms/backoffice/utils'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; +import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/property-type'; import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type'; import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; -import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/property-type'; -import { UmbPaginationManager, debounce } from '@umbraco-cms/backoffice/utils'; -interface GroupedItems { - [key: string]: Array; -} @customElement('umb-data-type-picker-flow-modal') export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< UmbDataTypePickerFlowModalData, @@ -35,10 +32,10 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< } @state() - private _groupedDataTypes?: GroupedItems; + private _groupedDataTypes?: Array<{ key: string; items: Array }> = []; @state() - private _groupedPropertyEditorUIs: GroupedItems = {}; + private _groupedPropertyEditorUIs: Array<{ key: string; items: Array }> = []; @state() private _currentPage = 1; @@ -48,13 +45,18 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< pagination = new UmbPaginationManager(); - private _createDataTypeModal!: UmbModalRouteRegistrationController; - #collectionRepository; - #dataTypes: Array = []; - #propertyEditorUIs: Array = []; + + #createDataTypeModal!: UmbModalRouteRegistrationController; + #currentFilterQuery = ''; + #dataTypes: Array = []; + + #groupLookup: Record = {}; + + #propertyEditorUIs: Array = []; + constructor() { super(); @@ -62,10 +64,10 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.#init(); } - private _createDataType(propertyEditorUiAlias: string) { + #createDataType(propertyEditorUiAlias: string) { // TODO: Could be nice with a more pretty way to prepend to the URL: // Open create modal: - this._createDataTypeModal.open( + this.#createDataTypeModal.open( { uiAlias: propertyEditorUiAlias }, `create/parent/${UMB_DATA_TYPE_ENTITY_TYPE}/null`, ); @@ -78,10 +80,13 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.#initPromise = Promise.all([ this.observe(umbExtensionsRegistry.byType('propertyEditorUi'), (propertyEditorUIs) => { // Only include Property Editor UIs which has Property Editor Schema Alias - this.#propertyEditorUIs = propertyEditorUIs.filter( - (propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias, - ); - this._performFiltering(); + this.#propertyEditorUIs = propertyEditorUIs + .filter((propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias) + .sort((a, b) => a.meta.label.localeCompare(b.meta.label)); + + this.#groupLookup = Object.fromEntries(propertyEditorUIs.map((ui) => [ui.alias, ui.meta.group])); + + this.#performFiltering(); }).asPromise(), ]); @@ -97,10 +102,10 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< }) .onSubmit((submitData) => { if (submitData?.dataTypeId) { - this._select(submitData.dataTypeId); + this.#select(submitData.dataTypeId); this._submitModal(); } else if (submitData?.createNewWithPropertyEditorUiAlias) { - this._createDataType(submitData.createNewWithPropertyEditorUiAlias); + this.#createDataType(submitData.createNewWithPropertyEditorUiAlias); } }) .observeRouteBuilder((routeBuilder) => { @@ -108,7 +113,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.requestUpdate('_dataTypePickerModalRouteBuilder'); }); - this._createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL) + this.#createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL) .addAdditionalPath(':uiAlias') .onSetup(async (params) => { const contentContextConsumer = this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, () => { @@ -133,7 +138,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< }; }) .onSubmit((value) => { - this._select(value?.unique); + this.#select(value?.unique); this._submitModal(); }); } @@ -156,14 +161,14 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< } } - private _handleDataTypeClick(dataType: UmbDataTypeItemModel) { + #handleDataTypeClick(dataType: UmbDataTypeItemModel) { if (dataType.unique) { - this._select(dataType.unique); + this.#select(dataType.unique); this._submitModal(); } } - private _select(unique: string | undefined) { + #select(unique: string | undefined) { this.value = { selection: unique ? [unique] : [] }; } @@ -184,98 +189,102 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< async #handleFiltering() { await this.#getDataTypes(); - this._performFiltering(); + this.#performFiltering(); } - private _performFiltering() { + #performFiltering() { if (this.#currentFilterQuery) { - const filteredDataTypes = this.#dataTypes.filter((dataType) => - dataType.name?.toLowerCase().includes(this.#currentFilterQuery), + const filteredDataTypes = this.#dataTypes + .filter((dataType) => dataType.name?.toLowerCase().includes(this.#currentFilterQuery)) + .sort((a, b) => a.name.localeCompare(b.name)); + + // TODO: groupBy is not known by TS yet + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const grouped = Object.groupBy(filteredDataTypes, (dataType: UmbDataTypeItemModel) => + fromCamelCase(this.#groupLookup[dataType.propertyEditorUiAlias] ?? 'Uncategorized'), ); - /* TODO: data type items doesn't have a group property. We will need a reference to the Property Editor UI to get the group. - this is a temp solution to group them as uncategorized. The same result as with the lodash groupBy. - */ - this._groupedDataTypes = { - undefined: filteredDataTypes, - }; + this._groupedDataTypes = Object.keys(grouped) + .sort() + .map((key) => ({ key, items: grouped[key] })); } else { - this._groupedDataTypes = undefined; + this._groupedDataTypes = []; } const filteredUIs = !this.#currentFilterQuery ? this.#propertyEditorUIs - : this.#propertyEditorUIs.filter((propertyEditorUI) => { - return ( + : this.#propertyEditorUIs.filter( + (propertyEditorUI) => propertyEditorUI.name.toLowerCase().includes(this.#currentFilterQuery) || - propertyEditorUI.alias.toLowerCase().includes(this.#currentFilterQuery) - ); - }); + propertyEditorUI.alias.toLowerCase().includes(this.#currentFilterQuery), + ); // TODO: groupBy is not known by TS yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - this._groupedPropertyEditorUIs = Object.groupBy( - filteredUIs, - (propertyEditorUI: ManifestPropertyEditorUi) => propertyEditorUI.meta.group, + const grouped = Object.groupBy(filteredUIs, (propertyEditorUi: ManifestPropertyEditorUi) => + fromCamelCase(propertyEditorUi.meta.group ?? 'Uncategorized'), ); + + this._groupedPropertyEditorUIs = Object.keys(grouped) + .sort() + .map((key) => ({ key, items: grouped[key] })); } override render() { return html` - - ${this._renderFilter()} ${this._renderGrid()} + + ${this.#renderFilter()} ${this.#renderGrid()}
- +
`; } - private _renderGrid() { - return this.#currentFilterQuery ? this._renderFilteredList() : this._renderUIs(); + #renderGrid() { + return this.#currentFilterQuery ? this.#renderFilteredList() : this.#renderUIs(); } - private _renderFilter() { + #renderFilter() { return html` `; } - private _renderFilteredList() { + #renderFilteredList() { if (!this._groupedDataTypes) return nothing; - const dataTypesEntries = Object.entries(this._groupedDataTypes); - if (!this._groupedPropertyEditorUIs) return nothing; - const editorUIEntries = Object.entries(this._groupedPropertyEditorUIs); - - if (dataTypesEntries.length === 0 && editorUIEntries.length === 0) { - return html`Nothing matches your search, try another search term.`; + if (this._groupedDataTypes.length === 0 && this._groupedPropertyEditorUIs.length === 0) { + return html`

Nothing matches your search, try another search term.

`; } return html` ${when( - dataTypesEntries.length > 0, - () => - html`
- Available configurations -
- ${this._renderDataTypes()}${this.#renderLoadMore()}`, + this._groupedDataTypes.length > 0, + () => html` +
+ Available configurations +
+ ${this.#renderDataTypes()} ${this.#renderLoadMore()} + `, )} ${when( - editorUIEntries.length > 0, - () => - html`
- Create a new configuration -
- ${this._renderUIs(true)}`, + this._groupedPropertyEditorUIs.length > 0, + () => html` +
+ Create a new configuration +
+ ${this.#renderUIs(true)} + `, )} `; } @@ -285,83 +294,93 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< return html``; } - private _renderDataTypes() { + #renderDataTypes() { if (!this._groupedDataTypes) return nothing; - const entries = Object.entries(this._groupedDataTypes); - // TODO: Fix so we can have Data Types grouped. (or choose not to group them) - return entries.map( - ([key, value]) => - html`
${key === 'undefined' ? 'Uncategorized' : key}
- ${this._renderGroupDataTypes(value)}`, + return this._groupedDataTypes.map( + (group) => html` +
${group.key}
+ ${this.#renderGroupDataTypes(group.items)} + `, ); } - private _renderUIs(createAsNewOnPick?: boolean) { + #renderUIs(createAsNewOnPick?: boolean) { if (!this._groupedPropertyEditorUIs) return nothing; - const entries = Object.entries(this._groupedPropertyEditorUIs); - - return entries.map( - ([key, value]) => - html`
${key === 'undefined' ? 'Uncategorized' : key}
- ${this._renderGroupUIs(value, createAsNewOnPick)}`, + return this._groupedPropertyEditorUIs.map( + (group) => html` +
${group.key}
+ ${this.#renderGroupUIs(group.items, createAsNewOnPick)} + `, ); } - private _renderGroupUIs(uis: Array, createAsNewOnPick?: boolean) { - return html`
    - ${this._dataTypePickerModalRouteBuilder - ? repeat( - uis, - (propertyEditorUI) => propertyEditorUI.alias, - (propertyEditorUI) => { - return html`
  • ${this._renderDataTypeButton(propertyEditorUI, createAsNewOnPick)}
  • `; - }, - ) - : ''} -
`; + #renderGroupUIs(uis: Array, createAsNewOnPick?: boolean) { + return html` +
    + ${this._dataTypePickerModalRouteBuilder + ? repeat( + uis, + (propertyEditorUI) => propertyEditorUI.alias, + (propertyEditorUI) => { + return html`
  • ${this.#renderDataTypeButton(propertyEditorUI, createAsNewOnPick)}
  • `; + }, + ) + : ''} +
+ `; } - private _renderDataTypeButton(propertyEditorUI: ManifestPropertyEditorUi, createAsNewOnPick?: boolean) { + #renderDataTypeButton(propertyEditorUI: ManifestPropertyEditorUi, createAsNewOnPick?: boolean) { if (createAsNewOnPick) { - return html` this._createDataType(propertyEditorUI.alias)}> - ${this._renderItemContent(propertyEditorUI)} - `; + return html` + this.#createDataType(propertyEditorUI.alias)}> + ${this.#renderItemContent(propertyEditorUI)} + + `; } else { - return html` - ${this._renderItemContent(propertyEditorUI)} - `; + return html` + + ${this.#renderItemContent(propertyEditorUI)} + + `; } } - private _renderItemContent(propertyEditorUI: ManifestPropertyEditorUi) { - return html`
- - ${propertyEditorUI.meta.label || propertyEditorUI.name} -
`; + + #renderItemContent(propertyEditorUI: ManifestPropertyEditorUi) { + return html` +
+ + ${propertyEditorUI.meta.label || propertyEditorUI.name} +
+ `; } - private _renderGroupDataTypes(dataTypes: Array) { - return html`
    - ${repeat( - dataTypes, - (dataType) => dataType.unique, - (dataType) => - html`
  • - -
    - - ${dataType.name} -
    -
    -
  • `, - )} -
`; + #renderGroupDataTypes(dataTypes: Array) { + return html` +
    + ${repeat( + dataTypes, + (dataType) => dataType.unique, + (dataType) => html` +
  • + this.#handleDataTypeClick(dataType)}> +
    + + ${dataType.name} +
    +
    +
  • + `, + )} +
+ `; } static override styles = [ @@ -426,6 +445,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< height: 100%; width: 100%; } + #item-grid .item .icon { font-size: 2em; margin: auto; @@ -436,7 +456,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< } .choice-type-headline { - text-transform: capitalize; border-bottom: 1px solid var(--uui-color-divider); } `, From 24429d912a0cd09daeee9392540b9be11d4aa3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 7 Aug 2024 19:43:14 +0200 Subject: [PATCH 030/152] move server modal validator --- .../src/packages/core/validation/context/index.ts | 2 -- .../bind-server-validation-to-form-control.controller.ts | 2 -- .../src/packages/core/validation/controllers/index.ts | 4 +++- .../server-model-validation.context-token.ts | 2 +- .../server-model-validation.context.ts | 4 +--- ...variant-values-validation-message-translator.controller.ts | 2 +- 6 files changed, 6 insertions(+), 10 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/validation/{context => controllers}/server-model-validation.context-token.ts (69%) rename src/Umbraco.Web.UI.Client/src/packages/core/validation/{context => controllers}/server-model-validation.context.ts (97%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts index 96fbced51a..99311cb3cf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts @@ -1,4 +1,2 @@ export * from './validation.context.js'; export * from './validation.context-token.js'; -export * from './server-model-validation.context.js'; -export * from './server-model-validation.context-token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts index f0abe49189..5d1b81293e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts @@ -30,7 +30,6 @@ export class UmbBindServerValidationToFormControl extends UmbControllerBase { // If not valid lets see if we should remove server validation [NL] if (!jsonStringComparison(this.#value, value)) { this.#value = value; - console.log('remove messages'); // Only remove server validations from validation context [NL] this.#messages.forEach((message) => { if (message.type === 'server') { @@ -51,7 +50,6 @@ export class UmbBindServerValidationToFormControl extends UmbControllerBase { context.messages.messagesOfTypeAndPath('server', dataPath), (messages) => { this.#messages = messages; - console.log('messages', messages); this.#isValid = messages.length === 0; if (!this.#isValid) { this.#setup(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts index 35003793f3..e28cfdeb9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts @@ -1,3 +1,5 @@ export * from './bind-server-validation-to-form-control.controller.js'; -export * from './observe-validation-state.controller.js'; export * from './form-control-validator.controller.js'; +export * from './observe-validation-state.controller.js'; +export * from './server-model-validation.context-token.js'; +export * from './server-model-validation.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts similarity index 69% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context-token.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts index 1f8a1932dc..a37d4b48c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts @@ -1,4 +1,4 @@ -import type { UmbServerModelValidationContext } from './index.js'; +import type { UmbServerModelValidationContext } from './server-model-validation.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export const UMB_SERVER_MODEL_VALIDATION_CONTEXT = new UmbContextToken( diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts similarity index 97% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index 9fd0db8735..b23fa9385e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -1,7 +1,7 @@ import type { UmbValidationMessageTranslator } from '../translators/validation-message-translator.interface.js'; import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; -import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; +import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from './server-model-validation.context-token.js'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -76,8 +76,6 @@ export class UmbServerModelValidationContext }); } // Check if there are missing properties: - console.log('send data:', this.#data); - console.log('error response:', errorBody); if (errorBody?.missingProperties) { // Retrieve the variants of he send data, as those are the once we will declare as missing properties: errorBody.missingProperties.forEach((alias) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts index 531806006c..72bd15df9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts @@ -1,4 +1,4 @@ -import type { UmbServerModelValidationContext } from '../context/server-model-validation.context.js'; +import type { UmbServerModelValidationContext } from '../controllers/server-model-validation.context.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import type { UmbValidationMessageTranslator } from './validation-message-translator.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; From fc1f722e8e45ed1c9dc05794280ed6c8972b4cb7 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 6 Aug 2024 18:28:13 +0100 Subject: [PATCH 031/152] Manifest viewer modal: replaces stringify function --- .../manifest-viewer-modal.element.ts | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/modals/manifest-viewer/manifest-viewer-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/modals/manifest-viewer/manifest-viewer-modal.element.ts index 2e5e8e476e..931b876ce3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/modals/manifest-viewer/manifest-viewer-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/modals/manifest-viewer/manifest-viewer-modal.element.ts @@ -1,29 +1,37 @@ import type { UmbManifestViewerModalData, UmbManifestViewerModalValue } from './manifest-viewer-modal.token.js'; -import { css, html, customElement, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -// JSON parser for the manifest viewer modal -// Enabling us to view JS code, but it is not optimal, but currently better than nothing [NL] -// Ideally we should have a JS code stringify that can print the manifest as JS. [NL] -function JsonParser(key: string, value: any) { - if (typeof value === 'function' && value !== null && value.toString) { - return Function.prototype.toString.call(value); - } - return value; -} - @customElement('umb-manifest-viewer-modal') export class UmbManifestViewerModalElement extends UmbModalBaseElement< UmbManifestViewerModalData, UmbManifestViewerModalValue > { + // Code adapted from https://stackoverflow.com/a/57668208/12787 + // Licensed under the permissions of the CC BY-SA 4.0 DEED + #stringify(obj: any): string { + let output = '{'; + for (const key in obj) { + let value = obj[key]; + if (typeof value === 'function') { + value = value.toString(); + } else if (value instanceof Array) { + value = JSON.stringify(value); + } else if (typeof value === 'object') { + value = this.#stringify(value); + } else { + value = `"${value}"`; + } + output += `\n ${key}: ${value},`; + } + return output + '\n}'; + } + override render() { return html` - + ${this.data - ? html`${JSON.stringify(this.data, JsonParser, 2)}` + ? html`${this.#stringify(this.data)}` : nothing}
@@ -32,7 +40,14 @@ export class UmbManifestViewerModalElement extends UmbModalBaseElement< `; } - static override styles = [css``]; + static override styles = [ + css` + umb-code-block { + border: none; + height: 100%; + } + `, + ]; } export default UmbManifestViewerModalElement; From 86fcef63d2b08ce5dfd2a72f700e468b6e4b1a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 7 Aug 2024 20:02:06 +0200 Subject: [PATCH 032/152] spell mistake --- .../validation/controllers/server-model-validation.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index b23fa9385e..fbf8611cdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -9,7 +9,7 @@ import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; type ServerFeedbackEntry = { path: string; messages: Array }; -/** This should ideally be generated by the server, but we currently don't generate error-modal-types. */ +/** This should ideally be generated by the server, but we currently don't generate error-model-types. */ interface ValidateErrorResponseBodyModel { detail: string; errors: Record>; From 3e3b93c6a2db70eeb4458ed5d14164cebd0a8e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 7 Aug 2024 20:53:36 +0200 Subject: [PATCH 033/152] path translation refactor --- .../context/validation-messages.manager.ts | 46 ++++++++-- .../validation/context/validation.context.ts | 19 ---- .../server-model-validation.context.ts | 90 +++++-------------- 3 files changed, 64 insertions(+), 91 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 633c0d523a..877bb5630d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -1,3 +1,4 @@ +import type { UmbValidationMessageTranslator } from '../translators/validation-message-translator.interface.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; @@ -54,7 +55,7 @@ export class UmbValidationMessagesManager { hasMessagesOfPathAndDescendant(path: string): Observable { return this.#messages.asObservablePart((msgs) => - // Find messages that starts with the given path, if the path is longer then require a dot or [ as the next character. Using a more performant way than Regex: + // Find messages that starts with the given path, if the path is longer then require a dot or [ as the next character. Using a more performant way than Regex: [NL] msgs.some( (x) => x.path.indexOf(path) === 0 && @@ -73,17 +74,15 @@ export class UmbValidationMessagesManager { } addMessage(type: UmbValidationMessageType, path: string, message: string): void { + path = this.#translatePath(path) ?? path; this.#messages.appendOne({ type, key: UmbId.new(), path, message }); } addMessages(type: UmbValidationMessageType, path: string, messages: Array): void { + path = this.#translatePath(path) ?? path; this.#messages.append(messages.map((message) => ({ type, key: UmbId.new(), path, message }))); } - /* - removeMessage(message: UmbValidationDataPath): void { - this.#messages.removeOne(message.key); - }*/ removeMessageByKey(key: string): void { this.#messages.removeOne(key); } @@ -94,11 +93,48 @@ export class UmbValidationMessagesManager { this.#messages.filter((x) => x.type !== type); } + #translatePath(path: string): string | undefined { + for (const translator of this.#translators) { + const newPath = translator.translate(path); + // If not undefined or false, then it was a valid translation: [NL] + if (newPath) { + // Lets try to translate it again, this will recursively translate the path until no more translations are possible (and then fallback to '?? newpath') [NL] + return this.#translatePath(newPath) ?? newPath; + } + } + return; + } + + #translators: Array = []; + addTranslator(translator: UmbValidationMessageTranslator): void { + if (this.#translators.indexOf(translator) === -1) { + this.#translators.push(translator); + } + // execute translators on all messages: + // Notice we are calling getValue() in each iteration to avoid the need to re-translate the same messages over and over again. [NL] + for (const msg of this.#messages.getValue()) { + const newPath = this.#translatePath(msg.path); + // If newPath is not false or undefined, a translation of it has occurred, meaning we ant to update it: [NL] + if (newPath) { + // update the specific message, with its new path: [NL] + this.#messages.updateOne(msg.key, { path: newPath }); + } + } + } + + removeTranslator(translator: UmbValidationMessageTranslator): void { + const index = this.#translators.indexOf(translator); + if (index !== -1) { + this.#translators.splice(index, 1); + } + } + reset(): void { this.#messages.setValue([]); } destroy(): void { + this.#translators = []; this.#messages.destroy(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 69ac384df9..b3ac8e5c4b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -39,25 +39,6 @@ export class UmbValidationContext extends UmbContextBase i } } - /*#onValidatorChange = (e: Event) => { - const target = e.target as unknown as UmbValidator | undefined; - if (!target) { - console.error('Validator did not exist.'); - return; - } - const dataPath = target.dataPath; - if (!dataPath) { - console.error('Validator did not exist or did not provide a data-path.'); - return; - } - - if (target.isValid) { - this.messages.removeMessagesByTypeAndPath('client', dataPath); - } else { - this.messages.addMessages('client', dataPath, target.getMessages()); - } - };*/ - /** * * @returns succeed {Promise} - Returns a promise that resolves to true if the validator succeeded, this depends on the validators and wether forceSucceed is set. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index fbf8611cdf..efc09c1a73 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -7,8 +7,6 @@ import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; -type ServerFeedbackEntry = { path: string; messages: Array }; - /** This should ideally be generated by the server, but we currently don't generate error-model-types. */ interface ValidateErrorResponseBodyModel { detail: string; @@ -24,6 +22,8 @@ export class UmbServerModelValidationContext extends UmbContextBase implements UmbValidator { + #init: Promise; + #validatePromise?: Promise; #validatePromiseResolve?: () => void; @@ -34,11 +34,10 @@ export class UmbServerModelValidationContext getData(): any { return this.#data; } - #translators: Array = []; constructor(host: UmbControllerHost) { super(host, UMB_SERVER_MODEL_VALIDATION_CONTEXT); - this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { + this.#init = this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { if (this.#context) { this.#context.removeValidator(this); } @@ -46,13 +45,12 @@ export class UmbServerModelValidationContext context.addValidator(this); // Run translators? - }); + }).asPromise(); } async askServerForValidation(data: unknown, requestPromise: Promise>): Promise { this.#context?.messages.removeMessagesByType('server'); - const serverFeedback: Array = []; this.#isValid = false; //this.#validatePromiseReject?.(); this.#validatePromise = new Promise((resolve) => { @@ -67,12 +65,17 @@ export class UmbServerModelValidationContext this.#isValid = error ? false : true; if (!this.#isValid) { + if (!this.#context) { + throw new Error('No context available for translation.'); + } + // We are missing some typing here, but we will just go wild with 'as any': [NL] const errorBody = (error as any).body as ValidateErrorResponseBodyModel; // Check if there are validation errors, since the error might be a generic ApiError if (errorBody?.errors) { Object.keys(errorBody.errors).forEach((path) => { - serverFeedback.push({ path, messages: errorBody.errors[path] }); + //serverFeedback.push({ path, messages: errorBody.errors[path] }); + this.#context!.messages.addMessages('server', path, errorBody.errors[path]); }); } // Check if there are missing properties: @@ -80,14 +83,13 @@ export class UmbServerModelValidationContext // Retrieve the variants of he send data, as those are the once we will declare as missing properties: errorBody.missingProperties.forEach((alias) => { this.#data.variants.forEach((variant: any) => { - serverFeedback.push({ - path: `$.values[${UmbDataPathPropertyValueFilter({ - alias: alias, - culture: variant.culture, - segment: variant.segment, - })}].value`, - messages: ['#validation_invalidEmpty'], - }); + const path = `$.values[${UmbDataPathPropertyValueFilter({ + alias: alias, + culture: variant.culture, + segment: variant.segment, + })}].value`; + // TODO: Make a const that holds this translation-key: + this.#context!.messages.addMessages('server', path, ['#validation_invalidEmpty']); }); }); } @@ -95,61 +97,16 @@ export class UmbServerModelValidationContext this.#validatePromiseResolve?.(); this.#validatePromiseResolve = undefined; - - // Translate feedback: - serverFeedback.forEach(this.#executeTranslatorsOnFeedback); } - #executeTranslatorsOnFeedback = (feedback: ServerFeedbackEntry) => { - if (!this.#context) { - throw new Error('No context available for translation.'); - } - const foundTranslator = this.#translators.find((translator) => { - const newPath = translator.translate(feedback.path); - if (newPath !== false) { - // TODO: I might need to restructure this part for adjusting existing feedback with a part-translation. - // Detect if some part is unhandled? - // If so only make a partial translation on the feedback, add a message for the handled part. - // then return [ of the partial translated feedback, and the partial handled part. ]; - - // TODO:Check if there was any temporary messages base on this path, like if it was partial-translated at one point.. - if (newPath !== undefined) { - this.#context!.messages.addMessages('server', newPath, feedback.messages); - return true; - } else { - // If the newPath is undefined, then there was a translation error and we should not keep the message. - throw new Error( - 'Validation Translator returned undefined, which means it failed to translate the path. Translator: ' + - translator.constructor.name + - ', Path: ' + - feedback.path, - ); - } - } - return false; - }); - if (foundTranslator) { - // Because this was successfully translated, we can remove the feedback from the server: - return []; - } else { - // Because this was not translated, we will carry over the message as is: - this.#context.messages.addMessages('server', feedback.path, feedback.messages); - return []; - } - }; - - addTranslator(translator: UmbValidationMessageTranslator): void { - if (this.#translators.indexOf(translator) === -1) { - this.#translators.push(translator); - } - // execute translators here? + async addTranslator(translator: UmbValidationMessageTranslator) { + await this.#init; + this.#context!.messages.addTranslator(translator); } - removeTranslator(translator: UmbValidationMessageTranslator): void { - const index = this.#translators.indexOf(translator); - if (index !== -1) { - this.#translators.splice(index, 1); - } + async removeTranslator(translator: UmbValidationMessageTranslator) { + await this.#init; + this.#context!.messages.removeTranslator(translator); } get isValid(): boolean { @@ -182,7 +139,6 @@ export class UmbServerModelValidationContext override destroy(): void { // TODO: make sure we destroy things properly: - this.#translators = []; super.destroy(); } } From 9d0d8b3d13349129f6163b85968b9529f55b543d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 09:57:23 +0200 Subject: [PATCH 034/152] add break --- .../src/packages/core/validation/context/validation.context.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index b3ac8e5c4b..797d0ef786 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -27,6 +27,7 @@ export class UmbValidationContext extends UmbContextBase i this.validate(); } } + removeValidator(validator: UmbValidator): void { const index = this.#validators.indexOf(validator); if (index !== -1) { From c89d95094ce89231ae2e2180c7ae371e64ebd899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 10:00:46 +0200 Subject: [PATCH 035/152] temporary variant missing properties fix --- .../validation/controllers/server-model-validation.context.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index efc09c1a73..89df70a0ac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -81,7 +81,9 @@ export class UmbServerModelValidationContext // Check if there are missing properties: if (errorBody?.missingProperties) { // Retrieve the variants of he send data, as those are the once we will declare as missing properties: - errorBody.missingProperties.forEach((alias) => { + // Temporary fix for missing properties, as we currently get one for each variant, but we do not know which variant it is for: [NL] + const uniqueMissingProperties = [...new Set(errorBody.missingProperties)]; + uniqueMissingProperties.forEach((alias) => { this.#data.variants.forEach((variant: any) => { const path = `$.values[${UmbDataPathPropertyValueFilter({ alias: alias, From 62864ba47bfd22246ab35c0363a626612acb0224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 10:58:03 +0200 Subject: [PATCH 036/152] property editor UI mandatory props --- .../property-editor-ui-element.interface.ts | 2 ++ .../property/property/property.context.ts | 2 ++ .../property/property/property.element.ts | 20 ++++++++++++++++--- .../property-editor-ui-text-box.element.ts | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts index 8043f4d9b6..39f40cde5b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts @@ -3,4 +3,6 @@ import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/ export interface UmbPropertyEditorUiElement extends HTMLElement { value?: unknown; config?: UmbPropertyEditorConfigCollection; + mandatory?: boolean; + mandatoryMessage?: string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index 76a0dbaa15..227cff6fc0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -43,6 +43,8 @@ export class UmbPropertyContext extends UmbContextBase(undefined); public readonly validation = this.#validation.asObservable(); + public readonly validationMandatory = this.#validation.asObservablePart((x) => x?.mandatory); + public readonly validationMandatoryMessage = this.#validation.asObservablePart((x) => x?.mandatoryMessage); private _editor = new UmbBasicState(undefined); public readonly editor = this._editor.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts index 52bd4c858b..21ed0c6a03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts @@ -220,9 +220,12 @@ export class UmbPropertyElement extends UmbLitElement { ); this.observe( - this.#propertyContext.validation, - (validation) => { - this._mandatory = validation?.mandatory; + this.#propertyContext.validationMandatory, + (mandatory) => { + this._mandatory = mandatory; + if (this._element) { + this._element.mandatory = mandatory; + } }, null, ); @@ -281,6 +284,8 @@ export class UmbPropertyElement extends UmbLitElement { if (this._element) { this._element.addEventListener('change', this._onPropertyEditorChange as any as EventListener); this._element.addEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener); + // No need to observe mandatory, as we already do so and set it on the _element if present: [NL] + this._element.mandatory = this._mandatory; // No need for a controller alias, as the clean is handled via the observer prop: this.#valueObserver = this.observe( @@ -303,6 +308,15 @@ export class UmbPropertyElement extends UmbLitElement { }, null, ); + this.#configObserver = this.observe( + this.#propertyContext.validationMandatoryMessage, + (mandatoryMessage) => { + if (mandatoryMessage) { + this._element!.mandatoryMessage = mandatoryMessage ?? undefined; + } + }, + null, + ); if ('checkValidity' in this._element) { this.#controlValidator = new UmbFormControlValidator(this, this._element as any, this.#dataPath); diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 6de904429a..a4a8c396a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -69,6 +69,7 @@ export class UmbPropertyEditorUITextBoxElement inputMode=${ifDefined(this._inputMode)} maxlength=${ifDefined(this._maxChars)} @input=${this.#onInput} + ?required=${this.readonly} ?readonly=${this.readonly}>`; } From b538195b58ff61eae593d8790f4fe80ae581393c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 11:00:07 +0200 Subject: [PATCH 037/152] build setup --- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/packages/static-file/package.json | 8 ++++++++ .../src/packages/static-file/vite.config.ts | 12 ++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/static-file/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/static-file/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9d5bc0795f..0b211c8e74 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -144,6 +144,7 @@ "./src/packages/packages", "./src/packages/property-editors", "./src/packages/search", + "./src/packages/static-file", "./src/packages/tags", "./src/packages/templating", "./src/packages/tiny-mce", diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/package.json b/src/Umbraco.Web.UI.Client/src/packages/static-file/package.json new file mode 100644 index 0000000000..9b84eeb204 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/static-file", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/vite.config.ts new file mode 100644 index 0000000000..fde0eb382a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/static-file'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); From bb6112ba7e5d3d03bcbd90186e9a5bc5fe9241cf Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 11:04:31 +0200 Subject: [PATCH 038/152] move constants to constants file --- .../modals/static-file-picker-modal.token.ts | 2 +- .../src/packages/static-file/tree/constants.ts | 4 ++++ .../src/packages/static-file/tree/index.ts | 2 +- .../src/packages/static-file/tree/manifests.ts | 11 ++++++----- 4 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/static-file/tree/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/modals/static-file-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/modals/static-file-picker-modal.token.ts index a1e5c2383d..9dec85120a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/static-file/modals/static-file-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/modals/static-file-picker-modal.token.ts @@ -1,4 +1,4 @@ -import { UMB_STATIC_FILE_TREE_ALIAS } from '../tree/manifests.js'; +import { UMB_STATIC_FILE_TREE_ALIAS } from '../tree/index.js'; import type { UmbStaticFileItemModel } from '../repository/item/types.js'; import { type UmbTreePickerModalValue, diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/constants.ts new file mode 100644 index 0000000000..ed91eeeaa5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/constants.ts @@ -0,0 +1,4 @@ +export const UMB_STATIC_FILE_TREE_REPOSITORY_ALIAS = 'Umb.Repository.StaticFile.Tree'; +export const UMB_STATIC_FILE_TREE_STORE_ALIAS = 'Umb.Store.StaticFile.Tree'; +export const UMB_STATIC_FILE_TREE_ALIAS = 'Umb.Tree.StaticFile'; +export const UMB_STATIC_FILE_TREE_ITEM_ALIAS = 'Umb.TreeItem.StaticFile'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/index.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/index.ts index ef66c13c81..1e974c5fe4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/index.ts @@ -3,4 +3,4 @@ export { UMB_STATIC_FILE_TREE_REPOSITORY_ALIAS, UMB_STATIC_FILE_TREE_STORE_ALIAS, UMB_STATIC_FILE_TREE_ALIAS, -} from './manifests.js'; +} from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts index a9f81fd83b..72e84e09e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts @@ -3,6 +3,12 @@ import { UMB_STATIC_FILE_FOLDER_ENTITY_TYPE, UMB_STATIC_FILE_ROOT_ENTITY_TYPE, } from '../entity.js'; +import { + UMB_STATIC_FILE_TREE_ALIAS, + UMB_STATIC_FILE_TREE_ITEM_ALIAS, + UMB_STATIC_FILE_TREE_REPOSITORY_ALIAS, + UMB_STATIC_FILE_TREE_STORE_ALIAS, +} from './constants.js'; import type { ManifestRepository, ManifestTree, @@ -11,11 +17,6 @@ import type { ManifestTypes, } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_STATIC_FILE_TREE_REPOSITORY_ALIAS = 'Umb.Repository.StaticFile.Tree'; -export const UMB_STATIC_FILE_TREE_STORE_ALIAS = 'Umb.Store.StaticFile.Tree'; -export const UMB_STATIC_FILE_TREE_ALIAS = 'Umb.Tree.StaticFile'; -export const UMB_STATIC_FILE_TREE_ITEM_ALIAS = 'Umb.TreeItem.StaticFile'; - const treeRepository: ManifestRepository = { type: 'repository', alias: UMB_STATIC_FILE_TREE_REPOSITORY_ALIAS, From 346d0211d4e1a5ead3c73ffc64f71433065598bf Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 11:07:31 +0200 Subject: [PATCH 039/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 54c785f561..7fa3688d98 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -25,6 +25,7 @@ "./src/packages/packages", "./src/packages/property-editors", "./src/packages/search", + "./src/packages/static-file", "./src/packages/tags", "./src/packages/templating", "./src/packages/tiny-mce", @@ -7535,6 +7536,10 @@ "resolved": "src/packages/search", "link": true }, + "node_modules/@umbraco-backoffice/static-file": { + "resolved": "src/packages/static-file", + "link": true + }, "node_modules/@umbraco-backoffice/tag": { "resolved": "src/packages/tags", "link": true @@ -22766,13 +22771,16 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, - "src/packages/packages": {}, + "src/packages/packages": { + "name": "@umbraco-backoffice/package" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, "src/packages/search": { "name": "@umbraco-backoffice/search" }, + "src/packages/static-file": {}, "src/packages/tags": { "name": "@umbraco-backoffice/tag" }, From 7260a6fd2efdb0f2e80f2a82d7e7ae0de8517173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 11:16:26 +0200 Subject: [PATCH 040/152] variant message --- .../property/property/property.context.ts | 29 +++++++++++++------ .../packages/core/variant/variant-id.class.ts | 10 +++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index 227cff6fc0..d271ed61c3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -46,22 +46,22 @@ export class UmbPropertyContext extends UmbContextBase x?.mandatory); public readonly validationMandatoryMessage = this.#validation.asObservablePart((x) => x?.mandatoryMessage); - private _editor = new UmbBasicState(undefined); - public readonly editor = this._editor.asObservable(); + #editor = new UmbBasicState(undefined); + public readonly editor = this.#editor.asObservable(); setEditor(editor: UmbPropertyEditorUiElement | undefined) { - this._editor.setValue(editor ?? undefined); + this.#editor.setValue(editor ?? undefined); } getEditor() { - return this._editor.getValue(); + return this.#editor.getValue(); } // property variant ID: #variantId = new UmbClassState(undefined); public readonly variantId = this.#variantId.asObservable(); - private _variantDifference = new UmbStringState(undefined); - public readonly variantDifference = this._variantDifference.asObservable(); + #variantDifference = new UmbStringState(undefined); + public readonly variantDifference = this.#variantDifference.asObservable(); #datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE; @@ -111,9 +111,20 @@ export class UmbPropertyContext extends UmbContextBase Date: Thu, 8 Aug 2024 11:17:30 +0200 Subject: [PATCH 041/152] alias null --- .../property/property/property.context.ts | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index d271ed61c3..0c121c7299 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -74,17 +74,29 @@ export class UmbPropertyContext extends UmbContextBase { - this._observeProperty(); - }); + this.observe( + this.alias, + () => { + this._observeProperty(); + }, + null, + ); - this.observe(this.configValues, (configValues) => { - this.#config.setValue(configValues ? new UmbPropertyEditorConfigCollection(configValues) : undefined); - }); + this.observe( + this.configValues, + (configValues) => { + this.#config.setValue(configValues ? new UmbPropertyEditorConfigCollection(configValues) : undefined); + }, + null, + ); - this.observe(this.variantId, () => { - this._generateVariantDifferenceString(); - }); + this.observe( + this.variantId, + () => { + this._generateVariantDifferenceString(); + }, + null, + ); } private async _observeProperty(): Promise { From 01e689a4fad4507f6235925aced165df01243b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 11:20:16 +0200 Subject: [PATCH 042/152] text box mandatory --- .../text-box/property-editor-ui-text-box.element.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index a4a8c396a7..11a4d5f2b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -25,6 +25,13 @@ export class UmbPropertyEditorUITextBoxElement @property({ type: Boolean, reflect: true }) readonly = false; + /** + * Sets the input to mandatory, meaning validation will fail if the value is empty. + * @type {boolean} + */ + @property({ type: Boolean }) + mandatory?: boolean; + #defaultType: UuiInputTypeType = 'text'; @state() @@ -69,7 +76,7 @@ export class UmbPropertyEditorUITextBoxElement inputMode=${ifDefined(this._inputMode)} maxlength=${ifDefined(this._maxChars)} @input=${this.#onInput} - ?required=${this.readonly} + ?required=${this.mandatory} ?readonly=${this.readonly}>`; } From f374c21e0ff924aa8ce51e69b0db815edf58a79c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 11:39:13 +0200 Subject: [PATCH 043/152] add build files --- src/Umbraco.Web.UI.Client/package-lock.json | 10 +++++++++- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/packages/relations/package.json | 8 ++++++++ .../src/packages/relations/vite.config.ts | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 54c785f561..9337ef8da6 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -24,6 +24,7 @@ "./src/packages/multi-url-picker", "./src/packages/packages", "./src/packages/property-editors", + "./src/packages/relations", "./src/packages/search", "./src/packages/tags", "./src/packages/templating", @@ -7531,6 +7532,10 @@ "resolved": "src/packages/property-editors", "link": true }, + "node_modules/@umbraco-backoffice/relation": { + "resolved": "src/packages/relations", + "link": true + }, "node_modules/@umbraco-backoffice/search": { "resolved": "src/packages/search", "link": true @@ -22766,10 +22771,13 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, - "src/packages/packages": {}, + "src/packages/packages": { + "name": "@umbraco-backoffice/package" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, + "src/packages/relations": {}, "src/packages/search": { "name": "@umbraco-backoffice/search" }, diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9d5bc0795f..7c55822a85 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -143,6 +143,7 @@ "./src/packages/multi-url-picker", "./src/packages/packages", "./src/packages/property-editors", + "./src/packages/relations", "./src/packages/search", "./src/packages/tags", "./src/packages/templating", diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/package.json b/src/Umbraco.Web.UI.Client/src/packages/relations/package.json new file mode 100644 index 0000000000..ed717f3881 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/relation", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts new file mode 100644 index 0000000000..755e0b84b0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/relations'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); From 904d59525aee7c0968ccc32bc5611606af528672 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 11:39:20 +0200 Subject: [PATCH 044/152] move consts --- .../relations/relation-types/collection/constants.ts | 1 + .../packages/relations/relation-types/collection/index.ts | 2 +- .../relations/relation-types/collection/manifests.ts | 3 +-- .../relation-types/collection/repository/constants.ts | 1 + .../relations/relation-types/collection/repository/index.ts | 2 +- .../relation-types/collection/repository/manifests.ts | 3 +-- .../relations/relation-types/repository/detail/constants.ts | 2 ++ .../relations/relation-types/repository/detail/index.ts | 2 +- .../relations/relation-types/repository/detail/manifests.ts | 5 +---- 9 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/constants.ts new file mode 100644 index 0000000000..8eb94e7ee4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/constants.ts @@ -0,0 +1 @@ +export const UMB_RELATION_TYPE_COLLECTION_ALIAS = 'Umb.Collection.RelationType'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/index.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/index.ts index 64a5d76908..6dcf6dd58e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/index.ts @@ -1,2 +1,2 @@ export { UmbRelationTypeCollectionRepository } from './repository/index.js'; -export { UMB_RELATION_TYPE_COLLECTION_ALIAS } from './manifests.js'; +export { UMB_RELATION_TYPE_COLLECTION_ALIAS } from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/manifests.ts index fa2f027bca..92464432af 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/manifests.ts @@ -1,10 +1,9 @@ +import { UMB_RELATION_TYPE_COLLECTION_ALIAS } from './constants.js'; import { UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS } from './repository/index.js'; import { manifests as collectionRepositoryManifests } from './repository/manifests.js'; import { manifests as collectionViewManifests } from './views/manifests.js'; import type { ManifestCollection, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_RELATION_TYPE_COLLECTION_ALIAS = 'Umb.Collection.RelationType'; - const collectionManifest: ManifestCollection = { type: 'collection', kind: 'default', diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/constants.ts new file mode 100644 index 0000000000..86bdb48b1c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/constants.ts @@ -0,0 +1 @@ +export const UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS = 'Umb.Repository.RelationType.Collection'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/index.ts index 4e128f9d5d..142b390843 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/index.ts @@ -1,2 +1,2 @@ -export { UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS } from './manifests.js'; +export { UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS } from './constants.js'; export { UmbRelationTypeCollectionRepository } from './relation-type-collection.repository.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/manifests.ts index 9de27e01d1..9c879ceacf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/manifests.ts @@ -1,7 +1,6 @@ +import { UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS } from './constants.js'; import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS = 'Umb.Repository.RelationType.Collection'; - const repository: ManifestRepository = { type: 'repository', alias: UMB_RELATION_TYPE_COLLECTION_REPOSITORY_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/constants.ts new file mode 100644 index 0000000000..a1025f4c1c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/constants.ts @@ -0,0 +1,2 @@ +export const UMB_RELATION_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.RelationType.Detail'; +export const UMB_RELATION_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.RelationType.Detail'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/index.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/index.ts index e5e155c2c7..19e0e34ac8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/index.ts @@ -1,2 +1,2 @@ export { UmbRelationTypeDetailRepository } from './relation-type-detail.repository.js'; -export { UMB_RELATION_TYPE_DETAIL_REPOSITORY_ALIAS } from './manifests.js'; +export { UMB_RELATION_TYPE_DETAIL_REPOSITORY_ALIAS } from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/manifests.ts index cf88507bae..9dc550e148 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/manifests.ts @@ -1,7 +1,6 @@ +import { UMB_RELATION_TYPE_DETAIL_REPOSITORY_ALIAS, UMB_RELATION_TYPE_DETAIL_STORE_ALIAS } from './constants.js'; import type { ManifestRepository, ManifestStore, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_RELATION_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.RelationType.Detail'; - const repository: ManifestRepository = { type: 'repository', alias: UMB_RELATION_TYPE_DETAIL_REPOSITORY_ALIAS, @@ -9,8 +8,6 @@ const repository: ManifestRepository = { api: () => import('./relation-type-detail.repository.js'), }; -export const UMB_RELATION_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.RelationType.Detail'; - const store: ManifestStore = { type: 'store', alias: UMB_RELATION_TYPE_DETAIL_STORE_ALIAS, From 4211564550463c6127a6ecb59e9150f08292a855 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 12:24:59 +0200 Subject: [PATCH 045/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 9337ef8da6..22235cb71a 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -22777,7 +22777,9 @@ "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, - "src/packages/relations": {}, + "src/packages/relations": { + "name": "@umbraco-backoffice/relation" + }, "src/packages/search": { "name": "@umbraco-backoffice/search" }, From db2770d23117d222e200dec50f0d0edb66f6fece Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 12:35:28 +0200 Subject: [PATCH 046/152] build submodules --- .../src/packages/relations/vite.config.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts index 755e0b84b0..b1b16816e8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/vite.config.ts @@ -8,5 +8,13 @@ const dist = '../../../dist-cms/packages/relations'; rmSync(dist, { recursive: true, force: true }); export default defineConfig({ - ...getDefaultConfig({ dist }), + ...getDefaultConfig({ + dist, + entry: { + 'relation-types/index': 'relation-types/index.ts', + 'relations/index': 'relations/index.ts', + manifests: 'manifests.ts', + 'umbraco-package': 'umbraco-package.ts', + }, + }), }); From 49d790758e8353cabecfe3d8080aef01fbb24782 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 12:42:56 +0200 Subject: [PATCH 047/152] Delete index.ts --- src/Umbraco.Web.UI.Client/src/packages/relations/index.ts | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/index.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/index.ts deleted file mode 100644 index 2a421f612c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import './relation-types/index.js'; -import './relations/index.js'; - -export * from './manifests.js'; From 73db0520cd0486e489d1603401236328290ce3f8 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 13:13:32 +0200 Subject: [PATCH 048/152] build setup --- src/Umbraco.Web.UI.Client/package-lock.json | 10 +++++++++- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/packages/markdown-editor/package.json | 8 ++++++++ .../src/packages/markdown-editor/vite.config.ts | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/markdown-editor/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/markdown-editor/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 54c785f561..c5b09551f0 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -18,6 +18,7 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", + "./src/packages/markdown-editor", "./src/packages/media", "./src/packages/members", "./src/packages/models-builder", @@ -7507,6 +7508,10 @@ "resolved": "src/packages/language", "link": true }, + "node_modules/@umbraco-backoffice/markdown": { + "resolved": "src/packages/markdown-editor", + "link": true + }, "node_modules/@umbraco-backoffice/media": { "resolved": "src/packages/media", "link": true @@ -22754,6 +22759,7 @@ "src/packages/language": { "name": "@umbraco-backoffice/language" }, + "src/packages/markdown-editor": {}, "src/packages/media": { "name": "@umbraco-backoffice/media" }, @@ -22766,7 +22772,9 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, - "src/packages/packages": {}, + "src/packages/packages": { + "name": "@umbraco-backoffice/package" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9d5bc0795f..cc2d8c88be 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -137,6 +137,7 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", + "./src/packages/markdown-editor", "./src/packages/media", "./src/packages/members", "./src/packages/models-builder", diff --git a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/package.json b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/package.json new file mode 100644 index 0000000000..d0b01652c2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/markdown", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/vite.config.ts new file mode 100644 index 0000000000..1d1c040c00 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/markdown-editor'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); From 5dee0acdaead11b06404bbbfa66f22e5ab7e492d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 13:36:37 +0200 Subject: [PATCH 049/152] build setup --- src/Umbraco.Web.UI.Client/package-lock.json | 10 +++++++++- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/packages/ufm/package.json | 8 ++++++++ .../src/packages/ufm/vite.config.ts | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/ufm/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/ufm/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 54c785f561..d3111eb427 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -28,6 +28,7 @@ "./src/packages/tags", "./src/packages/templating", "./src/packages/tiny-mce", + "./src/packages/ufm", "./src/packages/umbraco-news", "./src/packages/user", "./src/packages/webhook" @@ -7547,6 +7548,10 @@ "resolved": "src/packages/tiny-mce", "link": true }, + "node_modules/@umbraco-backoffice/ufm": { + "resolved": "src/packages/ufm", + "link": true + }, "node_modules/@umbraco-backoffice/umbraco-news": { "resolved": "src/packages/umbraco-news", "link": true @@ -22766,7 +22771,9 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, - "src/packages/packages": {}, + "src/packages/packages": { + "name": "@umbraco-backoffice/package" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, @@ -22782,6 +22789,7 @@ "src/packages/tiny-mce": { "name": "@umbraco-backoffice/tiny-mce" }, + "src/packages/ufm": {}, "src/packages/umbraco-news": { "name": "@umbraco-backoffice/umbraco-news" }, diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9d5bc0795f..da711e452c 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -147,6 +147,7 @@ "./src/packages/tags", "./src/packages/templating", "./src/packages/tiny-mce", + "./src/packages/ufm", "./src/packages/umbraco-news", "./src/packages/user", "./src/packages/webhook" diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/package.json b/src/Umbraco.Web.UI.Client/src/packages/ufm/package.json new file mode 100644 index 0000000000..9868c4a84c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/ufm", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/vite.config.ts new file mode 100644 index 0000000000..4a15f72e73 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/ufm'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); From 80a3a1ca4164fbd65b1d2c385ad4fb7b81bff5d8 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 14:30:40 +0200 Subject: [PATCH 050/152] move object type files to core --- src/Umbraco.Web.UI.Client/package.json | 2 +- .../src/packages/{ => core}/object-type/index.ts | 0 .../{ => core}/object-type/input-object-type.element.ts | 0 .../packages/{ => core}/object-type/object-type.repository.ts | 0 src/Umbraco.Web.UI.Client/src/packages/core/vite.config.ts | 1 + 5 files changed, 2 insertions(+), 1 deletion(-) rename src/Umbraco.Web.UI.Client/src/packages/{ => core}/object-type/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => core}/object-type/input-object-type.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => core}/object-type/object-type.repository.ts (100%) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9d5bc0795f..c47dc6df29 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -58,7 +58,7 @@ "./models": "./dist-cms/packages/core/models/index.js", "./multi-url-picker": "./dist-cms/packages/multi-url-picker/index.js", "./notification": "./dist-cms/packages/core/notification/index.js", - "./object-type": "./dist-cms/packages/object-type/index.js", + "./object-type": "./dist-cms/core/object-type/index.js", "./package": "./dist-cms/packages/packages/package/index.js", "./partial-view": "./dist-cms/packages/templating/partial-views/index.js", "./picker-input": "./dist-cms/packages/core/picker-input/index.js", diff --git a/src/Umbraco.Web.UI.Client/src/packages/object-type/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/object-type/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/object-type/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/object-type/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/object-type/input-object-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/object-type/input-object-type.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/object-type/input-object-type.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/object-type/input-object-type.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/object-type/object-type.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/object-type/object-type.repository.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/object-type/object-type.repository.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/object-type/object-type.repository.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/core/vite.config.ts index 3b9b7511b2..c04299b8e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/vite.config.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/vite.config.ts @@ -34,6 +34,7 @@ export default defineConfig({ 'modal/index': './modal/index.ts', 'models/index': './models/index.ts', 'notification/index': './notification/index.ts', + 'object-type/index': './object-type/index.ts', 'picker-input/index': './picker-input/index.ts', 'property-action/index': './property-action/index.ts', 'property-editor/index': './property-editor/index.ts', From 4074f054a6eae3bbecea13225c8d8883ba7b9107 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 14:50:31 +0200 Subject: [PATCH 051/152] set up telemetry package and bundle --- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/apps/backoffice/backoffice.element.ts | 1 + .../packages/settings/dashboards/manifests.ts | 17 ----------------- .../telemetry/dashboard-telemetry.element.ts | 0 .../telemetry/dashboard-telemetry.stories.ts | 0 .../telemetry/dashboard-telemetry.test.ts | 0 .../src/packages/telemetry/manifests.ts | 19 +++++++++++++++++++ .../src/packages/telemetry/package.json | 8 ++++++++ .../src/packages/telemetry/umbraco-package.ts | 9 +++++++++ .../src/packages/telemetry/vite.config.ts | 18 ++++++++++++++++++ 10 files changed, 56 insertions(+), 17 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards => }/telemetry/dashboard-telemetry.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards => }/telemetry/dashboard-telemetry.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards => }/telemetry/dashboard-telemetry.test.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/telemetry/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/telemetry/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/telemetry/umbraco-package.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/telemetry/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9d5bc0795f..c3c0a4175c 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -145,6 +145,7 @@ "./src/packages/property-editors", "./src/packages/search", "./src/packages/tags", + "./src/packages/telemetry", "./src/packages/templating", "./src/packages/tiny-mce", "./src/packages/umbraco-news", diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index b5b6c47879..e4920ef302 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -38,6 +38,7 @@ const CORE_PACKAGES = [ import('../../packages/umbraco-news/umbraco-package.js'), import('../../packages/user/umbraco-package.js'), import('../../packages/webhook/umbraco-package.js'), + import('../../packages/telemetry/umbraco-package.js'), ]; @customElement('umb-backoffice') diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts index 0d4c10f4e0..555f2cd9a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts @@ -37,23 +37,6 @@ const dashboards: Array = [ }, ], }, - { - type: 'dashboard', - alias: 'Umb.Dashboard.Telemetry', - name: 'Telemetry', - element: () => import('./telemetry/dashboard-telemetry.element.js'), - weight: 100, - meta: { - label: 'Telemetry Data', - pathname: 'telemetry', - }, - conditions: [ - { - alias: 'Umb.Condition.SectionAlias', - match: sectionAlias, - }, - ], - }, ]; export const manifests: Array = [...dashboards]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/telemetry/dashboard-telemetry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/telemetry/dashboard-telemetry.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/telemetry/dashboard-telemetry.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/telemetry/dashboard-telemetry.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/telemetry/dashboard-telemetry.test.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/telemetry/dashboard-telemetry.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/telemetry/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/manifests.ts new file mode 100644 index 0000000000..de18e665e7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/telemetry/manifests.ts @@ -0,0 +1,19 @@ +export const manifests = [ + { + type: 'dashboard', + alias: 'Umb.Dashboard.Telemetry', + name: 'Telemetry', + element: () => import('./dashboard-telemetry.element.js'), + weight: 100, + meta: { + label: 'Telemetry Data', + pathname: 'telemetry', + }, + conditions: [ + { + alias: 'Umb.Condition.SectionAlias', + match: 'Umb.Section.Settings', + }, + ], + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/telemetry/package.json b/src/Umbraco.Web.UI.Client/src/packages/telemetry/package.json new file mode 100644 index 0000000000..80800d336f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/telemetry/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/telemetry", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/telemetry/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/umbraco-package.ts new file mode 100644 index 0000000000..4094ad7c24 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/telemetry/umbraco-package.ts @@ -0,0 +1,9 @@ +export const name = 'Umbraco.Core.Telemetry'; +export const extensions = [ + { + name: 'Telemetry Bundle', + alias: 'Umb.Bundle.Telemetry', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/telemetry/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/vite.config.ts new file mode 100644 index 0000000000..15b98a42d8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/telemetry/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/telemetry'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ + dist, + entry: { + manifests: 'manifests.ts', + 'umbraco-package': 'umbraco-package.ts', + }, + }), +}); From e2f94a9bfb6b11e6bbade9f277fa871dfbd178f7 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 14:52:28 +0200 Subject: [PATCH 052/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 54c785f561..a402cb93ed 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -26,6 +26,7 @@ "./src/packages/property-editors", "./src/packages/search", "./src/packages/tags", + "./src/packages/telemetry", "./src/packages/templating", "./src/packages/tiny-mce", "./src/packages/umbraco-news", @@ -7539,6 +7540,10 @@ "resolved": "src/packages/tags", "link": true }, + "node_modules/@umbraco-backoffice/telemetry": { + "resolved": "src/packages/telemetry", + "link": true + }, "node_modules/@umbraco-backoffice/templating": { "resolved": "src/packages/templating", "link": true @@ -22766,7 +22771,9 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, - "src/packages/packages": {}, + "src/packages/packages": { + "name": "@umbraco-backoffice/package" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, @@ -22776,6 +22783,7 @@ "src/packages/tags": { "name": "@umbraco-backoffice/tag" }, + "src/packages/telemetry": {}, "src/packages/templating": { "name": "@umbraco-backoffice/templating" }, From fe5ba560273cc3dcb46c8e1f856f784937393c82 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 14:54:02 +0200 Subject: [PATCH 053/152] Update tsconfig.json --- src/Umbraco.Web.UI.Client/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 0c726f16e7..f92de6ee2d 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -84,7 +84,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "@umbraco-cms/backoffice/models": ["./src/packages/core/models/index.ts"], "@umbraco-cms/backoffice/multi-url-picker": ["./src/packages/multi-url-picker/index.ts"], "@umbraco-cms/backoffice/notification": ["./src/packages/core/notification/index.ts"], - "@umbraco-cms/backoffice/object-type": ["./src/packages/object-type/index.ts"], + "@umbraco-cms/backoffice/object-type": ["./src/core/object-type/index.ts"], "@umbraco-cms/backoffice/package": ["./src/packages/packages/package/index.ts"], "@umbraco-cms/backoffice/partial-view": ["./src/packages/templating/partial-views/index.ts"], "@umbraco-cms/backoffice/picker-input": ["./src/packages/core/picker-input/index.ts"], From 62fa41a5cdcca10456b4522ed24c42415cb0d0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 16:15:06 +0200 Subject: [PATCH 054/152] use const --- .../src/packages/core/validation/const.ts | 1 + .../controllers/server-model-validation.context.ts | 3 ++- .../src/packages/core/validation/index.ts | 1 + .../text-box/property-editor-ui-text-box.element.ts | 5 ++++- 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/const.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/const.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/const.ts new file mode 100644 index 0000000000..0160e54bf4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/const.ts @@ -0,0 +1 @@ +export const UMB_VALIDATION_EMPTY_LOCALIZATION_KEY = '#validation_invalidEmpty'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index 89df70a0ac..8701e7cda3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -6,6 +6,7 @@ import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from './server-model-validation.c import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../const.js'; /** This should ideally be generated by the server, but we currently don't generate error-model-types. */ interface ValidateErrorResponseBodyModel { @@ -91,7 +92,7 @@ export class UmbServerModelValidationContext segment: variant.segment, })}].value`; // TODO: Make a const that holds this translation-key: - this.#context!.messages.addMessages('server', path, ['#validation_invalidEmpty']); + this.#context!.messages.addMessages('server', path, [UMB_VALIDATION_EMPTY_LOCALIZATION_KEY]); }); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts index 9e9b667fa8..10a56d0882 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts @@ -1,3 +1,4 @@ +export * from './const.js'; export * from './context/index.js'; export * from './controllers/index.js'; export * from './events/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 11a4d5f2b0..4cac623483 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -7,7 +7,7 @@ import { type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; -import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; type UuiInputTypeType = typeof UUIInputElement.prototype.type; @@ -31,6 +31,8 @@ export class UmbPropertyEditorUITextBoxElement */ @property({ type: Boolean }) mandatory?: boolean; + @property({ type: String }) + mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY; #defaultType: UuiInputTypeType = 'text'; @@ -77,6 +79,7 @@ export class UmbPropertyEditorUITextBoxElement maxlength=${ifDefined(this._maxChars)} @input=${this.#onInput} ?required=${this.mandatory} + .requiredMessage=${this.mandatoryMessage} ?readonly=${this.readonly}>`; } From 63de7a69138dac5507c97309063f456fd4e75455 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 16:45:41 +0200 Subject: [PATCH 055/152] order --- .../src/apps/backoffice/backoffice.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index e4920ef302..0975e05086 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -32,13 +32,13 @@ const CORE_PACKAGES = [ import('../../packages/settings/umbraco-package.js'), import('../../packages/static-file/umbraco-package.js'), import('../../packages/tags/umbraco-package.js'), + import('../../packages/telemetry/umbraco-package.js'), import('../../packages/templating/umbraco-package.js'), import('../../packages/tiny-mce/umbraco-package.js'), import('../../packages/ufm/umbraco-package.js'), import('../../packages/umbraco-news/umbraco-package.js'), import('../../packages/user/umbraco-package.js'), import('../../packages/webhook/umbraco-package.js'), - import('../../packages/telemetry/umbraco-package.js'), ]; @customElement('umb-backoffice') From 0315ee79884fd49dd0d65783cc1e2e410dbfdadf Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 8 Aug 2024 16:47:01 +0200 Subject: [PATCH 056/152] add performance profiling package --- .../src/apps/backoffice/backoffice.element.ts | 1 + ...dashboard-performance-profiling.element.ts | 0 ...dashboard-performance-profiling.stories.ts | 0 .../performance-profiling/manifests.ts | 19 +++++++++++++++++++ .../performance-profiling/umbraco-package.ts | 9 +++++++++ .../packages/settings/dashboards/manifests.ts | 17 ----------------- 6 files changed, 29 insertions(+), 17 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards => }/performance-profiling/dashboard-performance-profiling.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards => }/performance-profiling/dashboard-performance-profiling.stories.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/performance-profiling/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/performance-profiling/umbraco-package.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 0975e05086..e81376198b 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -26,6 +26,7 @@ const CORE_PACKAGES = [ import('../../packages/models-builder/umbraco-package.js'), import('../../packages/multi-url-picker/umbraco-package.js'), import('../../packages/packages/umbraco-package.js'), + import('../../packages/performance-profiling/umbraco-package.js'), import('../../packages/property-editors/umbraco-package.js'), import('../../packages/relations/umbraco-package.js'), import('../../packages/search/umbraco-package.js'), diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/performance-profiling/dashboard-performance-profiling.element.ts b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/dashboard-performance-profiling.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/performance-profiling/dashboard-performance-profiling.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/performance-profiling/dashboard-performance-profiling.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/performance-profiling/dashboard-performance-profiling.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/dashboard-performance-profiling.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/performance-profiling/dashboard-performance-profiling.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/performance-profiling/dashboard-performance-profiling.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/manifests.ts new file mode 100644 index 0000000000..756137ea28 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/manifests.ts @@ -0,0 +1,19 @@ +export const manifests = [ + { + type: 'dashboard', + alias: 'Umb.Dashboard.Profiling', + name: 'Profiling', + element: () => import('./dashboard-performance-profiling.element.js'), + weight: 101, + meta: { + label: '#dashboardTabs_settingsProfiler', + pathname: 'profiling', + }, + conditions: [ + { + alias: 'Umb.Condition.SectionAlias', + match: 'Umb.Section.Settings', + }, + ], + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/umbraco-package.ts new file mode 100644 index 0000000000..1ee024fcbc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/umbraco-package.ts @@ -0,0 +1,9 @@ +export const name = 'Umbraco.Core.PerformanceProfiling'; +export const extensions = [ + { + name: 'Performance Profiling Bundle', + alias: 'Umb.Bundle.PerformanceProfiling', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts index 555f2cd9a7..1b01222a60 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts @@ -20,23 +20,6 @@ const dashboards: Array = [ }, ], }, - { - type: 'dashboard', - alias: 'Umb.Dashboard.Profiling', - name: 'Profiling', - element: () => import('./performance-profiling/dashboard-performance-profiling.element.js'), - weight: 101, - meta: { - label: '#dashboardTabs_settingsProfiler', - pathname: 'profiling', - }, - conditions: [ - { - alias: 'Umb.Condition.SectionAlias', - match: sectionAlias, - }, - ], - }, ]; export const manifests: Array = [...dashboards]; From cd84ff9b6d3562529b6bcf3044badb673018aa0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 20:33:12 +0200 Subject: [PATCH 057/152] POC for single message based validation --- .../validation/mixins/form-control.mixin.ts | 63 +++++++++++++------ .../property-editor-ui-text-box.element.ts | 14 +++++ 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index d50342ea8c..a94eca0182 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -13,8 +13,8 @@ type UmbNativeFormControlElement = Pick< * https://developer.mozilla.org/en-US/docs/Web/API/ValidityState * */ type FlagTypes = - | 'badInput' | 'customError' + | 'badInput' | 'patternMismatch' | 'rangeOverflow' | 'rangeUnderflow' @@ -23,9 +23,21 @@ type FlagTypes = | 'tooShort' | 'typeMismatch' | 'valueMissing' - | 'badInput' | 'valid'; +const WeightedErrorFlagTypes = [ + 'customError', + 'badInput', + 'patternMismatch', + 'rangeOverflow', + 'rangeUnderflow', + 'stepMismatch', + 'tooLong', + 'tooShort', + 'typeMismatch', + 'valueMissing', +]; + // Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public interface in a separate file. export interface UmbFormControlValidatorConfig { flagKey: FlagTypes; @@ -222,6 +234,11 @@ export function UmbFormControlMixin< checkMethod: checkMethod, } satisfies UmbFormControlValidatorConfig; this.#validators.push(validator); + // Sort validators based on the WeightedErrorFlagTypes order. [NL] + this.#validators.sort((a, b) => { + // This could easily be extended with a weight set on the validator object itself. [NL] + return WeightedErrorFlagTypes.indexOf(a.flagKey) - WeightedErrorFlagTypes.indexOf(b.flagKey); + }); return validator; } @@ -291,29 +308,38 @@ export function UmbFormControlMixin< */ protected _runValidators() { this.#validity = {}; - const messages: Set = new Set(); + //const messages: Set = new Set(); + let message: string | undefined = undefined; let innerFormControlEl: UmbNativeFormControlElement | undefined = undefined; - // Loop through inner native form controls to adapt their validityState. [NL] - this.#formCtrlElements.forEach((formCtrlEl) => { - let key: keyof ValidityState; - for (key in formCtrlEl.validity) { - if (key !== 'valid' && formCtrlEl.validity[key]) { - this.#validity[key] = true; - messages.add(formCtrlEl.validationMessage); - innerFormControlEl ??= formCtrlEl; - } - } - }); - // Loop through custom validators, currently its intentional to have them overwritten native validity. but might need to be reconsidered (This current way enables to overwrite with custom messages) [NL] - this.#validators.forEach((validator) => { + this.#validators.some((validator) => { if (validator.checkMethod()) { this.#validity[validator.flagKey] = true; - messages.add(validator.getMessageMethod()); + //messages.add(validator.getMessageMethod()); + message = validator.getMessageMethod(); + return true; } + return false; }); + if (message) { + // Loop through inner native form controls to adapt their validityState. [NL] + this.#formCtrlElements.some((formCtrlEl) => { + let key: keyof ValidityState; + for (key in formCtrlEl.validity) { + if (key !== 'valid' && formCtrlEl.validity[key]) { + this.#validity[key] = true; + //messages.add(formCtrlEl.validationMessage); + message = formCtrlEl.validationMessage; + innerFormControlEl ??= formCtrlEl; + return true; + } + } + return false; + }); + } + const hasError = Object.values(this.#validity).includes(true); // https://developer.mozilla.org/en-US/docs/Web/API/ValidityState#valid @@ -323,7 +349,8 @@ export function UmbFormControlMixin< this._internals.setValidity( this.#validity, // Turn messages into an array and join them with a comma. [NL]: - [...messages].join(', '), + //[...messages].join(', '), + message, innerFormControlEl ?? this.getFormElement() ?? undefined, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 4cac623483..0edcbdc497 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -55,6 +55,20 @@ export class UmbPropertyEditorUITextBoxElement this._placeholder = config?.getValueByAlias('placeholder'); } + constructor() { + super(); + this.addValidator( + 'tooShort', + () => 'custom too short msg', + () => String(this.value).length < 10, + ); + this.addValidator( + 'valueMissing', + () => 'custom valueMissing msg', + () => this.value === undefined && this.value === '', + ); + } + protected override firstUpdated(): void { this.addFormControlElement(this.shadowRoot!.querySelector('uui-input')!); } From 3bdb14e30c38e1f5444e678b45c7c2fab43728d9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 9 Aug 2024 09:59:09 +0200 Subject: [PATCH 058/152] build setup --- src/Umbraco.Web.UI.Client/package.json | 1 + .../performance-profiling/package.json | 8 ++++++++ .../performance-profiling/vite.config.ts | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/performance-profiling/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/performance-profiling/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index d5c2bf20e7..a9c05c834c 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -144,6 +144,7 @@ "./src/packages/models-builder", "./src/packages/multi-url-picker", "./src/packages/packages", + "./src/packages/performance-profiling", "./src/packages/property-editors", "./src/packages/relations", "./src/packages/search", diff --git a/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/package.json b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/package.json new file mode 100644 index 0000000000..f7aa90a5f6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/performance-profiling", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/vite.config.ts new file mode 100644 index 0000000000..22ca20f837 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/performance-profiling/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/performance-profiling'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ + dist, + entry: { + manifests: 'manifests.ts', + 'umbraco-package': 'umbraco-package.ts', + }, + }), +}); From f15755fe75a187d9bc8aab700e816df929189246 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 9 Aug 2024 10:12:06 +0200 Subject: [PATCH 059/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 34 +++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 859c860d9b..60fbedf475 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -25,6 +25,7 @@ "./src/packages/models-builder", "./src/packages/multi-url-picker", "./src/packages/packages", + "./src/packages/performance-profiling", "./src/packages/property-editors", "./src/packages/relations", "./src/packages/search", @@ -7513,14 +7514,14 @@ "resolved": "src/packages/language", "link": true }, - "node_modules/@umbraco-backoffice/markdown": { - "resolved": "src/packages/markdown-editor", - "link": true - }, "node_modules/@umbraco-backoffice/log-viewer": { "resolved": "src/packages/log-viewer", "link": true }, + "node_modules/@umbraco-backoffice/markdown": { + "resolved": "src/packages/markdown-editor", + "link": true + }, "node_modules/@umbraco-backoffice/media": { "resolved": "src/packages/media", "link": true @@ -7541,6 +7542,10 @@ "resolved": "src/packages/packages", "link": true }, + "node_modules/@umbraco-backoffice/performance-profiling": { + "resolved": "src/packages/performance-profiling", + "link": true + }, "node_modules/@umbraco-backoffice/property-editors": { "resolved": "src/packages/property-editors", "link": true @@ -22784,8 +22789,12 @@ "src/packages/language": { "name": "@umbraco-backoffice/language" }, - "src/packages/markdown-editor": {}, - "src/packages/log-viewer": {}, + "src/packages/log-viewer": { + "name": "@umbraco-backoffice/log-viewer" + }, + "src/packages/markdown-editor": { + "name": "@umbraco-backoffice/markdown" + }, "src/packages/media": { "name": "@umbraco-backoffice/media" }, @@ -22801,6 +22810,7 @@ "src/packages/packages": { "name": "@umbraco-backoffice/package" }, + "src/packages/performance-profiling": {}, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, @@ -22810,18 +22820,24 @@ "src/packages/search": { "name": "@umbraco-backoffice/search" }, - "src/packages/static-file": {}, + "src/packages/static-file": { + "name": "@umbraco-backoffice/static-file" + }, "src/packages/tags": { "name": "@umbraco-backoffice/tag" }, - "src/packages/telemetry": {}, + "src/packages/telemetry": { + "name": "@umbraco-backoffice/telemetry" + }, "src/packages/templating": { "name": "@umbraco-backoffice/templating" }, "src/packages/tiny-mce": { "name": "@umbraco-backoffice/tiny-mce" }, - "src/packages/ufm": {}, + "src/packages/ufm": { + "name": "@umbraco-backoffice/ufm" + }, "src/packages/umbraco-news": { "name": "@umbraco-backoffice/umbraco-news" }, From 549a49faa726bc7c136d8cec59f43e4ca7bdb462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 11:55:17 +0200 Subject: [PATCH 060/152] package-lock --- src/Umbraco.Web.UI.Client/package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 54c785f561..2fe99b5ec8 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -22766,7 +22766,9 @@ "src/packages/multi-url-picker": { "name": "@umbraco-backoffice/multi-url-picker" }, - "src/packages/packages": {}, + "src/packages/packages": { + "name": "@umbraco-backoffice/package" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, From bccc4fd5c12b36855cf048e6579b309c8d7eaf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 12:03:12 +0200 Subject: [PATCH 061/152] comment update --- .../workspace/contexts/submittable-workspace-context-base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts index b98584e961..d0adc3d42e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts @@ -42,7 +42,7 @@ export abstract class UmbSubmittableWorkspaceContextBase constructor(host: UmbControllerHost, workspaceAlias: string) { super(host, UMB_WORKSPACE_CONTEXT.toString()); this.workspaceAlias = workspaceAlias; - // TODO: Consider if we can turn this consumption to submitComplete, just as a getContext. [NL] + // TODO: Consider if we can move this consumption to #resolveSubmit, just as a getContext, but it depends if others use the modalContext prop.. [NL] this.consumeContext(UMB_MODAL_CONTEXT, (context) => { (this.modalContext as UmbModalContext) = context; }); From 4c22db7b99954ac8bec1ed609edb1a75027163f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 12:03:21 +0200 Subject: [PATCH 062/152] lint --- .../property-editor-ui-block-grid-areas-config.element.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-areas-config/property-editor-ui-block-grid-areas-config.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-areas-config/property-editor-ui-block-grid-areas-config.element.ts index bfa02c4f6b..56ed2f9a2b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-areas-config/property-editor-ui-block-grid-areas-config.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-areas-config/property-editor-ui-block-grid-areas-config.element.ts @@ -127,10 +127,7 @@ export class UmbPropertyEditorUIBlockGridAreasConfigElement .key=${area.key}>`, )}
- ` + ` : ''; } } From 3c890333f0e46bce714f5210ef2e178ea4eea5c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 12:03:30 +0200 Subject: [PATCH 063/152] sort imports --- .../validation/controllers/server-model-validation.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index 8701e7cda3..e511d8f561 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -2,11 +2,11 @@ import type { UmbValidationMessageTranslator } from '../translators/validation-m import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../const.js'; import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from './server-model-validation.context-token.js'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; -import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../const.js'; /** This should ideally be generated by the server, but we currently don't generate error-model-types. */ interface ValidateErrorResponseBodyModel { From 70a9dd55bbd28e0db186059dc7dfd2964dcc64cc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 9 Aug 2024 12:09:55 +0200 Subject: [PATCH 064/152] remove postinstall script --- src/Umbraco.Web.UI.Client/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 8474c9b433..4ae71b4ce4 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -161,7 +161,6 @@ "check": "npm run lint:errors && npm run compile && npm run build-storybook && npm run generate:jsonschema:dist", "check:paths": "node ./devops/build/check-path-length.js dist-cms 120", "compile": "tsc", - "postinstall": "npm run generate:tsconfig", "dev": "vite", "dev:server": "VITE_UMBRACO_USE_MSW=off vite", "dev:mock": "VITE_UMBRACO_USE_MSW=on vite", From c5e73877479c187317ec99e966cedfd0c0e2a62d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 9 Aug 2024 13:35:20 +0200 Subject: [PATCH 065/152] export member collection module --- src/Umbraco.Web.UI.Client/src/packages/members/member/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/index.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/index.ts index 3a751ec32f..e5bebd5e5d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/index.ts @@ -3,3 +3,4 @@ import './components/index.js'; export * from './entity.js'; export * from './components/index.js'; export * from './repository/index.js'; +export * from './collection/index.js'; From 1a71e7c767098776d9e0cec59858746989a7cfc6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 9 Aug 2024 13:35:32 +0200 Subject: [PATCH 066/152] export member group collection module --- .../src/packages/members/member-group/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/index.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/index.ts index 9ea0d68960..d8c7e845f9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/index.ts @@ -2,6 +2,7 @@ import './components/index.js'; export * from './entity.js'; export * from './repository/index.js'; +export * from './collection/index.js'; export * from './components/index.js'; export type { UmbMemberGroupDetailModel } from './types.js'; From 7a3d24aa5b46b9ac91a671a3ce9677d08cbc7ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 13:54:35 +0200 Subject: [PATCH 067/152] concept notes --- .../validation/context/validation.context.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 797d0ef786..255d520b00 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -9,12 +9,42 @@ export class UmbValidationContext extends UmbContextBase i #validationMode: boolean = false; #isValid: boolean = false; + #parent?: UmbValidationContext; + #baseDataPath?: string; + public readonly messages = new UmbValidationMessagesManager(); constructor(host: UmbControllerHost) { super(host, UMB_VALIDATION_CONTEXT); } + setDataPath(dataPath: string): void { + if (this.#baseDataPath) { + // Just fire an error, as I haven't made the right clean up jet. Or haven't thought about what should happen if it changes while already setup. + // cause maybe all the messages should be removed as we are not interested in the old once any more. But then on the other side, some might be relevant as this is the same entity that changed its paths? + throw new Error('Data path is already set, we do not support changing the context data-path as of now.'); + } + this.#baseDataPath = dataPath; + + this.consumeContext(UMB_VALIDATION_CONTEXT, (parent) => { + if (this.#parent) { + this.#parent.removeValidator(this); + } + this.#parent = parent; + parent.addValidator(this); + + // observe parent messages that fits with the path?. + // — Transfer message, without the base path? + + // observe our messages and make sure to propagate them? or observe our messages, if we have one, set one message to the parent with out base path. Or parse all?. + // Maybe it has to actively be add or remove... + + // Question is do we want sub messages to be added to the parent? or do we want to keep them separate?. + // How about removing sub messages, a sub message could come from the server/parent. so who decides to remove it? I would say it still should be the property editor. + // So maybe only remove if they exist upward, but not append upward? Maybe only append upward one for the basePath, and then if we have one message when getting spun up, we can run validation and if validation is good we remove the base-path from the parent. + }).skipHost(); + } + get isValid(): boolean { return this.#isValid; } @@ -90,6 +120,9 @@ export class UmbValidationContext extends UmbContextBase i } override destroy(): void { + this.removeFromParent(); + this.messages.destroy(); + (this.messages as any) = undefined; this.#destroyValidators(); super.destroy(); } From e2e8e3435150618e385b78c77900cb9837575e46 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 9 Aug 2024 14:43:07 +0200 Subject: [PATCH 068/152] add publish cache package and bundle --- src/Umbraco.Web.UI.Client/package.json | 3 ++- .../src/apps/backoffice/backoffice.element.ts | 1 + .../dashboard-published-status.element.ts | 0 .../dashboard-published-status.stories.ts | 0 .../dashboard-published-status.test.ts | 0 .../src/packages/publish-cache/manifests.ts | 19 ++++++++++++++ .../src/packages/publish-cache/package.json | 8 ++++++ .../packages/publish-cache/umbraco-package.ts | 2 ++ .../src/packages/publish-cache/vite.config.ts | 18 +++++++++++++ .../packages/settings/dashboards/manifests.ts | 25 ------------------- .../src/packages/settings/manifests.ts | 4 --- .../src/packages/settings/umbraco-package.ts | 9 +------ 12 files changed, 51 insertions(+), 38 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards/published-status => publish-cache}/dashboard-published-status.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards/published-status => publish-cache}/dashboard-published-status.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{settings/dashboards/published-status => publish-cache}/dashboard-published-status.test.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/publish-cache/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/publish-cache/package.json create mode 100644 src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/publish-cache/vite.config.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/settings/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index a9c05c834c..e3001ee40c 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -137,8 +137,8 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", - "./src/packages/markdown-editor", "./src/packages/log-viewer", + "./src/packages/markdown-editor", "./src/packages/media", "./src/packages/members", "./src/packages/models-builder", @@ -146,6 +146,7 @@ "./src/packages/packages", "./src/packages/performance-profiling", "./src/packages/property-editors", + "./src/packages/publish-cache", "./src/packages/relations", "./src/packages/search", "./src/packages/static-file", diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index e81376198b..d59e98126c 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -28,6 +28,7 @@ const CORE_PACKAGES = [ import('../../packages/packages/umbraco-package.js'), import('../../packages/performance-profiling/umbraco-package.js'), import('../../packages/property-editors/umbraco-package.js'), + import('../../packages/publish-cache/umbraco-package.js'), import('../../packages/relations/umbraco-package.js'), import('../../packages/search/umbraco-package.js'), import('../../packages/settings/umbraco-package.js'), diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/published-status/dashboard-published-status.element.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/dashboard-published-status.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/published-status/dashboard-published-status.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/publish-cache/dashboard-published-status.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/published-status/dashboard-published-status.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/dashboard-published-status.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/published-status/dashboard-published-status.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/publish-cache/dashboard-published-status.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/published-status/dashboard-published-status.test.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/dashboard-published-status.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/published-status/dashboard-published-status.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/publish-cache/dashboard-published-status.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/publish-cache/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/manifests.ts new file mode 100644 index 0000000000..de1be9b57e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/manifests.ts @@ -0,0 +1,19 @@ +export const manifests = [ + { + type: 'dashboard', + alias: 'Umb.Dashboard.PublishedStatus', + name: 'Published Status Dashboard', + element: () => import('./dashboard-published-status.element.js'), + weight: 300, + meta: { + label: '#dashboardTabs_settingsPublishedStatus', + pathname: 'published-status', + }, + conditions: [ + { + alias: 'Umb.Condition.SectionAlias', + match: 'Umb.Section.Settings', + }, + ], + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/publish-cache/package.json b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/package.json new file mode 100644 index 0000000000..0a7fb90a2a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/publish-cache", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts new file mode 100644 index 0000000000..b7e6f07906 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts @@ -0,0 +1,2 @@ +export const name = 'Umbraco.Core.PublishCache'; +export const extensions = []; diff --git a/src/Umbraco.Web.UI.Client/src/packages/publish-cache/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/vite.config.ts new file mode 100644 index 0000000000..f9a7968319 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/publish-cache'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ + dist, + entry: { + manifests: 'manifests.ts', + 'umbraco-package': 'umbraco-package.ts', + }, + }), +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts deleted file mode 100644 index 1b01222a60..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ManifestDashboard, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; - -const sectionAlias = 'Umb.Section.Settings'; - -const dashboards: Array = [ - { - type: 'dashboard', - alias: 'Umb.Dashboard.PublishedStatus', - name: 'Published Status Dashboard', - element: () => import('./published-status/dashboard-published-status.element.js'), - weight: 300, - meta: { - label: '#dashboardTabs_settingsPublishedStatus', - pathname: 'published-status', - }, - conditions: [ - { - alias: 'Umb.Condition.SectionAlias', - match: sectionAlias, - }, - ], - }, -]; - -export const manifests: Array = [...dashboards]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/manifests.ts deleted file mode 100644 index 72ba9e5dbd..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/manifests.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { manifests as dashboardManifests } from './dashboards/manifests.js'; -import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; - -export const manifests: Array = [...dashboardManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts index 87a8be3eec..4f594994c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts @@ -1,9 +1,2 @@ export const name = 'Umbraco.Core.Settings'; -export const extensions = [ - { - name: 'Settings Bundle', - alias: 'Umb.Bundle.Settings', - type: 'bundle', - js: () => import('./manifests.js'), - }, -]; +export const extensions = []; From 1e64a02e4d3e5d5b9fba23b4ec65f1466fc37aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 19:54:27 +0200 Subject: [PATCH 069/152] remove validator when disconnected --- ...alidation-message-translator.controller.ts | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts index 72bd15df9c..de829b4aca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts @@ -1,23 +1,33 @@ -import type { UmbServerModelValidationContext } from '../controllers/server-model-validation.context.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import type { UmbValidationMessageTranslator } from './validation-message-translator.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from '../index.js'; export class UmbVariantValuesValidationMessageTranslator extends UmbControllerBase implements UmbValidationMessageTranslator { // - #context: UmbServerModelValidationContext; + #context?: typeof UMB_SERVER_MODEL_VALIDATION_CONTEXT.TYPE; - constructor(host: UmbControllerHost, context: UmbServerModelValidationContext) { + constructor(host: UmbControllerHost) { super(host); - context.addTranslator(this); - this.#context = context; + + this.consumeContext(UMB_SERVER_MODEL_VALIDATION_CONTEXT, (context) => { + this.#context = context; + context.addTranslator(this); + }); + } + + override hostDisconnected(): void { + this.#context?.removeTranslator(this); + this.#context = undefined; + super.hostDisconnected(); } translate(path: string) { + if (!this.#context) return; if (path.indexOf('$.values[') !== 0) { // We do not handle this path. return false; @@ -43,8 +53,10 @@ export class UmbVariantValuesValidationMessageTranslator return '$.values[' + UmbDataPathPropertyValueFilter(specificValue) + path.substring(path.indexOf(']')); } + //hostDisconnected is called when a controller is begin destroyed, so this destroy method is not needed. + /* override destroy(): void { super.destroy(); this.#context.removeTranslator(this); - } + }*/ } From 39eb9e02e96169502c6e5b6a66263a4acffa455d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 19:54:33 +0200 Subject: [PATCH 070/152] further concept notes --- .../core/validation/context/validation.context.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 255d520b00..36d63f3df2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -42,6 +42,14 @@ export class UmbValidationContext extends UmbContextBase i // Question is do we want sub messages to be added to the parent? or do we want to keep them separate?. // How about removing sub messages, a sub message could come from the server/parent. so who decides to remove it? I would say it still should be the property editor. // So maybe only remove if they exist upward, but not append upward? Maybe only append upward one for the basePath, and then if we have one message when getting spun up, we can run validation and if validation is good we remove the base-path from the parent. + + // Whats the value of having them local? + // It makes it more complex, + // It makes it harder to debug + // But it makes it easier to remove translators as they get taken out. But maybe this is handled by the controller pattern? + // The amount of translator instances will still be the same, as we then end up with multiple validation contexts and therefor just more things, not necessary less. + + // We may still do so, but only for Workspaces. }).skipHost(); } @@ -120,7 +128,10 @@ export class UmbValidationContext extends UmbContextBase i } override destroy(): void { - this.removeFromParent(); + if (this.#parent) { + this.#parent.removeValidator(this); + } + this.#parent = undefined; this.messages.destroy(); (this.messages as any) = undefined; this.#destroyValidators(); From fe052375e36fbeac31691470b36863b4d37bc168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 20:13:57 +0200 Subject: [PATCH 071/152] path translators --- .../context/validation-messages.manager.ts | 2 +- .../server-model-validation.context.ts | 2 +- .../core/validation/translators/index.ts | 5 +- ...tion-message-path-translator.interface.ts} | 0 ...idation-path-translator-base.controller.ts | 29 +++++++++ ...alidation-message-translator.controller.ts | 62 ------------------- ...s-validation-path-translator.controller.ts | 31 ++++++++++ .../workspace/document-workspace.context.ts | 4 +- 8 files changed, 67 insertions(+), 68 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/{validation-message-translator.interface.ts => validation-message-path-translator.interface.ts} (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 877bb5630d..a04c28b8ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -1,4 +1,4 @@ -import type { UmbValidationMessageTranslator } from '../translators/validation-message-translator.interface.js'; +import type { UmbValidationMessageTranslator } from '../translators/validation-message-path-translator.interface.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index e511d8f561..fc54223d05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -1,4 +1,4 @@ -import type { UmbValidationMessageTranslator } from '../translators/validation-message-translator.interface.js'; +import type { UmbValidationMessageTranslator } from '../translators/validation-message-path-translator.interface.js'; import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts index 43b09bb49f..661a080599 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts @@ -1,2 +1,3 @@ -export type * from './validation-message-translator.interface.js'; -export * from './variant-values-validation-message-translator.controller.js'; +export type * from './validation-message-path-translator.interface.js'; +export * from './variant-values-validation-path-translator.controller.js'; +export * from './validation-path-translator-base.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-translator.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-path-translator.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-translator.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-message-path-translator.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts new file mode 100644 index 0000000000..e541cfe777 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts @@ -0,0 +1,29 @@ +import type { UmbValidationMessageTranslator } from './validation-message-path-translator.interface.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from '../index.js'; + +export abstract class UmbValidationPathTranslatorBase + extends UmbControllerBase + implements UmbValidationMessageTranslator +{ + // + protected _context?: typeof UMB_SERVER_MODEL_VALIDATION_CONTEXT.TYPE; + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_SERVER_MODEL_VALIDATION_CONTEXT, (context) => { + this._context = context; + context.addTranslator(this); + }); + } + + override hostDisconnected(): void { + this._context?.removeTranslator(this); + this._context = undefined; + super.hostDisconnected(); + } + + abstract translate(path: string): ReturnType; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts deleted file mode 100644 index de829b4aca..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-message-translator.controller.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; -import type { UmbValidationMessageTranslator } from './validation-message-translator.interface.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from '../index.js'; - -export class UmbVariantValuesValidationMessageTranslator - extends UmbControllerBase - implements UmbValidationMessageTranslator -{ - // - #context?: typeof UMB_SERVER_MODEL_VALIDATION_CONTEXT.TYPE; - - constructor(host: UmbControllerHost) { - super(host); - - this.consumeContext(UMB_SERVER_MODEL_VALIDATION_CONTEXT, (context) => { - this.#context = context; - context.addTranslator(this); - }); - } - - override hostDisconnected(): void { - this.#context?.removeTranslator(this); - this.#context = undefined; - super.hostDisconnected(); - } - - translate(path: string) { - if (!this.#context) return; - if (path.indexOf('$.values[') !== 0) { - // We do not handle this path. - return false; - } - const pathEnd = path.indexOf(']'); - if (pathEnd === -1) { - // We do not handle this path. - return false; - } - // retrieve the number from the message values index: [NL] - const index = parseInt(path.substring(9, pathEnd)); - - if (isNaN(index)) { - // index is not a number, this means its not a path we want to translate. [NL] - return false; - } - - // Get the data from the validation request, the context holds that for us: [NL] - const data = this.#context.getData(); - - const specificValue = data.values[index]; - // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: - return '$.values[' + UmbDataPathPropertyValueFilter(specificValue) + path.substring(path.indexOf(']')); - } - - //hostDisconnected is called when a controller is begin destroyed, so this destroy method is not needed. - /* - override destroy(): void { - super.destroy(); - this.#context.removeTranslator(this); - }*/ -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts new file mode 100644 index 0000000000..af284b6024 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts @@ -0,0 +1,31 @@ +import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; +import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; + +export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathTranslatorBase { + translate(path: string) { + if (!this._context) return; + if (path.indexOf('$.values[') !== 0) { + // We do not handle this path. + return false; + } + const pathEnd = path.indexOf(']'); + if (pathEnd === -1) { + // We do not handle this path. + return false; + } + // retrieve the number from the message values index: [NL] + const index = parseInt(path.substring(9, pathEnd)); + + if (isNaN(index)) { + // index is not a number, this means its not a path we want to translate. [NL] + return false; + } + + // Get the data from the validation request, the context holds that for us: [NL] + const data = this._context.getData(); + + const specificValue = data.values[index]; + // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: + return '$.values[' + UmbDataPathPropertyValueFilter(specificValue) + path.substring(path.indexOf(']')); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index d1b754b1f2..35f237a2b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -54,7 +54,7 @@ import { import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { UmbServerModelValidationContext, - UmbVariantValuesValidationMessageTranslator, + UmbVariantValuesValidationPathTranslator, } from '@umbraco-cms/backoffice/validation'; import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; @@ -162,7 +162,7 @@ export class UmbDocumentWorkspaceContext constructor(host: UmbControllerHost) { super(host, UMB_DOCUMENT_WORKSPACE_ALIAS); - new UmbVariantValuesValidationMessageTranslator(this, this.#serverValidation); + new UmbVariantValuesValidationPathTranslator(this); this.observe(this.contentTypeUnique, (unique) => this.structure.loadType(unique)); this.observe(this.varies, (varies) => (this.#varies = varies)); From 8492eba85a16e0bcc71220927fc55caadb322649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 20:53:46 +0200 Subject: [PATCH 072/152] correct weight order --- .../src/packages/core/validation/mixins/form-control.mixin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index a94eca0182..75b2e32777 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -27,15 +27,15 @@ type FlagTypes = const WeightedErrorFlagTypes = [ 'customError', + 'valueMissing', 'badInput', + 'typeMismatch', 'patternMismatch', 'rangeOverflow', 'rangeUnderflow', 'stepMismatch', 'tooLong', 'tooShort', - 'typeMismatch', - 'valueMissing', ]; // Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public interface in a separate file. From 3999e601d8db9d2f91bc5a1232ec659f9f18f9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 20:54:10 +0200 Subject: [PATCH 073/152] destroy flow --- .../packages/core/validation/context/validation.context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 36d63f3df2..e4f3df8838 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -132,9 +132,9 @@ export class UmbValidationContext extends UmbContextBase i this.#parent.removeValidator(this); } this.#parent = undefined; - this.messages.destroy(); - (this.messages as any) = undefined; this.#destroyValidators(); + this.messages?.destroy(); + (this.messages as any) = undefined; super.destroy(); } } From c9fddd7077307b6de36d384416bc1dccac31845e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 21:08:47 +0200 Subject: [PATCH 074/152] sort validators correct --- .../core/validation/mixins/form-control.mixin.ts | 9 ++++----- .../text-box/property-editor-ui-text-box.element.ts | 6 +++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index 75b2e32777..29fc1a662e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -43,6 +43,7 @@ export interface UmbFormControlValidatorConfig { flagKey: FlagTypes; getMessageMethod: () => string; checkMethod: () => boolean; + weight: number; } export interface UmbFormControlMixinInterface extends HTMLElement { @@ -232,13 +233,11 @@ export function UmbFormControlMixin< flagKey: flagKey, getMessageMethod: getMessageMethod, checkMethod: checkMethod, + weight: WeightedErrorFlagTypes.indexOf(flagKey), } satisfies UmbFormControlValidatorConfig; this.#validators.push(validator); // Sort validators based on the WeightedErrorFlagTypes order. [NL] - this.#validators.sort((a, b) => { - // This could easily be extended with a weight set on the validator object itself. [NL] - return WeightedErrorFlagTypes.indexOf(a.flagKey) - WeightedErrorFlagTypes.indexOf(b.flagKey); - }); + this.#validators.sort((a, b) => (a.weight > b.weight ? 1 : b.weight > a.weight ? -1 : 0)); return validator; } @@ -323,7 +322,7 @@ export function UmbFormControlMixin< return false; }); - if (message) { + if (!message) { // Loop through inner native form controls to adapt their validityState. [NL] this.#formCtrlElements.some((formCtrlEl) => { let key: keyof ValidityState; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 0edcbdc497..59a8dc3fb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -57,6 +57,7 @@ export class UmbPropertyEditorUITextBoxElement constructor() { super(); + // Test validators: this.addValidator( 'tooShort', () => 'custom too short msg', @@ -65,7 +66,10 @@ export class UmbPropertyEditorUITextBoxElement this.addValidator( 'valueMissing', () => 'custom valueMissing msg', - () => this.value === undefined && this.value === '', + () => { + console.log('valueMissing', this.value); + return this.value === undefined || this.value === ''; + }, ); } From 32524300d9ede50e0593da36654242c89ad604d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 21:24:08 +0200 Subject: [PATCH 075/152] clear messages --- .../core/validation/context/validation-messages.manager.ts | 2 +- .../src/packages/core/validation/context/validation.context.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index a04c28b8ef..174a8b1a90 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -129,7 +129,7 @@ export class UmbValidationMessagesManager { } } - reset(): void { + clear(): void { this.#messages.setValue([]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index e4f3df8838..37cdd986eb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -33,6 +33,8 @@ export class UmbValidationContext extends UmbContextBase i this.#parent = parent; parent.addValidator(this); + this.messages.clear(); + // observe parent messages that fits with the path?. // — Transfer message, without the base path? From ca307ba990ca31a150a3b50b2be31c2496604647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 21:36:00 +0200 Subject: [PATCH 076/152] messagesOfPathAndDescendant --- .../core/validation/context/validation-messages.manager.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 174a8b1a90..a7f296df21 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -35,7 +35,6 @@ export class UmbValidationMessagesManager { return this.#messages.getValue().length !== 0; } - /* messagesOfPathAndDescendant(path: string): Observable> { // Find messages that starts with the given path, if the path is longer then require a dot or [ as the next character. using a more performant way than Regex: return this.#messages.asObservablePart((msgs) => @@ -46,7 +45,6 @@ export class UmbValidationMessagesManager { ), ); } - */ messagesOfTypeAndPath(type: UmbValidationMessageType, path: string): Observable> { // Find messages that matches the given type and path. @@ -83,6 +81,10 @@ export class UmbValidationMessagesManager { this.#messages.append(messages.map((message) => ({ type, key: UmbId.new(), path, message }))); } + /*appendMessages(messages: Array): void { + this.#messages.append(messages); + }*/ + removeMessageByKey(key: string): void { this.#messages.removeOne(key); } From c6490222c6945f5b291199d62816f8052b10a0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 21:36:12 +0200 Subject: [PATCH 077/152] observe parent messages --- .../core/validation/context/validation.context.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 37cdd986eb..4446a71711 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -35,6 +35,17 @@ export class UmbValidationContext extends UmbContextBase i this.messages.clear(); + // TODO: Observe for messages that gets removed, so they can be removed from parent as well? + + this.observe( + parent.messages.messagesOfPathAndDescendant(dataPath), + (msgs) => { + //this.messages.appendMessages(msgs); + // Set all these messages to our own messages, and observe them specifically? + }, + 'observeParentMessages', + ); + // observe parent messages that fits with the path?. // — Transfer message, without the base path? From f94c9b2b5c7fdba77924e6525af2fa94dcaf48f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Aug 2024 21:47:29 +0200 Subject: [PATCH 078/152] remove test validators --- .../property-editor-ui-text-box.element.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 59a8dc3fb3..4cac623483 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -55,24 +55,6 @@ export class UmbPropertyEditorUITextBoxElement this._placeholder = config?.getValueByAlias('placeholder'); } - constructor() { - super(); - // Test validators: - this.addValidator( - 'tooShort', - () => 'custom too short msg', - () => String(this.value).length < 10, - ); - this.addValidator( - 'valueMissing', - () => 'custom valueMissing msg', - () => { - console.log('valueMissing', this.value); - return this.value === undefined || this.value === ''; - }, - ); - } - protected override firstUpdated(): void { this.addFormControlElement(this.shadowRoot!.querySelector('uui-input')!); } From 9a6bb5b5e7165361818210046adfe8eccb597619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 10 Aug 2024 01:14:12 +0200 Subject: [PATCH 079/152] updated thoughts --- .../validation/context/validation.context.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 4446a71711..275a57b804 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -35,34 +35,34 @@ export class UmbValidationContext extends UmbContextBase i this.messages.clear(); - // TODO: Observe for messages that gets removed, so they can be removed from parent as well? + // TODO: Observe for local messages that gets removed, so they can be removed from parent as well? + // TODO: Observe for parent messages that gets removed, so they can be removed from local as well? this.observe( parent.messages.messagesOfPathAndDescendant(dataPath), (msgs) => { //this.messages.appendMessages(msgs); - // Set all these messages to our own messages, and observe them specifically? + msgs.forEach((msg) => { + // TODO: Subtract the base path from the path, so it becomes local to this context: + this.messages.addMessage(msg.type, msg.path, msg.message); + }); + + // observe if one of the parent got removed? }, 'observeParentMessages', ); - // observe parent messages that fits with the path?. - // — Transfer message, without the base path? + // observe if one of the locals got removed. + // It can maybe be done with one set of known/gotten parent messages, that then can be used to detect which are removed. Maybe from both sides. - // observe our messages and make sure to propagate them? or observe our messages, if we have one, set one message to the parent with out base path. Or parse all?. - // Maybe it has to actively be add or remove... - - // Question is do we want sub messages to be added to the parent? or do we want to keep them separate?. - // How about removing sub messages, a sub message could come from the server/parent. so who decides to remove it? I would say it still should be the property editor. - // So maybe only remove if they exist upward, but not append upward? Maybe only append upward one for the basePath, and then if we have one message when getting spun up, we can run validation and if validation is good we remove the base-path from the parent. - - // Whats the value of having them local? - // It makes it more complex, - // It makes it harder to debug - // But it makes it easier to remove translators as they get taken out. But maybe this is handled by the controller pattern? - // The amount of translator instances will still be the same, as we then end up with multiple validation contexts and therefor just more things, not necessary less. - - // We may still do so, but only for Workspaces. + // Benefits for workspaces: + // — The workspace can be validated locally, and then the messages can propagate to parent context. (Do we even want that?) + // — The workspace can easier know about its validation state + // — Its the only way the sub-workspace can be validated without triggering the whole validation. + // - The workspace can inherit messages from parent context... — which is good for Blocks + // - The workspace can have its own server messages, that is propagated to parent context. + // - The workspace can inherit server messages from parent context... — server validation of a block, that is part of the parent workspace. + // - Remove parent messages if they go away again if they gets remove here. }).skipHost(); } From 7d9d0158ecf2715cb8c5d652b815ddfff78f1b53 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 12 Aug 2024 09:38:39 +0200 Subject: [PATCH 080/152] correct path to object type module --- src/Umbraco.Web.UI.Client/package-lock.json | 28 ++++++++++++++------- src/Umbraco.Web.UI.Client/package.json | 2 +- src/Umbraco.Web.UI.Client/tsconfig.json | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 859c860d9b..6e7f33e9bc 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -7513,14 +7513,14 @@ "resolved": "src/packages/language", "link": true }, - "node_modules/@umbraco-backoffice/markdown": { - "resolved": "src/packages/markdown-editor", - "link": true - }, "node_modules/@umbraco-backoffice/log-viewer": { "resolved": "src/packages/log-viewer", "link": true }, + "node_modules/@umbraco-backoffice/markdown": { + "resolved": "src/packages/markdown-editor", + "link": true + }, "node_modules/@umbraco-backoffice/media": { "resolved": "src/packages/media", "link": true @@ -22784,8 +22784,12 @@ "src/packages/language": { "name": "@umbraco-backoffice/language" }, - "src/packages/markdown-editor": {}, - "src/packages/log-viewer": {}, + "src/packages/log-viewer": { + "name": "@umbraco-backoffice/log-viewer" + }, + "src/packages/markdown-editor": { + "name": "@umbraco-backoffice/markdown" + }, "src/packages/media": { "name": "@umbraco-backoffice/media" }, @@ -22810,18 +22814,24 @@ "src/packages/search": { "name": "@umbraco-backoffice/search" }, - "src/packages/static-file": {}, + "src/packages/static-file": { + "name": "@umbraco-backoffice/static-file" + }, "src/packages/tags": { "name": "@umbraco-backoffice/tag" }, - "src/packages/telemetry": {}, + "src/packages/telemetry": { + "name": "@umbraco-backoffice/telemetry" + }, "src/packages/templating": { "name": "@umbraco-backoffice/templating" }, "src/packages/tiny-mce": { "name": "@umbraco-backoffice/tiny-mce" }, - "src/packages/ufm": {}, + "src/packages/ufm": { + "name": "@umbraco-backoffice/ufm" + }, "src/packages/umbraco-news": { "name": "@umbraco-backoffice/umbraco-news" }, diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 3b8ba3fa43..f419f03da6 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -58,7 +58,7 @@ "./models": "./dist-cms/packages/core/models/index.js", "./multi-url-picker": "./dist-cms/packages/multi-url-picker/index.js", "./notification": "./dist-cms/packages/core/notification/index.js", - "./object-type": "./dist-cms/core/object-type/index.js", + "./object-type": "./dist-cms/packages/core/object-type/index.js", "./package": "./dist-cms/packages/packages/package/index.js", "./partial-view": "./dist-cms/packages/templating/partial-views/index.js", "./picker-input": "./dist-cms/packages/core/picker-input/index.js", diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index f92de6ee2d..715ff5c48d 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -84,7 +84,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "@umbraco-cms/backoffice/models": ["./src/packages/core/models/index.ts"], "@umbraco-cms/backoffice/multi-url-picker": ["./src/packages/multi-url-picker/index.ts"], "@umbraco-cms/backoffice/notification": ["./src/packages/core/notification/index.ts"], - "@umbraco-cms/backoffice/object-type": ["./src/core/object-type/index.ts"], + "@umbraco-cms/backoffice/object-type": ["./src/packages/core/object-type/index.ts"], "@umbraco-cms/backoffice/package": ["./src/packages/packages/package/index.ts"], "@umbraco-cms/backoffice/partial-view": ["./src/packages/templating/partial-views/index.ts"], "@umbraco-cms/backoffice/picker-input": ["./src/packages/core/picker-input/index.ts"], From 0234400510b8b05c35f4f417cc5949ccfd301162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 10:14:51 +0200 Subject: [PATCH 081/152] two way binding --- .../context/validation-messages.manager.ts | 5 +- .../validation/context/validation.context.ts | 51 +++++++++++++++++-- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index a7f296df21..73844ef3c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -13,6 +13,7 @@ export interface UmbValidationMessage { export class UmbValidationMessagesManager { #messages = new UmbArrayState([], (x) => x.key); + messages = this.#messages.asObservable(); constructor() { this.#messages.asObservable().subscribe((x) => console.log('all messages:', x)); @@ -71,9 +72,9 @@ export class UmbValidationMessagesManager { ); } - addMessage(type: UmbValidationMessageType, path: string, message: string): void { + addMessage(type: UmbValidationMessageType, path: string, message: string, key: string = UmbId.new()): void { path = this.#translatePath(path) ?? path; - this.#messages.appendOne({ type, key: UmbId.new(), path, message }); + this.#messages.appendOne({ type, key, path, message }); } addMessages(type: UmbValidationMessageType, path: string, messages: Array): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 275a57b804..bf1f3caac0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -1,15 +1,24 @@ import type { UmbValidator } from '../interfaces/validator.interface.js'; -import { UmbValidationMessagesManager } from './validation-messages.manager.js'; +import { UmbValidationMessage, UmbValidationMessagesManager } from './validation-messages.manager.js'; import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +function ReplaceStartOfString(path: string, startFrom: string, startTo: string): string { + if (path.startsWith(startFrom + '.')) { + return startTo + path.slice(startFrom.length); + } + return path; +} + export class UmbValidationContext extends UmbContextBase implements UmbValidator { #validators: Array = []; #validationMode: boolean = false; #isValid: boolean = false; #parent?: UmbValidationContext; + #parentMessages?: Array; + #localMessages?: Array; #baseDataPath?: string; public readonly messages = new UmbValidationMessagesManager(); @@ -42,16 +51,50 @@ export class UmbValidationContext extends UmbContextBase i parent.messages.messagesOfPathAndDescendant(dataPath), (msgs) => { //this.messages.appendMessages(msgs); + if (this.#parentMessages) { + // Remove the local messages that does not exist in the parent anymore: + const toRemove = this.#parentMessages.filter((msg) => !msgs.find((m) => m.key === msg.key)); + toRemove.forEach((msg) => { + this.messages.removeMessageByKey(msg.key); + }); + } + this.#parentMessages = msgs; msgs.forEach((msg) => { // TODO: Subtract the base path from the path, so it becomes local to this context: - this.messages.addMessage(msg.type, msg.path, msg.message); + const path = ReplaceStartOfString(msg.path, '$', this.#baseDataPath!); + //console.log('up path', path); + // Notice, the local message uses the same key. [NL] + this.messages.addMessage(msg.type, path, msg.message, msg.key); }); - - // observe if one of the parent got removed? }, 'observeParentMessages', ); + this.observe( + this.messages.messages, + (msgs) => { + if (!this.#parent) return; + //this.messages.appendMessages(msgs); + if (this.#localMessages) { + // Remove the parent messages that does not exist locally anymore: + const toRemove = this.#localMessages.filter((msg) => !msgs.find((m) => m.key === msg.key)); + toRemove.forEach((msg) => { + this.#parent!.messages.removeMessageByKey(msg.key); + }); + } + this.#localMessages = msgs; + msgs.forEach((msg) => { + // TODO: Prefix the base path from our base path, so it fits in the parent context: + // replace this.#baseDataPath (if it starts with it) with $ in the path, so it becomes relative to the parent context + const path = ReplaceStartOfString(msg.path, this.#baseDataPath!, '$'); + console.log('down path', path); + // Notice, the parent message uses the same key. [NL] + this.#parent!.messages.addMessage(msg.type, path, msg.message, msg.key); + }); + }, + 'observeLocalMessages', + ); + // observe if one of the locals got removed. // It can maybe be done with one set of known/gotten parent messages, that then can be used to detect which are removed. Maybe from both sides. From 5637b8ac74f51d4e8fd9f28d43676a6503e015cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 10:15:34 +0200 Subject: [PATCH 082/152] remove todos --- .../src/packages/core/validation/context/validation.context.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index bf1f3caac0..6c5a033b33 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -44,9 +44,6 @@ export class UmbValidationContext extends UmbContextBase i this.messages.clear(); - // TODO: Observe for local messages that gets removed, so they can be removed from parent as well? - // TODO: Observe for parent messages that gets removed, so they can be removed from local as well? - this.observe( parent.messages.messagesOfPathAndDescendant(dataPath), (msgs) => { From d16be4e8377eb68b35428d13d09ba5fffc03154a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 10:27:06 +0200 Subject: [PATCH 083/152] data path state in property context --- .../core/property/property/property.context.ts | 11 +++++++++++ .../core/property/property/property.element.ts | 12 ++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index 0c121c7299..c217bacb0f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -43,9 +43,13 @@ export class UmbPropertyContext extends UmbContextBase(undefined); public readonly validation = this.#validation.asObservable(); + public readonly validationMandatory = this.#validation.asObservablePart((x) => x?.mandatory); public readonly validationMandatoryMessage = this.#validation.asObservablePart((x) => x?.mandatoryMessage); + #dataPath = new UmbStringState(undefined); + public readonly dataPath = this.#dataPath.asObservable(); + #editor = new UmbBasicState(undefined); public readonly editor = this.#editor.asObservable(); @@ -206,6 +210,13 @@ export class UmbPropertyContext extends UmbContextBase { this._invalid = invalid; }); } public get dataPath(): string | undefined { - return this.#dataPath; + return this.#propertyContext.getDataPath(); } - #dataPath?: string; @state() private _variantDifference?: string; @@ -319,13 +318,14 @@ export class UmbPropertyElement extends UmbLitElement { ); if ('checkValidity' in this._element) { - this.#controlValidator = new UmbFormControlValidator(this, this._element as any, this.#dataPath); + const dataPath = this.dataPath; + this.#controlValidator = new UmbFormControlValidator(this, this._element as any, dataPath); // We trust blindly that the dataPath is available at this stage. [NL] - if (this.#dataPath) { + if (dataPath) { this.#validationMessageBinder = new UmbBindServerValidationToFormControl( this, this._element as any, - this.#dataPath, + dataPath, ); this.#validationMessageBinder.value = this.#propertyContext.getValue(); } From a1f97ef884ebb895a21bf5c3865d6cd50f98cec5 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 12 Aug 2024 10:50:30 +0200 Subject: [PATCH 084/152] register manifest bundle --- .../src/packages/publish-cache/umbraco-package.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts index b7e6f07906..cdb1d3ecdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/publish-cache/umbraco-package.ts @@ -1,2 +1,9 @@ export const name = 'Umbraco.Core.PublishCache'; -export const extensions = []; +export const extensions = [ + { + name: 'Publish Cache Bundle', + alias: 'Umb.Bundle.PublishCache', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; From ab9fda40904e616d05a883d2dd6ab3f3bf3c4b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 11:38:11 +0200 Subject: [PATCH 085/152] parse data path to workspace --- .../block-grid/context/block-grid-entries.context.ts | 2 +- .../block-list/context/block-list-entries.context.ts | 2 +- .../workspace/block-list-workspace.modal-token.ts | 2 +- .../block-rte/context/block-rte-entries.context.ts | 2 +- .../block/block/context/block-entries.context.ts | 6 ++++++ .../src/packages/block/block/index.ts | 1 + .../utils/data-path-content-data-filter.function.ts | 12 ++++++++++++ .../block/workspace/block-workspace.modal-token.ts | 3 ++- .../core/modal/token/workspace-modal.token.ts | 1 + .../contexts/submittable-workspace-context-base.ts | 10 +++++++++- 10 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts index f14c1594ac..0d24f68d0e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts @@ -131,7 +131,7 @@ export class UmbBlockGridEntriesContext data: { entityType: 'block', preset: {}, - originData: { areaKey: this.#areaKey, parentUnique: this.#parentUnique }, + originData: { areaKey: this.#areaKey, parentUnique: this.#parentUnique, baseDataPath: this._dataPath }, }, modal: { size: 'medium' }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts index 8f74c76255..b2108eb02f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts @@ -46,7 +46,7 @@ export class UmbBlockListEntriesContext extends UmbBlockEntriesContext< .addUniquePaths(['propertyAlias', 'variantId']) .addAdditionalPath('block') .onSetup(() => { - return { data: { entityType: 'block', preset: {} }, modal: { size: 'medium' } }; + return { data: { entityType: 'block', preset: {}, baseDataPath: this._dataPath }, modal: { size: 'medium' } }; }) .observeRouteBuilder((routeBuilder) => { const newPath = routeBuilder({}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/workspace/block-list-workspace.modal-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/workspace/block-list-workspace.modal-token.ts index e2334ca5a0..89888c14d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/workspace/block-list-workspace.modal-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/workspace/block-list-workspace.modal-token.ts @@ -15,7 +15,7 @@ export const UMB_BLOCK_LIST_WORKSPACE_MODAL = new UmbModalToken, UmbWorkspaceModalValue>; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts index 3e4f13eb02..4571835198 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts @@ -47,7 +47,7 @@ export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext< .addUniquePaths(['propertyAlias', 'variantId']) .addAdditionalPath('block') .onSetup(() => { - return { data: { entityType: 'block', preset: {} }, modal: { size: 'medium' } }; + return { data: { entityType: 'block', preset: {}, baseDataPath: this._dataPath }, modal: { size: 'medium' } }; }) .observeRouteBuilder((routeBuilder) => { const newPath = routeBuilder({}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts index 4f8b41066f..038d7c2709 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts @@ -27,6 +27,8 @@ export abstract class UmbBlockEntriesContext< protected _workspacePath = new UmbStringState(undefined); workspacePath = this._workspacePath.asObservable(); + protected _dataPath?: string; + public abstract readonly canCreate: Observable; protected _layoutEntries = new UmbArrayState([], (x) => x.contentUdi); @@ -48,6 +50,10 @@ export abstract class UmbBlockEntriesContext< return this._manager!; } + setDataPath(path: string) { + this._dataPath = path; + } + protected abstract _gotBlockManager(): void; // Public methods: diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts index 828e8f118c..70eae81ddd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts @@ -1,4 +1,5 @@ export * from './context/index.js'; export * from './modals/index.js'; +export * from './utils/data-path-content-data-filter.function.js'; export * from './types.js'; export * from './workspace/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts new file mode 100644 index 0000000000..adc9b07110 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts @@ -0,0 +1,12 @@ +/** + * Validation Data Path filter for Block Content Data. + * write a JSON-Path filter similar to `?(@.udi = 'my-udi://1234')` + * @param udi {string} - The udi of the block content data. + * @returns + */ +export function UmbDataPathBlockContentDataFilter(udi: string): string { + // write a array of strings for each property, where alias must be present and culture and segment are optional + //const filters: Array = [`@.udi = '${udi}'`]; + //return `?(${filters.join(' && ')})`; + return `?(@.udi = '${udi}')`; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.modal-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.modal-token.ts index aaebad4028..701f468ca8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.modal-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.modal-token.ts @@ -3,6 +3,7 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; export interface UmbBlockWorkspaceData extends UmbWorkspaceModalData { originData: OriginDataType; + baseDataPath: string; } export const UMB_BLOCK_WORKSPACE_MODAL = new UmbModalToken( @@ -12,7 +13,7 @@ export const UMB_BLOCK_WORKSPACE_MODAL = new UmbModalToken, UmbWorkspaceModalValue>; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts index f5b7edf84c..c9b2f63681 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts @@ -2,6 +2,7 @@ import { UmbModalToken } from './modal-token.js'; export interface UmbWorkspaceModalData { entityType: string; preset: Partial; + baseDataPath?: string; } // TODO: It would be good with a WorkspaceValueBaseType, to avoid the hardcoded type for unique here: diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts index d0adc3d42e..cfb3691f86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts @@ -61,6 +61,14 @@ export abstract class UmbSubmittableWorkspaceContextBase this.#isNew.setValue(isNew); } + /** + * If a Workspace has multiple validation contexts, then this method can be overwritten to return the correct one. + * @returns Promise that resolves to void when the validation is complete. + */ + async validate(): Promise { + return this.validation.validate(); + } + async requestSubmit(): Promise { return this.validateAndSubmit( () => this.submit(), @@ -76,7 +84,7 @@ export abstract class UmbSubmittableWorkspaceContextBase this.#submitResolve = resolve; this.#submitReject = reject; }); - this.validation.validate().then( + this.validate().then( async () => { onValid().then(this.#completeSubmit, this.#rejectSubmit); }, From de42d6a7d3bff099d0831889d929f7f3a756bf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 11:41:15 +0200 Subject: [PATCH 086/152] concept of multiple validation contexts --- .../contexts/submittable-workspace-context-base.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts index cfb3691f86..f5f6c91460 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts @@ -18,7 +18,8 @@ export abstract class UmbSubmittableWorkspaceContextBase // TODO: We could make a base type for workspace modal data, and use this here: As well as a base for the result, to make sure we always include the unique (instead of the object type) public readonly modalContext?: UmbModalContext<{ preset: object }>; - public readonly validation = new UmbValidationContext(this); + //public readonly validation = new UmbValidationContext(this); + #validationContexts: Array = []; #submitPromise: Promise | undefined; #submitResolve: (() => void) | undefined; @@ -49,7 +50,8 @@ export abstract class UmbSubmittableWorkspaceContextBase } protected resetState() { - this.validation.reset(); + //this.validation.reset(); + this.#validationContexts.forEach((context) => context.reset()); this.#isNew.setValue(undefined); } @@ -65,8 +67,9 @@ export abstract class UmbSubmittableWorkspaceContextBase * If a Workspace has multiple validation contexts, then this method can be overwritten to return the correct one. * @returns Promise that resolves to void when the validation is complete. */ - async validate(): Promise { - return this.validation.validate(); + async validate(): Promise> { + //return this.validation.validate(); + return Promise.all(this.#validationContexts.map((context) => context.validate())); } async requestSubmit(): Promise { @@ -123,7 +126,8 @@ export abstract class UmbSubmittableWorkspaceContextBase this.#resolveSubmit(); // Calling reset on the validation context here. [NL] - this.validation.reset(); + // TODO: Consider if we really ant this, cause on save, we do not want to reset this... [NL] (Also adapt to concept of multiple validation contexts) + //this.validation.reset(); }; //abstract getIsDirty(): Promise; From 1dcf4b08b933285dc70363b2ab32de9a5621ce05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 11:45:21 +0200 Subject: [PATCH 087/152] baseDataPath of modal tokens --- .../workspace/block-grid-workspace.modal-token.ts | 7 ++++++- .../block-rte/workspace/block-rte-workspace.modal-token.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-workspace.modal-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-workspace.modal-token.ts index fba0224034..4e29db4375 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-workspace.modal-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-workspace.modal-token.ts @@ -17,7 +17,12 @@ export const UMB_BLOCK_GRID_WORKSPACE_MODAL = new UmbModalToken, UmbWorkspaceModalValue>; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/workspace/block-rte-workspace.modal-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/workspace/block-rte-workspace.modal-token.ts index 84ca328671..89dbaa9bd7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/workspace/block-rte-workspace.modal-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/workspace/block-rte-workspace.modal-token.ts @@ -11,7 +11,7 @@ export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken, UmbWorkspaceModalValue>; From f5ff0bc4efce35e3f75eb1c35362b5677ac4893b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 13:01:24 +0200 Subject: [PATCH 088/152] addValidationContext --- .../workspace/contexts/submittable-workspace-context-base.ts | 5 ++++- .../data-type/workspace/data-type-workspace.context.ts | 3 +++ .../documents/workspace/document-workspace.context.ts | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts index f5f6c91460..1960c6b0cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts @@ -7,7 +7,7 @@ import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; +import type { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; export abstract class UmbSubmittableWorkspaceContextBase extends UmbContextBase> @@ -20,6 +20,9 @@ export abstract class UmbSubmittableWorkspaceContextBase //public readonly validation = new UmbValidationContext(this); #validationContexts: Array = []; + addValidationContext(context: UmbValidationContext) { + this.#validationContexts.push(context); + } #submitPromise: Promise | undefined; #submitResolve: (() => void) | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts index 49aa80140f..78ca3771c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts @@ -28,6 +28,7 @@ import { UmbRequestReloadChildrenOfEntityEvent, UmbRequestReloadStructureForEntityEvent, } from '@umbraco-cms/backoffice/entity-action'; +import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; type EntityType = UmbDataTypeDetailModel; @@ -99,6 +100,8 @@ export class UmbDataTypeWorkspaceContext constructor(host: UmbControllerHost) { super(host, 'Umb.Workspace.DataType'); + this.addValidationContext(new UmbValidationContext(this)); + this.#observePropertyEditorSchemaAlias(); this.#observePropertyEditorUIAlias(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 35f237a2b0..1cac5fadf9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -54,6 +54,7 @@ import { import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { UmbServerModelValidationContext, + UmbValidationContext, UmbVariantValuesValidationPathTranslator, } from '@umbraco-cms/backoffice/validation'; import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint'; @@ -162,6 +163,8 @@ export class UmbDocumentWorkspaceContext constructor(host: UmbControllerHost) { super(host, UMB_DOCUMENT_WORKSPACE_ALIAS); + this.addValidationContext(new UmbValidationContext(this)); + new UmbVariantValuesValidationPathTranslator(this); this.observe(this.contentTypeUnique, (unique) => this.structure.loadType(unique)); From e5e4877b97918063bb60bef7897e8f61802901d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 14:57:30 +0200 Subject: [PATCH 089/152] Validation context for Block Workspace --- .../.vscode/settings.json | 1 + .../block/workspace/block-element-manager.ts | 12 +++++++++- .../workspace/block-workspace.context.ts | 3 +++ .../edit/block-workspace-view-edit.element.ts | 4 ++-- .../context/validation-messages.manager.ts | 9 +++++--- .../validation/context/validation.context.ts | 23 +++++++++++++++---- .../submittable-workspace-context-base.ts | 5 ++++ .../workspace/data-type-workspace.context.ts | 2 +- .../workspace/document-workspace.context.ts | 2 +- 9 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/.vscode/settings.json b/src/Umbraco.Web.UI.Client/.vscode/settings.json index 1f9ce773f9..f4ca096129 100644 --- a/src/Umbraco.Web.UI.Client/.vscode/settings.json +++ b/src/Umbraco.Web.UI.Client/.vscode/settings.json @@ -22,6 +22,7 @@ "umbraco", "Uncategorized", "uninitialize", + "unprovide", "variantable" ], "exportall.config.folderListener": [], diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts index 54bcd7aeff..f089f7cd87 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts @@ -4,8 +4,9 @@ import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { type UmbClassInterface, UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UmbDocumentTypeDetailRepository } from '@umbraco-cms/backoffice/document-type'; +import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; export class UmbBlockElementManager extends UmbControllerBase { // @@ -24,6 +25,8 @@ export class UmbBlockElementManager extends UmbControllerBase { new UmbDocumentTypeDetailRepository(this), ); + readonly validation = new UmbValidationContext(this); + constructor(host: UmbControllerHost) { // TODO: Get Workspace Alias via Manifest. super(host); @@ -99,6 +102,13 @@ export class UmbBlockElementManager extends UmbControllerBase { return new UmbBlockElementPropertyDatasetContext(host, this); } + public setup(host: UmbClassInterface) { + this.createPropertyDatasetContext(host); + + // Provide Validation Context for this view: + this.validation.provideAt(host); + } + public override destroy(): void { this.#data.destroy(); this.structure.destroy(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index 0464b2fe88..e2fda43a9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -60,6 +60,9 @@ export class UmbBlockWorkspaceContext { this.#modalContext = context; context.onSubmit().catch(this.#modalRejected); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts index 1677f4ba5b..acc21154e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts @@ -69,8 +69,8 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U const dataManager = this.#blockWorkspace[this.#managerName]; this.#tabsStructureHelper.setStructureManager(dataManager.structure); - // Create Data Set: - dataManager.createPropertyDatasetContext(this); + // Create Data Set & setup Validation Context: + dataManager.setup(this); this.observe( this.#blockWorkspace![this.#managerName!].structure.hasRootContainers('Group'), diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 73844ef3c4..e991c10492 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -82,9 +82,12 @@ export class UmbValidationMessagesManager { this.#messages.append(messages.map((message) => ({ type, key: UmbId.new(), path, message }))); } - /*appendMessages(messages: Array): void { - this.#messages.append(messages); - }*/ + /* + appendMessage(msg: UmbValidationMessage): void { + const path = this.#translatePath(msg.path) ?? msg.path; + this.#messages.appendOne({ ...msg, path }); + } + */ removeMessageByKey(key: string): void { this.#messages.removeOne(key); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 6c5a033b33..f74b25ae27 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -1,8 +1,8 @@ +import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbValidationMessage, UmbValidationMessagesManager } from './validation-messages.manager.js'; import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; -import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { type UmbClassInterface, UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; function ReplaceStartOfString(path: string, startFrom: string, startTo: string): string { if (path.startsWith(startFrom + '.')) { @@ -11,7 +11,10 @@ function ReplaceStartOfString(path: string, startFrom: string, startTo: string): return path; } -export class UmbValidationContext extends UmbContextBase implements UmbValidator { +export class UmbValidationContext extends UmbControllerBase implements UmbValidator { + // The current provider controller, that is providing this context: + #providerCtrl?: UmbContextProviderController; + #validators: Array = []; #validationMode: boolean = false; #isValid: boolean = false; @@ -23,8 +26,18 @@ export class UmbValidationContext extends UmbContextBase i public readonly messages = new UmbValidationMessagesManager(); - constructor(host: UmbControllerHost) { - super(host, UMB_VALIDATION_CONTEXT); + /** + * Provides the validation context to the current host, if not already provided to a different host. + * @returns instance {UmbValidationContext} - Returns it self. + */ + provide(): UmbValidationContext { + if (this.#providerCtrl) return this; + this.provideContext(UMB_VALIDATION_CONTEXT, this); + return this; + } + provideAt(controllerHost: UmbClassInterface): void { + this.#providerCtrl?.destroy(); + this.#providerCtrl = controllerHost.provideContext(UMB_VALIDATION_CONTEXT, this); } setDataPath(dataPath: string): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts index 1960c6b0cd..f2429a9518 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/submittable-workspace-context-base.ts @@ -20,6 +20,11 @@ export abstract class UmbSubmittableWorkspaceContextBase //public readonly validation = new UmbValidationContext(this); #validationContexts: Array = []; + + /** + * Appends a validation context to the workspace. + * @param context + */ addValidationContext(context: UmbValidationContext) { this.#validationContexts.push(context); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts index 78ca3771c5..46bb5ec6a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts @@ -100,7 +100,7 @@ export class UmbDataTypeWorkspaceContext constructor(host: UmbControllerHost) { super(host, 'Umb.Workspace.DataType'); - this.addValidationContext(new UmbValidationContext(this)); + this.addValidationContext(new UmbValidationContext(this).provide()); this.#observePropertyEditorSchemaAlias(); this.#observePropertyEditorUIAlias(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 1cac5fadf9..4a4058acc3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -163,7 +163,7 @@ export class UmbDocumentWorkspaceContext constructor(host: UmbControllerHost) { super(host, UMB_DOCUMENT_WORKSPACE_ALIAS); - this.addValidationContext(new UmbValidationContext(this)); + this.addValidationContext(new UmbValidationContext(this).provide()); new UmbVariantValuesValidationPathTranslator(this); From 2141d4c7bb70ce36a9c344ac3f5b22688eef273c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 12 Aug 2024 19:18:01 +0200 Subject: [PATCH 090/152] observe selection from selection manager --- .../member-picker-modal.element.ts | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index 5ed953118a..cb03e1b029 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -37,6 +37,18 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< this.#selectionManager.setSelection(this.value?.selection ?? []); } + constructor() { + super(); + this.observe( + this.#selectionManager.selection, + (selection) => { + this.updateValue({ selection }); + this.requestUpdate(); + }, + 'umbSelectionObserver', + ); + } + override async firstUpdated() { const { data } = await this.#collectionRepository.requestCollection({}); this._members = data?.items ?? []; @@ -72,15 +84,6 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< this._searching = false; } - #submit() { - this.value = { selection: this.#selectionManager.getSelection() }; - this.modalContext?.submit(); - } - - #close() { - this.modalContext?.reject(); - } - #onSearchClear() { this._searchQuery = ''; this._searchResult = []; @@ -91,12 +94,14 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< ${this.#renderSearch()} ${this.#renderItems()}
- + this.modalContext?.reject()}> + @click=${() => this.modalContext?.submit()}>
`; } From 6e68f49d5a0387c15e1433af3ef3a04a005fc649 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 12 Aug 2024 19:23:03 +0200 Subject: [PATCH 091/152] prevent search with empty string --- .../member-picker-modal/member-picker-modal.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index cb03e1b029..8977625f1c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -79,6 +79,7 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #debouncedSearch = debounce(this.#search, 300); async #search() { + if (!this._searchQuery) return; const { data } = await this.#searchProvider.search({ query: this._searchQuery }); this._searchResult = data?.items ?? []; this._searching = false; From b68a65d29a83a2f28599e9d1a81c8635d9d1934f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 12 Aug 2024 19:27:25 +0200 Subject: [PATCH 092/152] add empty search result text --- .../member-picker-modal/member-picker-modal.element.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index 8977625f1c..bcd3db3584 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -139,6 +139,10 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< } #renderSearchResult() { + if (this._searchQuery && this._searching === false && this._searchResult.length === 0) { + return this.#renderEmptySearchResult(); + } + return html` ${repeat( this._searchResult, @@ -148,6 +152,10 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< `; } + #renderEmptySearchResult() { + return html`No result for "${this._searchQuery}".`; + } + #renderMemberItem(item: UmbMemberItemModel | UmbMemberDetailModel) { return html` Date: Mon, 12 Aug 2024 18:42:30 +0100 Subject: [PATCH 093/152] Reinstated (and deprecated) the `submitLabel` property on public interfaces --- .../modal/token/property-editor-ui-picker-modal.token.ts | 5 ++++- .../data-type-picker-flow-modal.token.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts index bf682ced50..185f5ff781 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts @@ -1,6 +1,9 @@ import { UmbModalToken } from './modal-token.js'; -export interface UmbPropertyEditorUIPickerModalData {} +export interface UmbPropertyEditorUIPickerModalData { + /** @deprecated This property will be removed in Umbraco 15. */ + submitLabel?: string; +} export type UmbPropertyEditorUIPickerModalValue = { selection: Array; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts index ebad5be051..55a1643d90 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts @@ -1,6 +1,9 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export interface UmbDataTypePickerFlowModalData {} +export interface UmbDataTypePickerFlowModalData { + /** @deprecated This property will be removed in Umbraco 15. */ + submitLabel?: string; +} export type UmbDataTypePickerFlowModalValue = { selection: Array; From f1a347c4e7362d4616be706c00697595b723b479 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 12 Aug 2024 18:54:25 +0100 Subject: [PATCH 094/152] Resolved @sonarcloud intentionality issues --- .../data-type-picker-flow-modal.element.ts | 6 ++---- .../property-editor-ui-picker-modal.element.ts | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index 94255693c5..cf2af5dd45 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -198,7 +198,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< .filter((dataType) => dataType.name?.toLowerCase().includes(this.#currentFilterQuery)) .sort((a, b) => a.name.localeCompare(b.name)); - // TODO: groupBy is not known by TS yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error const grouped = Object.groupBy(filteredDataTypes, (dataType: UmbDataTypeItemModel) => @@ -206,7 +205,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< ); this._groupedDataTypes = Object.keys(grouped) - .sort() + .sort((a, b) => a.localeCompare(b)) .map((key) => ({ key, items: grouped[key] })); } else { this._groupedDataTypes = []; @@ -220,7 +219,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< propertyEditorUI.alias.toLowerCase().includes(this.#currentFilterQuery), ); - // TODO: groupBy is not known by TS yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error const grouped = Object.groupBy(filteredUIs, (propertyEditorUi: ManifestPropertyEditorUi) => @@ -228,7 +226,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< ); this._groupedPropertyEditorUIs = Object.keys(grouped) - .sort() + .sort((a, b) => a.localeCompare(b)) .map((key) => ({ key, items: grouped[key] })); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts index 047800054c..3fb5bd97c2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts @@ -57,7 +57,6 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< } #groupPropertyEditorUIs(items: Array) { - // TODO: groupBy is not known by TS yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error const grouped = Object.groupBy(items, (propertyEditorUi: ManifestPropertyEditorUi) => @@ -65,7 +64,7 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< ); this._groupedPropertyEditorUIs = Object.keys(grouped) - .sort() + .sort((a, b) => a.localeCompare(b)) .map((key) => ({ key, items: grouped[key] })); } From b586e6006648aaf72a0c6d776b1cc92ff0db8d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 12 Aug 2024 21:46:40 +0200 Subject: [PATCH 095/152] temp block data translator --- .../property-editor-ui-block-list.element.ts | 30 ++++++++++++- .../src/packages/block/block/index.ts | 2 +- ...a-validation-path-translator.controller.ts | 45 +++++++++++++++++++ ...data-path-element-data-filter.function.ts} | 10 +++-- .../packages/block/block/validation/index.ts | 2 + .../validation/context/validation.context.ts | 5 +++ 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts rename src/Umbraco.Web.UI.Client/src/packages/block/block/{utils/data-path-content-data-filter.function.ts => validation/data-path-element-data-filter.function.ts} (50%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index 2a96c2e0c0..6c3e347419 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -15,9 +15,11 @@ import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models'; import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import type { UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/block'; +import { UmbVariantValuesValidationPathTranslator, type UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/block'; import '../../components/block-list-entry/index.js'; +import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; +import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => { @@ -44,6 +46,8 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement }, }); + #contentDataPathTranslator?: UmbVariantValuesValidationPathTranslator; + //#catalogueModal: UmbModalRouteRegistrationController; private _value: UmbBlockListValueModel = { @@ -117,10 +121,34 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement #managerContext = new UmbBlockListManagerContext(this); #entriesContext = new UmbBlockListEntriesContext(this); + #validationContext = new UmbValidationContext(this); constructor() { super(); + this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => { + this.observe( + context.dataPath, + (dataPath) => { + // + // TODO: Make translator for settings. + + this.#contentDataPathTranslator?.destroy(); + if (dataPath) { + // Set the data path for the local validation context: + this.#validationContext.setDataPath(dataPath); + + this.#contentDataPathTranslator = new UmbVariantValuesValidationPathTranslator( + this, + dataPath, + 'contentData', + ); + } + }, + 'observeDataPath', + ); + }); + this.observe(this.#entriesContext.layoutEntries, (layouts) => { this._layouts = layouts; // Update sorter. diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts index 70eae81ddd..0e7cb3f0dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/index.ts @@ -1,5 +1,5 @@ export * from './context/index.js'; export * from './modals/index.js'; -export * from './utils/data-path-content-data-filter.function.js'; export * from './types.js'; +export * from './validation/index.js'; export * from './workspace/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts new file mode 100644 index 0000000000..f507fb8f55 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts @@ -0,0 +1,45 @@ +import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbValidationPathTranslatorBase } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathBlockElementDataFilter } from './data-path-element-data-filter.function.js'; + +export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathTranslatorBase { + #pathStart: string; + + constructor(host: UmbControllerHost, baseDataPath: string, propertyName: 'contentData' | 'settingsData') { + super(host); + this.#pathStart = baseDataPath + '.' + propertyName + '['; + console.log('UmbVariantValuesValidationPathTranslator', this.#pathStart); + } + + translate(path: string) { + if (!this._context) return; + console.log('translate', path); + if (path.indexOf(this.#pathStart) !== 0) { + // We do not handle this path. + return false; + } + const startLength = this.#pathStart.length; + console.log('translate got a match on step one'); + const pathEnd = path.indexOf(']', startLength); + if (pathEnd === -1) { + // We do not handle this path. + return false; + } + // retrieve the number from the message values index: [NL] + const index = parseInt(path.substring(startLength, pathEnd)); + + console.log('translate index', path.substring(startLength, pathEnd), index); + if (isNaN(index)) { + // index is not a number, this means its not a path we want to translate. [NL] + return false; + } + + // Get the data from the validation request, the context holds that for us: [NL] + const data = this._context.getData(); + console.log('go to this point', data); + + const specificValue = data.contentData[index]; + // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: + return this.#pathStart + UmbDataPathBlockElementDataFilter(specificValue) + path.substring(path.indexOf(']')); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts similarity index 50% rename from src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts index adc9b07110..be01a28c10 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/utils/data-path-content-data-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts @@ -1,12 +1,14 @@ +import type { UmbBlockDataType } from '../types.js'; + /** - * Validation Data Path filter for Block Content Data. + * Validation Data Path filter for Block Element Data. * write a JSON-Path filter similar to `?(@.udi = 'my-udi://1234')` - * @param udi {string} - The udi of the block content data. + * @param udi {string} - The udi of the block Element data. * @returns */ -export function UmbDataPathBlockContentDataFilter(udi: string): string { +export function UmbDataPathBlockElementDataFilter(data: UmbBlockDataType): string { // write a array of strings for each property, where alias must be present and culture and segment are optional //const filters: Array = [`@.udi = '${udi}'`]; //return `?(${filters.join(' && ')})`; - return `?(@.udi = '${udi}')`; + return `?(@.udi = '${data.udi}')`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts new file mode 100644 index 0000000000..34a563d9d5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts @@ -0,0 +1,2 @@ +export * from './block-data-validation-path-translator.controller.js'; +export * from './data-path-element-data-filter.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index f74b25ae27..fcc03148d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -3,6 +3,7 @@ import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbValidationMessage, UmbValidationMessagesManager } from './validation-messages.manager.js'; import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; import { type UmbClassInterface, UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; function ReplaceStartOfString(path: string, startFrom: string, startTo: string): string { if (path.startsWith(startFrom + '.')) { @@ -26,6 +27,10 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida public readonly messages = new UmbValidationMessagesManager(); + constructor(host: UmbControllerHost) { + super(host); + } + /** * Provides the validation context to the current host, if not already provided to a different host. * @returns instance {UmbValidationContext} - Returns it self. From 02d498c476ab12894d68cc8ad9b990695a3e5441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 00:04:47 +0200 Subject: [PATCH 096/152] Json Path querying --- .../dataset-dashboard.ts | 2 +- .../sorter-group.ts | 2 +- .../property-editor-ui-block-list.element.ts | 15 ++- ...y-validation-path-translator.controller.ts | 28 ++++++ ...a-validation-path-translator.controller.ts | 19 ++-- .../block/workspace/block-element-manager.ts | 5 +- .../workspace/block-workspace.context.ts | 4 +- ...-workspace-view-edit-properties.element.ts | 19 +++- .../validation/context/validation.context.ts | 41 ++++++-- .../server-model-validation.context.ts | 23 ++--- ...idation-path-translator-base.controller.ts | 7 +- ...s-validation-path-translator.controller.ts | 2 +- .../packages/core/validation/utils/index.ts | 1 + .../validation/utils/json-path.function.ts | 94 +++++++++++++++++++ .../core/validation/utils/json-path.test.ts | 37 ++++++++ 15 files changed, 246 insertions(+), 53 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.test.ts diff --git a/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/dataset-dashboard.ts b/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/dataset-dashboard.ts index 6f483bd387..20cc50236d 100644 --- a/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/dataset-dashboard.ts +++ b/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/dataset-dashboard.ts @@ -1,7 +1,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; -import { UmbPropertyValueData, type UmbPropertyDatasetElement } from '@umbraco-cms/backoffice/property'; +import { type UmbPropertyValueData, type UmbPropertyDatasetElement } from '@umbraco-cms/backoffice/property'; @customElement('example-dataset-dashboard') export class ExampleDatasetDashboard extends UmbElementMixin(LitElement) { diff --git a/src/Umbraco.Web.UI.Client/examples/sorter-with-nested-containers/sorter-group.ts b/src/Umbraco.Web.UI.Client/examples/sorter-with-nested-containers/sorter-group.ts index a26b40c1c2..38a4c681f9 100644 --- a/src/Umbraco.Web.UI.Client/examples/sorter-with-nested-containers/sorter-group.ts +++ b/src/Umbraco.Web.UI.Client/examples/sorter-with-nested-containers/sorter-group.ts @@ -1,7 +1,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, LitElement, repeat, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; -import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import './sorter-item.js'; import ExampleSorterItem from './sorter-item.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index 6c3e347419..def96de8f3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -15,7 +15,10 @@ import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models'; import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UmbVariantValuesValidationPathTranslator, type UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/block'; +import { + UmbBlockElementDataValidationPathTranslator, + type UmbBlockLayoutBaseModel, +} from '@umbraco-cms/backoffice/block'; import '../../components/block-list-entry/index.js'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; @@ -46,7 +49,8 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement }, }); - #contentDataPathTranslator?: UmbVariantValuesValidationPathTranslator; + #validationContext = new UmbValidationContext(this).provide(); + #contentDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; //#catalogueModal: UmbModalRouteRegistrationController; @@ -121,7 +125,6 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement #managerContext = new UmbBlockListManagerContext(this); #entriesContext = new UmbBlockListEntriesContext(this); - #validationContext = new UmbValidationContext(this); constructor() { super(); @@ -138,11 +141,7 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement // Set the data path for the local validation context: this.#validationContext.setDataPath(dataPath); - this.#contentDataPathTranslator = new UmbVariantValuesValidationPathTranslator( - this, - dataPath, - 'contentData', - ); + this.#contentDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'contentData'); } }, 'observeDataPath', diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts new file mode 100644 index 0000000000..572de655f9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts @@ -0,0 +1,28 @@ +import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { + GetPropertyNameFromPath, + UmbDataPathPropertyValueFilter, + UmbValidationPathTranslatorBase, +} from '@umbraco-cms/backoffice/validation'; + +export class UmbBlockElementDataValidationPathTranslator extends UmbValidationPathTranslatorBase { + constructor(host: UmbControllerHost) { + super(host); + } + + translate(path: string) { + if (!this._context) return; + if (path.indexOf('$.') !== 0) { + // We do not handle this path. + return false; + } + + const rest = path.substring(2); + const key = GetPropertyNameFromPath(rest); + + const specificValue = { alias: key }; + // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: + //return '$.values' + UmbVariantValuesValidationPathTranslator(specificValue) + path.substring(path.indexOf(']')); + return '$.values[' + UmbDataPathPropertyValueFilter(specificValue) + '.value'; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts index f507fb8f55..bb34cd1c01 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts @@ -1,25 +1,24 @@ -import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbValidationPathTranslatorBase } from '@umbraco-cms/backoffice/validation'; import { UmbDataPathBlockElementDataFilter } from './data-path-element-data-filter.function.js'; -export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathTranslatorBase { +export class UmbBlockElementDataValidationPathTranslator extends UmbValidationPathTranslatorBase { + #propertyName: string; #pathStart: string; - constructor(host: UmbControllerHost, baseDataPath: string, propertyName: 'contentData' | 'settingsData') { + constructor(host: UmbControllerHost, propertyName: 'contentData' | 'settingsData') { super(host); - this.#pathStart = baseDataPath + '.' + propertyName + '['; - console.log('UmbVariantValuesValidationPathTranslator', this.#pathStart); + this.#propertyName = propertyName; + this.#pathStart = '$.' + propertyName + '['; } translate(path: string) { if (!this._context) return; - console.log('translate', path); if (path.indexOf(this.#pathStart) !== 0) { // We do not handle this path. return false; } const startLength = this.#pathStart.length; - console.log('translate got a match on step one'); const pathEnd = path.indexOf(']', startLength); if (pathEnd === -1) { // We do not handle this path. @@ -28,17 +27,15 @@ export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathT // retrieve the number from the message values index: [NL] const index = parseInt(path.substring(startLength, pathEnd)); - console.log('translate index', path.substring(startLength, pathEnd), index); if (isNaN(index)) { // index is not a number, this means its not a path we want to translate. [NL] return false; } // Get the data from the validation request, the context holds that for us: [NL] - const data = this._context.getData(); - console.log('go to this point', data); + const data = this._context.getTranslationData(); - const specificValue = data.contentData[index]; + const specificValue = data[this.#propertyName][index]; // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: return this.#pathStart + UmbDataPathBlockElementDataFilter(specificValue) + path.substring(path.indexOf(']')); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts index f089f7cd87..373561a74e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts @@ -27,11 +27,14 @@ export class UmbBlockElementManager extends UmbControllerBase { readonly validation = new UmbValidationContext(this); - constructor(host: UmbControllerHost) { + constructor(host: UmbControllerHost, dataPathPropertyName: string) { // TODO: Get Workspace Alias via Manifest. super(host); this.observe(this.contentTypeId, (id) => this.structure.loadType(id)); + this.observe(this.unique, (udi) => + this.validation.setDataPath('$.' + dataPathPropertyName + `[?(@.udi = '${udi}')]`), + ); } reset() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index e2fda43a9c..d3671ecc2a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -47,9 +47,9 @@ export class UmbBlockWorkspaceContext x?.contentUdi); readonly contentUdi = this.#layout.asObservablePart((x) => x?.contentUdi); - readonly content = new UmbBlockElementManager(this); + readonly content = new UmbBlockElementManager(this, 'contentData'); - readonly settings = new UmbBlockElementManager(this); + readonly settings = new UmbBlockElementManager(this, 'settingsData'); // TODO: Get the name of the contentElementType.. #label = new UmbStringState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-properties.element.ts index 5487628534..295ee4073e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-properties.element.ts @@ -32,6 +32,9 @@ export class UmbBlockWorkspaceViewEditPropertiesElement extends UmbLitElement { @state() _propertyStructure: Array = []; + @state() + _dataPaths?: Array; + constructor() { super(); @@ -48,26 +51,36 @@ export class UmbBlockWorkspaceViewEditPropertiesElement extends UmbLitElement { this.#propertyStructureHelper.propertyStructure, (propertyStructure) => { this._propertyStructure = propertyStructure; + this.#generatePropertyDataPath(); }, 'observePropertyStructure', ); } + #generatePropertyDataPath() { + if (!this._propertyStructure) return; + this._dataPaths = this._propertyStructure.map((property) => `$.${property.alias}`); + } + override render() { return repeat( this._propertyStructure, (property) => property.alias, - (property) => html` `, + (property, index) => + html` `, ); } static override styles = [ UmbTextStyles, css` - umb-property-type-based-property { + .property { border-bottom: 1px solid var(--uui-color-divider); } - umb-property-type-based-property:last-child { + .property:last-child { border-bottom: 0; } `, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index fcc03148d5..408eb0d163 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -1,9 +1,12 @@ import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; import type { UmbValidator } from '../interfaces/validator.interface.js'; -import { UmbValidationMessage, UmbValidationMessagesManager } from './validation-messages.manager.js'; +import { type UmbValidationMessage, UmbValidationMessagesManager } from './validation-messages.manager.js'; import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; import { type UmbClassInterface, UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbValidationMessageTranslator } from '../translators/index.js'; +import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { GetValueByJsonPath } from '../utils/json-path.function.js'; function ReplaceStartOfString(path: string, startFrom: string, startTo: string): string { if (path.startsWith(startFrom + '.')) { @@ -16,6 +19,19 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida // The current provider controller, that is providing this context: #providerCtrl?: UmbContextProviderController; + // Local version of the data send to the server, only use-case is for translation. + #translationData = new UmbObjectState(undefined); + translationDataOf(path: string): any { + console.log('GetValueByJsonPath', path); + return this.#translationData.asObservablePart((data) => GetValueByJsonPath(data, path)); + } + setTranslationData(data: any): void { + this.#translationData.setValue(data); + } + getTranslationData(): any { + return this.#translationData.getValue(); + } + #validators: Array = []; #validationMode: boolean = false; #isValid: boolean = false; @@ -28,9 +44,18 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida public readonly messages = new UmbValidationMessagesManager(); constructor(host: UmbControllerHost) { + // This is overridden to avoid setting a controllerAlias, this might make sense, but currently i want to leave it out. [NL] super(host); } + async addTranslator(translator: UmbValidationMessageTranslator) { + this.messages.addTranslator(translator); + } + + async removeTranslator(translator: UmbValidationMessageTranslator) { + this.messages.removeTranslator(translator); + } + /** * Provides the validation context to the current host, if not already provided to a different host. * @returns instance {UmbValidationContext} - Returns it self. @@ -62,6 +87,12 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida this.messages.clear(); + console.log('observe path', dataPath); + this.observe(parent.translationDataOf(dataPath), (data) => { + console.log('got data', data); + this.setTranslationData(data); + }); + this.observe( parent.messages.messagesOfPathAndDescendant(dataPath), (msgs) => { @@ -76,8 +107,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida this.#parentMessages = msgs; msgs.forEach((msg) => { // TODO: Subtract the base path from the path, so it becomes local to this context: - const path = ReplaceStartOfString(msg.path, '$', this.#baseDataPath!); - //console.log('up path', path); + const path = ReplaceStartOfString(msg.path, this.#baseDataPath!, '$'); // Notice, the local message uses the same key. [NL] this.messages.addMessage(msg.type, path, msg.message, msg.key); }); @@ -101,8 +131,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida msgs.forEach((msg) => { // TODO: Prefix the base path from our base path, so it fits in the parent context: // replace this.#baseDataPath (if it starts with it) with $ in the path, so it becomes relative to the parent context - const path = ReplaceStartOfString(msg.path, this.#baseDataPath!, '$'); - console.log('down path', path); + const path = ReplaceStartOfString(msg.path, '$', this.#baseDataPath!); // Notice, the parent message uses the same key. [NL] this.#parent!.messages.addMessage(msg.type, path, msg.message, msg.key); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts index fc54223d05..4d92ff53ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts @@ -1,4 +1,3 @@ -import type { UmbValidationMessageTranslator } from '../translators/validation-message-path-translator.interface.js'; import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; @@ -23,8 +22,6 @@ export class UmbServerModelValidationContext extends UmbContextBase implements UmbValidator { - #init: Promise; - #validatePromise?: Promise; #validatePromiseResolve?: () => void; @@ -38,7 +35,7 @@ export class UmbServerModelValidationContext constructor(host: UmbControllerHost) { super(host, UMB_SERVER_MODEL_VALIDATION_CONTEXT); - this.#init = this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { + this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { if (this.#context) { this.#context.removeValidator(this); } @@ -64,11 +61,15 @@ export class UmbServerModelValidationContext const { error } = await requestPromise; this.#isValid = error ? false : true; - - if (!this.#isValid) { + if (this.#isValid) { + // Send data to context for translation: + this.#context?.setTranslationData(undefined); + } else { if (!this.#context) { throw new Error('No context available for translation.'); } + // Send data to context for translation: + this.#context.setTranslationData(data); // We are missing some typing here, but we will just go wild with 'as any': [NL] const errorBody = (error as any).body as ValidateErrorResponseBodyModel; @@ -102,16 +103,6 @@ export class UmbServerModelValidationContext this.#validatePromiseResolve = undefined; } - async addTranslator(translator: UmbValidationMessageTranslator) { - await this.#init; - this.#context!.messages.addTranslator(translator); - } - - async removeTranslator(translator: UmbValidationMessageTranslator) { - await this.#init; - this.#context!.messages.removeTranslator(translator); - } - get isValid(): boolean { return this.#isValid; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts index e541cfe777..61cf4d2911 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts @@ -1,19 +1,20 @@ import type { UmbValidationMessageTranslator } from './validation-message-path-translator.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from '../index.js'; +import { UMB_VALIDATION_CONTEXT } from '../index.js'; export abstract class UmbValidationPathTranslatorBase extends UmbControllerBase implements UmbValidationMessageTranslator { // - protected _context?: typeof UMB_SERVER_MODEL_VALIDATION_CONTEXT.TYPE; + protected _context?: typeof UMB_VALIDATION_CONTEXT.TYPE; constructor(host: UmbControllerHost) { super(host); - this.consumeContext(UMB_SERVER_MODEL_VALIDATION_CONTEXT, (context) => { + this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { + this._context?.removeTranslator(this); this._context = context; context.addTranslator(this); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts index af284b6024..cdbc8b992a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts @@ -22,7 +22,7 @@ export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathT } // Get the data from the validation request, the context holds that for us: [NL] - const data = this._context.getData(); + const data = this._context.getTranslationData(); const specificValue = data.values[index]; // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts index 1fb8cf117c..f67191b0ce 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts @@ -1 +1,2 @@ export * from './data-path-property-value-filter.function.js'; +export * from './json-path.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts new file mode 100644 index 0000000000..e24bf87508 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts @@ -0,0 +1,94 @@ +export function GetValueByJsonPath(data: any, path: string): any { + // strip $ from the path: + const strippedPath = path.startsWith('$.') ? path.slice(2) : path; + // get value from the path: + return GetNextPropertyValueFromPath(data, strippedPath); +} + +export function GetPropertyNameFromPath(path: string): string { + // find next '.' or '[' in the path, using regex: + const match = path.match(/\.|\[/); + // If no match is found, we assume its a single key so lets return the value of the key: + if (match === null || match.index === undefined) return path; + + // split the path at the first match: + return path.slice(0, match.index); +} + +function GetNextPropertyValueFromPath(data: any, path: string): any { + if (!data) return undefined; + // find next '.' or '[' in the path, using regex: + const match = path.match(/\.|\[/); + // If no match is found, we assume its a single key so lets return the value of the key: + if (match === null || match.index === undefined) return data[path]; + + // split the path at the first match: + const key = path.slice(0, match.index); + const rest = path.slice(match.index + 1); + + if (!key) return undefined; + // get the value of the key from the data: + const value = data[key]; + // if there is no rest of the path, return the value: + if (rest === undefined) return value; + // if the value is an array, get the value at the index: + if (Array.isArray(value)) { + // get the value until the next ']', the value can be anything in between the brackets: + const lookupEnd = rest.match(/\]/); + if (!lookupEnd) return undefined; + // get everything before the match: + const entryPointer = rest.slice(0, lookupEnd.index); + + // check if the entryPointer is a JSON Path Filter ( starting with ?( and ending with ) ): + if (entryPointer.startsWith('?(') && entryPointer.endsWith(')')) { + // get the filter from the entryPointer: + console.log('query', entryPointer); + // get the filter as a function: + const jsFilter = JsFilterFromJsonPathFilter(entryPointer); + // find the index of the value that matches the filter: + const index = value.findIndex(jsFilter[0]); + // if the index is -1, return undefined: + if (index === -1) return undefined; + // get the value at the index: + const data = value[index]; + // Check for safety: + if (lookupEnd.index === undefined || lookupEnd.index + 1 >= rest.length) { + return data; + } + // continue with the rest of the path: + return GetNextPropertyValueFromPath(data, rest.slice(lookupEnd.index + 2)) ?? data; + } else { + // get the value at the index: + const indexAsNumber = parseInt(entryPointer); + if (isNaN(indexAsNumber)) return undefined; + const data = value[indexAsNumber]; + // Check for safety: + if (lookupEnd.index === undefined || lookupEnd.index + 1 >= rest.length) { + return data; + } + // continue with the rest of the path: + return GetNextPropertyValueFromPath(data, rest.slice(lookupEnd.index + 2)) ?? data; + } + } else { + // continue with the rest of the path: + return GetNextPropertyValueFromPath(value, rest); + } +} + +function JsFilterFromJsonPathFilter(filter: string): any { + // strip ?( and ) from the filter + const jsFilter = filter.slice(2, -1); + // split the filter into parts by splitting at ' && ' + const parts = jsFilter.split(' && '); + // map each part to a function that returns true if the part is true + return parts.map((part) => { + // split the part into key and value + const [path, equal] = part.split(' = '); + // remove @. + const key = path.slice(2); + // remove quotes: + const value = equal.slice(1, -1); + // return a function that returns true if the key is equal to the value + return (item: any) => item[key] === value; + }); +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.test.ts new file mode 100644 index 0000000000..3673b28ecd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.test.ts @@ -0,0 +1,37 @@ +import { expect } from '@open-wc/testing'; +import { GetValueByJsonPath } from './json-path.function.js'; + +describe('UmbJsonPathFunctions', () => { + it('retrieve property value', () => { + const result = GetValueByJsonPath({ value: 'test' }, '$.value'); + + expect(result).to.eq('test'); + }); + + it('value of first entry in an array', () => { + const result = GetValueByJsonPath({ values: ['test'] }, '$.values[0]'); + + expect(result).to.eq('test'); + }); + + it('value property of first entry in an array', () => { + const result = GetValueByJsonPath({ values: [{ value: 'test' }] }, '$.values[0].value'); + + expect(result).to.eq('test'); + }); + + it('value property of first entry in an array', () => { + const result = GetValueByJsonPath( + { values: [{ value: { deepData: [{ value: 'inner' }] } }] }, + '$.values[0].value.deepData[0].value', + ); + + expect(result).to.eq('inner'); + }); + + it('query of first entry in an array', () => { + const result = GetValueByJsonPath({ values: [{ id: '123', value: 'test' }] }, "$.values[?(@.id = '123')].value"); + + expect(result).to.eq('test'); + }); +}); From 4c926bcd2461af8c101e0fd8e8f3cbd643472226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 09:49:09 +0200 Subject: [PATCH 097/152] udi data path correction --- .../block-data-validation-path-translator.controller.ts | 4 ++++ .../block/block/workspace/block-element-manager.ts | 8 +++++--- .../core/validation/context/validation.context.ts | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts index bb34cd1c01..d5c10401ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts @@ -36,6 +36,10 @@ export class UmbBlockElementDataValidationPathTranslator extends UmbValidationPa const data = this._context.getTranslationData(); const specificValue = data[this.#propertyName][index]; + if (!specificValue || !specificValue.udi) { + console.log('block did not have UDI', this.#propertyName, index, data); + return false; + } // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: return this.#pathStart + UmbDataPathBlockElementDataFilter(specificValue) + path.substring(path.indexOf(']')); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts index 373561a74e..67ef88b64d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts @@ -32,9 +32,11 @@ export class UmbBlockElementManager extends UmbControllerBase { super(host); this.observe(this.contentTypeId, (id) => this.structure.loadType(id)); - this.observe(this.unique, (udi) => - this.validation.setDataPath('$.' + dataPathPropertyName + `[?(@.udi = '${udi}')]`), - ); + this.observe(this.unique, (udi) => { + if (udi) { + this.validation.setDataPath('$.' + dataPathPropertyName + `[?(@.udi = '${udi}')]`); + } + }); } reset() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 408eb0d163..d42b829d4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -22,7 +22,6 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida // Local version of the data send to the server, only use-case is for translation. #translationData = new UmbObjectState(undefined); translationDataOf(path: string): any { - console.log('GetValueByJsonPath', path); return this.#translationData.asObservablePart((data) => GetValueByJsonPath(data, path)); } setTranslationData(data: any): void { @@ -72,10 +71,13 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida setDataPath(dataPath: string): void { if (this.#baseDataPath) { + if (this.#baseDataPath === dataPath) return; + console.log(this.#baseDataPath, dataPath); // Just fire an error, as I haven't made the right clean up jet. Or haven't thought about what should happen if it changes while already setup. // cause maybe all the messages should be removed as we are not interested in the old once any more. But then on the other side, some might be relevant as this is the same entity that changed its paths? throw new Error('Data path is already set, we do not support changing the context data-path as of now.'); } + if (!dataPath) return; this.#baseDataPath = dataPath; this.consumeContext(UMB_VALIDATION_CONTEXT, (parent) => { @@ -87,9 +89,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida this.messages.clear(); - console.log('observe path', dataPath); this.observe(parent.translationDataOf(dataPath), (data) => { - console.log('got data', data); this.setTranslationData(data); }); From 30037e3da0381de1e2912f7c3f32df7762d91495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 10:13:41 +0200 Subject: [PATCH 098/152] avoid adding existing messages --- .../context/validation-messages.manager.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index e991c10492..ac3cb96345 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -74,12 +74,22 @@ export class UmbValidationMessagesManager { addMessage(type: UmbValidationMessageType, path: string, message: string, key: string = UmbId.new()): void { path = this.#translatePath(path) ?? path; + // check if there is an existing message with the same path and type, and append the new messages: [NL] + if (this.#messages.getValue().find((x) => x.type === type && x.path === path && x.message === message)) { + return; + } this.#messages.appendOne({ type, key, path, message }); } addMessages(type: UmbValidationMessageType, path: string, messages: Array): void { path = this.#translatePath(path) ?? path; - this.#messages.append(messages.map((message) => ({ type, key: UmbId.new(), path, message }))); + // filter out existing messages with the same path and type, and append the new messages: [NL] + const existingMessages = this.#messages.getValue(); + const newMessages = messages.filter( + (message) => + existingMessages.find((x) => x.type === type && x.path === path && x.message === message) === undefined, + ); + this.#messages.append(newMessages.map((message) => ({ type, key: UmbId.new(), path, message }))); } /* From 47fc18c1e97a9f6861b11c5d0736c09d886a757d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 10:13:48 +0200 Subject: [PATCH 099/152] Readme --- .../src/packages/core/validation/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md new file mode 100644 index 0000000000..54c462bdc0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -0,0 +1,3 @@ +# Backoffice Validation System + +The validation system works around a system of messages, a message is based on a type, path and message. From d763f804c851f921165350f9d510d73472eb091f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 10:17:23 +0200 Subject: [PATCH 100/152] rename message prop to body --- .../context/validation-messages.manager.ts | 17 ++++++++--------- .../validation/context/validation.context.ts | 4 ++-- ...ver-validation-to-form-control.controller.ts | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index ac3cb96345..7b3f1fe625 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -8,7 +8,7 @@ export interface UmbValidationMessage { type: UmbValidationMessageType; key: string; path: string; - message: string; + body: string; } export class UmbValidationMessagesManager { @@ -72,24 +72,23 @@ export class UmbValidationMessagesManager { ); } - addMessage(type: UmbValidationMessageType, path: string, message: string, key: string = UmbId.new()): void { + addMessage(type: UmbValidationMessageType, path: string, body: string, key: string = UmbId.new()): void { path = this.#translatePath(path) ?? path; // check if there is an existing message with the same path and type, and append the new messages: [NL] - if (this.#messages.getValue().find((x) => x.type === type && x.path === path && x.message === message)) { + if (this.#messages.getValue().find((x) => x.type === type && x.path === path && x.body === body)) { return; } - this.#messages.appendOne({ type, key, path, message }); + this.#messages.appendOne({ type, key, path, body: body }); } - addMessages(type: UmbValidationMessageType, path: string, messages: Array): void { + addMessages(type: UmbValidationMessageType, path: string, bodies: Array): void { path = this.#translatePath(path) ?? path; // filter out existing messages with the same path and type, and append the new messages: [NL] const existingMessages = this.#messages.getValue(); - const newMessages = messages.filter( - (message) => - existingMessages.find((x) => x.type === type && x.path === path && x.message === message) === undefined, + const newBodies = bodies.filter( + (message) => existingMessages.find((x) => x.type === type && x.path === path && x.body === message) === undefined, ); - this.#messages.append(newMessages.map((message) => ({ type, key: UmbId.new(), path, message }))); + this.#messages.append(newBodies.map((body) => ({ type, key: UmbId.new(), path, body }))); } /* diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index d42b829d4f..a5b5c87796 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -109,7 +109,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida // TODO: Subtract the base path from the path, so it becomes local to this context: const path = ReplaceStartOfString(msg.path, this.#baseDataPath!, '$'); // Notice, the local message uses the same key. [NL] - this.messages.addMessage(msg.type, path, msg.message, msg.key); + this.messages.addMessage(msg.type, path, msg.body, msg.key); }); }, 'observeParentMessages', @@ -133,7 +133,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida // replace this.#baseDataPath (if it starts with it) with $ in the path, so it becomes relative to the parent context const path = ReplaceStartOfString(msg.path, '$', this.#baseDataPath!); // Notice, the parent message uses the same key. [NL] - this.#parent!.messages.addMessage(msg.type, path, msg.message, msg.key); + this.#parent!.messages.addMessage(msg.type, path, msg.body, msg.key); }); }, 'observeLocalMessages', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts index 5d1b81293e..7300c874ba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts @@ -66,7 +66,7 @@ export class UmbBindServerValidationToFormControl extends UmbControllerBase { if (!this.#controlValidator) { this.#controlValidator = this.#control.addValidator( 'customError', - () => this.#messages.map((x) => x.message).join(', '), + () => this.#messages.map((x) => x.body).join(', '), () => !this.#isValid, ); //this.#control.addEventListener('change', this.#onControlChange); From e135fda6b2e4083984e84f36accc20e32f18e2cb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 13 Aug 2024 11:16:08 +0200 Subject: [PATCH 101/152] reexport blocktype type --- src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts index dccd64dad7..4bb332af04 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts @@ -1,4 +1,5 @@ import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; +export type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export interface UmbBlockTypeGroup { name?: string; From b9cc0d73d32f86d077d7e273a1ea2e0613720939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 11:40:51 +0200 Subject: [PATCH 102/152] single equal sign --- .../utils/data-path-property-value-filter.function.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts index 408cf91b06..52953837cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts @@ -14,10 +14,10 @@ export function UmbDataPathPropertyValueFilter( // write a array of strings for each property, where alias must be present and culture and segment are optional const filters: Array = [`@.alias = '${value.alias}'`]; if (value.culture) { - filters.push(`@.culture == '${value.culture}'`); + filters.push(`@.culture = '${value.culture}'`); } if (value.segment) { - filters.push(`@.segment == '${value.segment}'`); + filters.push(`@.segment = '${value.segment}'`); } return `?(${filters.join(' && ')})`; } From 75a83e17b81b0d13db2340bb98e0bcbdd4c36385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 11:40:57 +0200 Subject: [PATCH 103/152] readme --- .../src/packages/core/validation/README.md | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index 54c462bdc0..56115e45ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -1,3 +1,64 @@ # Backoffice Validation System -The validation system works around a system of messages, a message is based on a type, path and message. +The validation system works around a system of Validation Messages, provided via Validation Contexts and connected to the application via Validators. + +The system both supports handling front-end validation, server-validation and other things can as well be hooked into it. + +## Validation Context + +The core of the system is a Validation Context, this holds the messages and more. + +### Validation Messages + +A Validation message consist of a type, path and body. This typically looks like this: + +``` +{ + type: "client", + path: "$.values[?(@.alias = 'my-property-alias')].value", + message: "Must contain at least 3 words" +} +``` + +Because each validation issue is presented in the Validation Context as a Message, its existence will be available for anyone to observe. +One benefit of this is that Elements that are removed from screen can still have their validation messages preventing the submission of a dataset. +As well Tabs and other navigation can use this to be highlighted, so the user can be guide to the location. + +#### Path + +The Path, points to the location of the model that the message is concerning. + +The following models headline is too long, which can be target with this path: + +Data: +``` +{ + type: "client", + path: "$.values[?(@.alias = 'my-property-alias')].value", + message: "Must contain at least 3 words" +} +``` + +JsonPath: +``` +"$.values[?(@.alias = 'my-property-alias')].value" +``` + + +Paths are based on JSONPath, using JSON Path Queries when looking up data of an Array. Using Queries enables Paths to not point to specific index, but what makes a entry unique. + +Messages are set via Validators, which is the glue between a the context and a validation source. + +## Validators + +Messages can be set by Validators, a Validator gets assigned to the Validation Context. Making the Context aware about the Validators. + +When the validation context is asked to Validate, it can then call the `validate` method on all the Validators. + +The Validate method can be async, meaning it can request the server or other way figure out its state before resolving. + +We provide a few built in Validators which handles most cases. + +### Form Control Validator + +This Validator binds a Form Control Element with the Validation Context. When the Form Control becomes Invalid, its Validation Message is appended to the Validation Context. From 5d74285094cc95b40d2056e5864fdcf6a7f93ec0 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 13 Aug 2024 11:42:42 +0200 Subject: [PATCH 104/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 60fbedf475..8942e710b9 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -7,7 +7,6 @@ "": { "name": "@umbraco-cms/backoffice", "version": "14.2.0", - "hasInstallScript": true, "license": "MIT", "workspaces": [ "./src/packages/block", @@ -18,8 +17,8 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", - "./src/packages/markdown-editor", "./src/packages/log-viewer", + "./src/packages/markdown-editor", "./src/packages/media", "./src/packages/members", "./src/packages/models-builder", @@ -27,6 +26,7 @@ "./src/packages/packages", "./src/packages/performance-profiling", "./src/packages/property-editors", + "./src/packages/publish-cache", "./src/packages/relations", "./src/packages/search", "./src/packages/static-file", @@ -7550,6 +7550,10 @@ "resolved": "src/packages/property-editors", "link": true }, + "node_modules/@umbraco-backoffice/publish-cache": { + "resolved": "src/packages/publish-cache", + "link": true + }, "node_modules/@umbraco-backoffice/relation": { "resolved": "src/packages/relations", "link": true @@ -22810,10 +22814,13 @@ "src/packages/packages": { "name": "@umbraco-backoffice/package" }, - "src/packages/performance-profiling": {}, + "src/packages/performance-profiling": { + "name": "@umbraco-backoffice/performance-profiling" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, + "src/packages/publish-cache": {}, "src/packages/relations": { "name": "@umbraco-backoffice/relation" }, From 9b95cf38d0ece5d0ee9da96a163a7a1e4ca07946 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 13 Aug 2024 11:42:56 +0200 Subject: [PATCH 105/152] Update package-lock.json --- src/Umbraco.Web.UI.Client/package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 8942e710b9..44f6c6ecef 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -22820,7 +22820,9 @@ "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, - "src/packages/publish-cache": {}, + "src/packages/publish-cache": { + "name": "@umbraco-backoffice/publish-cache" + }, "src/packages/relations": { "name": "@umbraco-backoffice/relation" }, From cddbf09a5083db41b43b9cc728847c4b2540151d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 11:44:46 +0200 Subject: [PATCH 106/152] rename server model validator --- .../src/packages/core/validation/README.md | 30 +++++++++++++++---- .../core/validation/controllers/index.ts | 4 +-- .../server-model-validation.context-token.ts | 6 ---- .../server-model-validator.context-token.ts | 6 ++++ ...t.ts => server-model-validator.context.ts} | 8 ++--- .../workspace/document-workspace.context.ts | 4 +-- 6 files changed, 39 insertions(+), 19 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context-token.ts rename src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/{server-model-validation.context.ts => server-model-validator.context.ts} (94%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index 56115e45ed..45f1029d76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -28,22 +28,40 @@ As well Tabs and other navigation can use this to be highlighted, so the user ca The Path, points to the location of the model that the message is concerning. -The following models headline is too long, which can be target with this path: +The following models headline can be target with this path: Data: ``` { - type: "client", - path: "$.values[?(@.alias = 'my-property-alias')].value", - message: "Must contain at least 3 words" + settings: { + title: 'too short' + } } ``` JsonPath: ``` -"$.values[?(@.alias = 'my-property-alias')].value" +"$.settings.title" ``` +The following example shows how we use JsonPath Queries to target entries of an array: + +Data: +``` +{ + values: [ + { + alias: 'my-alias', + value: 'my-value' + } + ] +} +``` + +JsonPath: +``` +"$.values.[?(@.alias = 'my-alias')].value" +``` Paths are based on JSONPath, using JSON Path Queries when looking up data of an Array. Using Queries enables Paths to not point to specific index, but what makes a entry unique. @@ -62,3 +80,5 @@ We provide a few built in Validators which handles most cases. ### Form Control Validator This Validator binds a Form Control Element with the Validation Context. When the Form Control becomes Invalid, its Validation Message is appended to the Validation Context. + +### Server Model Validator diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts index e28cfdeb9c..66e51504c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts @@ -1,5 +1,5 @@ export * from './bind-server-validation-to-form-control.controller.js'; export * from './form-control-validator.controller.js'; export * from './observe-validation-state.controller.js'; -export * from './server-model-validation.context-token.js'; -export * from './server-model-validation.context.js'; +export * from './server-model-validator.context-token.js'; +export * from './server-model-validator.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts deleted file mode 100644 index a37d4b48c4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context-token.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { UmbServerModelValidationContext } from './server-model-validation.context.js'; -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; - -export const UMB_SERVER_MODEL_VALIDATION_CONTEXT = new UmbContextToken( - 'UmbServerModelValidationContext', -); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context-token.ts new file mode 100644 index 0000000000..67f3cdbffa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context-token.ts @@ -0,0 +1,6 @@ +import type { UmbServerModelValidatorContext } from './server-model-validator.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_SERVER_MODEL_VALIDATOR_CONTEXT = new UmbContextToken( + 'UmbServerModelValidationContext', +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts index 4d92ff53ca..98aa46550e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts @@ -2,7 +2,7 @@ import type { UmbValidator } from '../interfaces/validator.interface.js'; import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../const.js'; -import { UMB_SERVER_MODEL_VALIDATION_CONTEXT } from './server-model-validation.context-token.js'; +import { UMB_SERVER_MODEL_VALIDATOR_CONTEXT } from './server-model-validator.context-token.js'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; @@ -18,8 +18,8 @@ interface ValidateErrorResponseBodyModel { type: string; } -export class UmbServerModelValidationContext - extends UmbContextBase +export class UmbServerModelValidatorContext + extends UmbContextBase implements UmbValidator { #validatePromise?: Promise; @@ -34,7 +34,7 @@ export class UmbServerModelValidationContext } constructor(host: UmbControllerHost) { - super(host, UMB_SERVER_MODEL_VALIDATION_CONTEXT); + super(host, UMB_SERVER_MODEL_VALIDATOR_CONTEXT); this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { if (this.#context) { this.#context.removeValidator(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 4a4058acc3..956366c908 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -53,7 +53,7 @@ import { } from '@umbraco-cms/backoffice/entity-action'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { - UmbServerModelValidationContext, + UmbServerModelValidatorContext, UmbValidationContext, UmbVariantValuesValidationPathTranslator, } from '@umbraco-cms/backoffice/validation'; @@ -91,7 +91,7 @@ export class UmbDocumentWorkspaceContext #languages = new UmbArrayState([], (x) => x.unique); public readonly languages = this.#languages.asObservable(); - #serverValidation = new UmbServerModelValidationContext(this); + #serverValidation = new UmbServerModelValidatorContext(this); #validationRepository?: UmbDocumentValidationRepository; #blueprintRepository = new UmbDocumentBlueprintDetailRepository(this); From f13a22c2050ad6b4bf9d66c95922c9fbad1f20de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 13:20:31 +0200 Subject: [PATCH 107/152] more readme --- .../src/packages/core/validation/README.md | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index 45f1029d76..cdca0a9e7b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -8,7 +8,7 @@ The system both supports handling front-end validation, server-validation and ot The core of the system is a Validation Context, this holds the messages and more. -### Validation Messages +## Validation Messages A Validation message consist of a type, path and body. This typically looks like this: @@ -24,7 +24,7 @@ Because each validation issue is presented in the Validation Context as a Messag One benefit of this is that Elements that are removed from screen can still have their validation messages preventing the submission of a dataset. As well Tabs and other navigation can use this to be highlighted, so the user can be guide to the location. -#### Path +### Path The Path, points to the location of the model that the message is concerning. @@ -82,3 +82,25 @@ We provide a few built in Validators which handles most cases. This Validator binds a Form Control Element with the Validation Context. When the Form Control becomes Invalid, its Validation Message is appended to the Validation Context. ### Server Model Validator + +This Validator can asks a end-point for validation of the model. + +The Server Model Validator stores the data that was send to the server on the Validation Context. This is then later used by Validation Path Translators to convert index based paths to Json Path Queries. + +This is needed to allow the user to make changes to the data, without loosing the accuracy of the messages coming from server validation. + +## Validation Path Translator + +Validation Path Translator translate Message Paths into a format that works while manipulating the model. +This enables the user to retrieve validation messages from the server regarding entries of a list, and then the user can insert more items and still have the validation appearing in the right spots. +This would not be possible with index based paths, which is why we translate them into JSON Path Queries. + +Such conversation could be from this path: +``` +"$.values.[5].value" +``` + +To this path: +``` +"$.values.[?(@.alias = 'my-alias')].value" +``` From 43653f0d14c02a4d92b498fc607166ca86c7dc4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 14:16:37 +0200 Subject: [PATCH 108/152] JSDocs --- .../src/packages/core/validation/README.md | 21 ++++-- .../validation/context/validation.context.ts | 73 +++++++++++++++---- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index cdca0a9e7b..dc08d38679 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -24,9 +24,10 @@ Because each validation issue is presented in the Validation Context as a Messag One benefit of this is that Elements that are removed from screen can still have their validation messages preventing the submission of a dataset. As well Tabs and other navigation can use this to be highlighted, so the user can be guide to the location. -### Path +### Path aka. Data Path -The Path, points to the location of the model that the message is concerning. +The Path also named Data Path, A Path pointing to the related data in the model. +A massage uses this to point to the location in the model that the message is concerned. The following models headline can be target with this path: @@ -91,9 +92,9 @@ This is needed to allow the user to make changes to the data, without loosing th ## Validation Path Translator -Validation Path Translator translate Message Paths into a format that works while manipulating the model. -This enables the user to retrieve validation messages from the server regarding entries of a list, and then the user can insert more items and still have the validation appearing in the right spots. -This would not be possible with index based paths, which is why we translate them into JSON Path Queries. +Validation Path Translator translate Message Paths into a format that is independent of the actual current data. But compatible with mutations of that data model. +This enables the user to retrieve validation messages from the server, and then the user can insert more items and still have the validation appearing in the right spots. +This would not be possible with index based paths, which is why we translate those into JSON Path Queries. Such conversation could be from this path: ``` @@ -104,3 +105,13 @@ To this path: ``` "$.values.[?(@.alias = 'my-alias')].value" ``` + +Once this path is converted to use Json Path Queries, the Data can be changed. The concerned entry might get another index. Without that affecting the accuracy of the path. + +### Late registered Path Translators + +Translators can be registered late. This means that a Property Editor that has a complex value structure, can register a Path Translator for its part of the data. Such Translator will appear late because the Property might not be rendered in the users current view, but first when the user navigates there. +This is completely fine, as messages can be partly translated and then enhanced by late coming Path Translators. + +This fact enables a property to observe if there is any Message Paths that start with the same path as the Data Path for the Property. In this was a property can know that it contains a Validation Message without the Message Path begin completely translated. + diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index a5b5c87796..0fa945de35 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -15,6 +15,11 @@ function ReplaceStartOfString(path: string, startFrom: string, startTo: string): return path; } +/** + * Validation Context is the core of Validation. + * It hosts Validators that has to validate for the context to be valid. + * It can also be used as a Validator as part of a parent Validation Context. + */ export class UmbValidationContext extends UmbControllerBase implements UmbValidator { // The current provider controller, that is providing this context: #providerCtrl?: UmbContextProviderController; @@ -47,10 +52,18 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida super(host); } + /** + * Add a path translator to this validation context. + * @param translator + */ async addTranslator(translator: UmbValidationMessageTranslator) { this.messages.addTranslator(translator); } + /** + * Remove a path translator from this validation context. + * @param translator + */ async removeTranslator(translator: UmbValidationMessageTranslator) { this.messages.removeTranslator(translator); } @@ -64,11 +77,33 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida this.provideContext(UMB_VALIDATION_CONTEXT, this); return this; } + /** + * Provide this validation context to a specific controller host. + * This can be used to Host a validation context in a Workspace, but provide it on a certain scope, like a specific Workspace View. + * @param controllerHost {UmbClassInterface} + */ provideAt(controllerHost: UmbClassInterface): void { this.#providerCtrl?.destroy(); this.#providerCtrl = controllerHost.provideContext(UMB_VALIDATION_CONTEXT, this); } + /** + * Define a specific data path for this validation context. + * This will turn this validation context into a sub-context of the parent validation context. + * This means that a two-way binding for messages will be established between the parent and the sub-context. + * And it will inherit the Translation Data from its parent. + * + * messages and data will be localizes accordingly to the given data path. + * @param dataPath {string} - The data path to bind this validation context to. + * @returns + * @example + * ```ts + * const validationContext = new UmbValidationContext(host); + * validationContext.setDataPath("$.values[?(@.alias='my-property')].value"); + * ``` + * + * A message with the path: '$.values[?(@.alias='my-property')].value.innerProperty', will for above example become '$.innerProperty' for the local Validation Context. + */ setDataPath(dataPath: string): void { if (this.#baseDataPath) { if (this.#baseDataPath === dataPath) return; @@ -106,7 +141,6 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } this.#parentMessages = msgs; msgs.forEach((msg) => { - // TODO: Subtract the base path from the path, so it becomes local to this context: const path = ReplaceStartOfString(msg.path, this.#baseDataPath!, '$'); // Notice, the local message uses the same key. [NL] this.messages.addMessage(msg.type, path, msg.body, msg.key); @@ -129,7 +163,6 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } this.#localMessages = msgs; msgs.forEach((msg) => { - // TODO: Prefix the base path from our base path, so it fits in the parent context: // replace this.#baseDataPath (if it starts with it) with $ in the path, so it becomes relative to the parent context const path = ReplaceStartOfString(msg.path, '$', this.#baseDataPath!); // Notice, the parent message uses the same key. [NL] @@ -138,25 +171,24 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida }, 'observeLocalMessages', ); - - // observe if one of the locals got removed. - // It can maybe be done with one set of known/gotten parent messages, that then can be used to detect which are removed. Maybe from both sides. - - // Benefits for workspaces: - // — The workspace can be validated locally, and then the messages can propagate to parent context. (Do we even want that?) - // — The workspace can easier know about its validation state - // — Its the only way the sub-workspace can be validated without triggering the whole validation. - // - The workspace can inherit messages from parent context... — which is good for Blocks - // - The workspace can have its own server messages, that is propagated to parent context. - // - The workspace can inherit server messages from parent context... — server validation of a block, that is part of the parent workspace. - // - Remove parent messages if they go away again if they gets remove here. }).skipHost(); + // 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] } + /** + * Get if this context is valid. + * Notice this does not verify the validity. + */ get isValid(): boolean { return this.#isValid; } + /** + * Add a validator to this context. + * This validator will have to be valid for the context to be valid. + * If the context is in validation mode, the validator will be validated immediately. + * @param validator { UmbValidator } - The validator to add to this context. + */ addValidator(validator: UmbValidator): void { if (this.#validators.includes(validator)) return; this.#validators.push(validator); @@ -166,6 +198,10 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } } + /** + * Remove a validator from this context. + * @param validator {UmbValidator} - The validator to remove from this context. + */ removeValidator(validator: UmbValidator): void { const index = this.#validators.indexOf(validator); if (index !== -1) { @@ -179,7 +215,8 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } /** - * + * Validate this context, all the validators of this context will be validated. + * Notice its a recursive check meaning sub validation contexts also validates their validators. * @returns succeed {Promise} - Returns a promise that resolves to true if the validator succeeded, this depends on the validators and wether forceSucceed is set. */ async validate(): Promise { @@ -206,6 +243,9 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida return Promise.resolve(); } + /** + * Focus the first invalid element that this context can find. + */ focusFirstInvalidElement(): void { const firstInvalid = this.#validators.find((v) => !v.isValid); if (firstInvalid) { @@ -213,6 +253,9 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } } + /** + * Reset the validation state of this context. + */ reset(): void { this.#validationMode = false; this.#validators.forEach((v) => v.reset()); From 7044fffdd7dd06f64104230860e0a1cc666fc9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 14:59:59 +0200 Subject: [PATCH 109/152] icons examples --- .../examples/icons/README.md | 7 ++++ .../examples/icons/files/icon-bomb.ts | 1 + .../examples/icons/files/icon-bones.ts | 14 ++++++++ .../examples/icons/icons-dashboard.ts | 34 +++++++++++++++++++ .../examples/icons/icons-dictionary.ts | 10 ++++++ .../examples/icons/index.ts | 21 ++++++++++++ .../icon-registry/icon-registry.context.ts | 4 +-- 7 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/README.md create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/index.ts diff --git a/src/Umbraco.Web.UI.Client/examples/icons/README.md b/src/Umbraco.Web.UI.Client/examples/icons/README.md new file mode 100644 index 0000000000..42c0147752 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/README.md @@ -0,0 +1,7 @@ +# Icons Example + +This example demonstrates how to registerer your own icons. + +Currently they have to be made as JavaScript files that exports an SVG string. + +Declared as part of a Icon Dictionary in a JavaScript file. diff --git a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts new file mode 100644 index 0000000000..fe1e2907ac --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts @@ -0,0 +1 @@ +export default ``; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts new file mode 100644 index 0000000000..1b37bf8a17 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts @@ -0,0 +1,14 @@ +export default ` + + + +`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts b/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts new file mode 100644 index 0000000000..5917503b48 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts @@ -0,0 +1,34 @@ +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit'; +import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; + +@customElement('example-icons-dashboard') +export class ExampleIconsDashboard extends UmbElementMixin(LitElement) { + override render() { + return html` + +

Custom icons:

+ + +
+ `; + } + + static override styles = [ + UmbTextStyles, + css` + :host { + display: block; + padding: var(--uui-size-layout-1); + } + `, + ]; +} + +export default ExampleIconsDashboard; + +declare global { + interface HTMLElementTagNameMap { + 'example-icons-dashboard': ExampleIconsDashboard; + } +} diff --git a/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts b/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts new file mode 100644 index 0000000000..2472d6bf5d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts @@ -0,0 +1,10 @@ +export default [ + { + name: 'my-icon-bomb', + path: () => import('./files/icon-bomb.js'), + }, + { + name: 'my-icon-bones', + path: () => import('./files/icon-bones.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/examples/icons/index.ts b/src/Umbraco.Web.UI.Client/examples/icons/index.ts new file mode 100644 index 0000000000..b53fa46a80 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/index.ts @@ -0,0 +1,21 @@ +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'icons', + name: 'Example Dataset Dashboard', + alias: 'example.dashboard.dataset', + js: () => import('./icons-dictionary.js'), + }, + { + type: 'dashboard', + name: 'Example Icons Dashboard', + alias: 'example.dashboard.icons', + element: () => import('./icons-dashboard.js'), + weight: 900, + meta: { + label: 'Icons example', + pathname: 'icons-example', + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-registry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-registry.context.ts index 2dfbcca3bf..d479851c79 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-registry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-registry.context.ts @@ -30,12 +30,12 @@ export class UmbIconRegistryContext extends UmbContextBase(manifest.js); if (!js || !js.default || !Array.isArray(js.default)) { From c2cc8b26e2c21c349313ff02b6c6b0d04dfeb198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 15:02:46 +0200 Subject: [PATCH 110/152] note on colors --- .../examples/dashboard-with-property-dataset/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/README.md b/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/README.md index 4a1b15255a..557c5709cc 100644 --- a/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/README.md +++ b/src/Umbraco.Web.UI.Client/examples/dashboard-with-property-dataset/README.md @@ -3,3 +3,8 @@ This example demonstrates the essence of the Property Dataset. This dashboard implements such, to display a few selected Property Editors and bind the data back to the Dashboard. + + +## SVG code of Icons + +Make sure to use currentColor for fill or stroke color, as that will make the icon adapt to the font color of where its begin used. From c9332958f0e4809891add2c882f3eb40c3128269 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 13 Aug 2024 16:40:11 +0100 Subject: [PATCH 111/152] Member picker modal UI tweaks - Localization of "Search" placeholder label - Shows search clear button only when there is text/query - Code imports sorting --- .../member-picker-modal.element.ts | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts index bcd3db3584..e0c3ec8ed1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/member-picker-modal/member-picker-modal.element.ts @@ -1,13 +1,13 @@ import { UmbMemberCollectionRepository } from '../../collection/index.js'; -import type { UmbMemberDetailModel } from '../../types.js'; import { UmbMemberSearchProvider } from '../../search/member.search-provider.js'; +import type { UmbMemberDetailModel } from '../../types.js'; import type { UmbMemberItemModel } from '../../repository/index.js'; import type { UmbMemberPickerModalValue, UmbMemberPickerModalData } from './member-picker-modal.token.js'; -import { html, customElement, state, repeat, css, nothing } from '@umbraco-cms/backoffice/external/lit'; -import { UmbSelectionManager, debounce } from '@umbraco-cms/backoffice/utils'; +import { css, customElement, html, nothing, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { debounce, UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-member-picker-modal') export class UmbMemberPickerModalElement extends UmbModalBaseElement< @@ -91,20 +91,21 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< } override render() { - return html` - ${this.#renderSearch()} ${this.#renderItems()} - -
- this.modalContext?.reject()}> - this.modalContext?.submit()}> -
-
`; + return html` + + ${this.#renderSearch()} ${this.#renderItems()} +
+ this.modalContext?.reject()}> + this.modalContext?.submit()}> +
+
+ `; } #renderItems() { @@ -120,18 +121,26 @@ export class UmbMemberPickerModalElement extends UmbModalBaseElement< #renderSearch() { return html` - +
${this._searching ? html`` : html``}
- -
- - - -
+ ${when( + this._searchQuery, + () => html` +
+ + + +
+ `, + )}
${this.#renderSearchResult()} From 6b83621b8d6d9af8fbd452151434439e09390d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 21:01:31 +0200 Subject: [PATCH 112/152] Memoization --- .../bind-server-validation-to-form-control.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts index 7300c874ba..9b2857a841 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/bind-server-validation-to-form-control.controller.ts @@ -1,7 +1,7 @@ import type { UmbValidationMessage } from '../context/validation-messages.manager.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; import type { UmbFormControlMixinInterface } from '../mixins/form-control.mixin.js'; -import { jsonStringComparison } from '@umbraco-cms/backoffice/observable-api'; +import { defaultMemoization } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -28,7 +28,7 @@ export class UmbBindServerValidationToFormControl extends UmbControllerBase { this.#value = value; } else { // If not valid lets see if we should remove server validation [NL] - if (!jsonStringComparison(this.#value, value)) { + if (!defaultMemoization(this.#value, value)) { this.#value = value; // Only remove server validations from validation context [NL] this.#messages.forEach((message) => { From 8d92416644bf822470eaecf0a15b10622c593f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 21:01:45 +0200 Subject: [PATCH 113/152] validation directive --- .../src/external/lit/index.ts | 2 +- .../property-type-workspace.context.ts | 4 ++ ...roperty-workspace-view-settings.element.ts | 24 +++++---- .../bind-to-validation.lit-directive.ts | 51 +++++++++++++++++++ .../src/packages/core/validation/index.ts | 1 + 5 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/lit/index.ts b/src/Umbraco.Web.UI.Client/src/external/lit/index.ts index e7c9bc62c3..a1f3492898 100644 --- a/src/Umbraco.Web.UI.Client/src/external/lit/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/lit/index.ts @@ -1,6 +1,6 @@ export * from 'lit'; export * from 'lit/decorators.js'; -export { directive, AsyncDirective } from 'lit/async-directive.js'; +export { directive, AsyncDirective, type PartInfo } from 'lit/async-directive.js'; export * from 'lit/directives/class-map.js'; export * from 'lit/directives/if-defined.js'; export * from 'lit/directives/map.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts index 880e05d8c2..a5d949cb39 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts @@ -16,6 +16,7 @@ import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-regist import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; import { UmbId } from '@umbraco-cms/backoffice/id'; +import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; export class UmbPropertyTypeWorkspaceContext extends UmbSubmittableWorkspaceContextBase @@ -39,6 +40,9 @@ export class UmbPropertyTypeWorkspaceContext { this.#context = instance; - this.observe(instance.data, (data) => { - this._data = data; - }); + this.observe( + instance.data, + (data) => { + this._data = data; + }, + 'observeData', + ); }); this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (instance) => { @@ -180,10 +185,12 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i @@ -426,14 +433,11 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i uui-input { width: 100%; } - #alias-lock { - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; + uui-input:focus-within { + z-index: 1; } - #alias-lock uui-icon { - margin-bottom: 2px; + uui-input-lock:focus-within { + z-index: 1; } .container { display: flex; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts new file mode 100644 index 0000000000..088c266842 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts @@ -0,0 +1,51 @@ +import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { AsyncDirective, directive, nothing, type ElementPart } from '@umbraco-cms/backoffice/external/lit'; +import { UmbFormControlMixinInterface, UmbFormControlValidator } from '@umbraco-cms/backoffice/validation'; + +/** + * The `bind to validation` directive connects the Form Control Element to closets Validation Context. + */ +class UmbBindToValidationDirective extends AsyncDirective { + #host?: UmbControllerHost; + #dataPath?: string; + #el?: UmbFormControlMixinInterface; + #validator?: UmbFormControlValidator; + + override render(host: UmbControllerHost, dataPath: string) { + return nothing; + } + + override update(part: ElementPart, args: [UmbControllerHost, string]) { + if (this.#el !== part.element) { + this.#host = args[0]; + this.#dataPath = args[1]; + if (!this.#host || !this.#dataPath || !part.element) return nothing; + this.#el = part.element as UmbFormControlMixinInterface; + this.#validator = new UmbFormControlValidator(this.#host, this.#el, this.#dataPath); + } + return nothing; + } + + override disconnected() { + if (this.#validator) { + this.#validator?.destroy(); + this.#validator = undefined; + } + this.#el = undefined; + } + + /*override reconnected() { + }*/ +} + +/** + * @description + * A Lit directive, which binds the validation state of the element to the Validation Context. + * @example: + * ```js + * html``; + * ``` + */ +export const umbBindToValidation = directive(UmbBindToValidationDirective); + +//export type { UmbFocusDirective }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts index 10a56d0882..ecb51f74ac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts @@ -6,3 +6,4 @@ export * from './interfaces/index.js'; export * from './mixins/index.js'; export * from './translators/index.js'; export * from './utils/index.js'; +export * from './directives/bind-to-validation.lit-directive.js'; From 6bfbc719bc6e4ef65e79c9e5385dfc3f0bb75e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 21:12:57 +0200 Subject: [PATCH 114/152] readme addition --- .../src/packages/core/validation/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index dc08d38679..b7d458a5c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -82,6 +82,8 @@ We provide a few built in Validators which handles most cases. This Validator binds a Form Control Element with the Validation Context. When the Form Control becomes Invalid, its Validation Message is appended to the Validation Context. +Notice this one also comes as a Lit Directive called `umbBindToValidation`. + ### Server Model Validator This Validator can asks a end-point for validation of the model. From 0eba18c384083aedba6ed2efbc1a33af93b5bbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 13 Aug 2024 21:26:59 +0200 Subject: [PATCH 115/152] validation for property type workspace --- ...roperty-workspace-view-settings.element.ts | 60 +++++++++++-------- .../data-type-flow-input.element.ts | 22 ++++++- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts index 8ca49fbda7..2315c179fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts @@ -182,28 +182,34 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i
- - - - - + + + + + + + + +
- + + +
Validation diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index 0b433b76ed..b0166029f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -1,10 +1,10 @@ import { UMB_DATA_TYPE_PICKER_FLOW_MODAL } from '../../modals/index.js'; import { UMB_DATATYPE_WORKSPACE_MODAL } from '../../workspace/index.js'; import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; // Note: Does only support picking a single data type. But this could be developed later into this same component. To follow other picker input components. /** @@ -15,7 +15,7 @@ import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; * @fires focus - when the input gains focus */ @customElement('umb-data-type-flow-input') -export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, '') { +export class UmbInputDataTypeElement extends UmbFormControlMixin(UmbLitElement, '') { protected override getFormElement() { return undefined; } @@ -47,6 +47,12 @@ export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, constructor() { super(); + this.addValidator( + 'valueMissing', + () => UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + () => this.hasAttribute('required') && this.value === '', + ); + this.#editDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL); new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL) @@ -66,6 +72,10 @@ export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, }); } + override focus() { + this.shadowRoot?.querySelector('umb-ref-data-type')?.focus(); + } + override render() { return this._ids && this._ids.length > 0 ? html` @@ -87,6 +97,9 @@ export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, label="Select Property Editor" look="placeholder" color="default" + @focus=${() => { + this.pristine = false; + }} .href=${this._createRoute}> `; } @@ -98,6 +111,11 @@ export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, --uui-button-padding-top-factor: 4; --uui-button-padding-bottom-factor: 4; } + :host(:invalid:not([pristine])) #empty-state-button { + --uui-button-border-color: var(--uui-color-danger); + --uui-button-border-width: 2px; + --uui-button-contrast: var(--uui-color-danger); + } `, ]; } From ba9b04dfb527da58ebaa64c02c4b157ec44676db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 10:38:37 +0200 Subject: [PATCH 116/152] break out code --- .../packages/core/validation/context/validation.context.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 0fa945de35..cbf4266b63 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -228,6 +228,11 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida () => Promise.resolve(false), ); + if (!this.messages) { + // This Context has been destroyed while is was validating, so we should not continue. + return; + } + // If we have any messages then we are not valid, otherwise lets check the validation results: [NL] // This enables us to keep client validations though UI is not present anymore — because the client validations got defined as messages. [NL] const isValid = this.messages.getHasAnyMessages() ? false : resultsStatus; From fd6402a4bcefc972548d7b73027bb6c633b8d07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 11:17:26 +0200 Subject: [PATCH 117/152] filter type --- .../block/validation/data-path-element-data-filter.function.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts index be01a28c10..380058b22c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts @@ -6,7 +6,7 @@ import type { UmbBlockDataType } from '../types.js'; * @param udi {string} - The udi of the block Element data. * @returns */ -export function UmbDataPathBlockElementDataFilter(data: UmbBlockDataType): string { +export function UmbDataPathBlockElementDataFilter(data: Pick): string { // write a array of strings for each property, where alias must be present and culture and segment are optional //const filters: Array = [`@.udi = '${udi}'`]; //return `?(${filters.join(' && ')})`; From e8bbb863b3337c2a80a83f0812f2186ca153897f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 11:17:53 +0200 Subject: [PATCH 118/152] ability to define controller alias --- .../controllers/observe-validation-state.controller.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/observe-validation-state.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/observe-validation-state.controller.ts index 3c4ee10667..798f63d6c0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/observe-validation-state.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/observe-validation-state.controller.ts @@ -2,12 +2,16 @@ import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -const CtrlSymbol = Symbol(); const ObserveSymbol = Symbol(); export class UmbObserveValidationStateController extends UmbControllerBase { - constructor(host: UmbControllerHost, dataPath: string | undefined, callback: (messages: boolean) => void) { - super(host, CtrlSymbol); + constructor( + host: UmbControllerHost, + dataPath: string | undefined, + callback: (messages: boolean) => void, + controllerAlias?: string, + ) { + super(host, controllerAlias ?? 'observeValidationState_' + dataPath); if (dataPath) { this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { this.observe(context.messages.hasMessagesOfPathAndDescendant(dataPath), callback, ObserveSymbol); From 32bba703adddf742e8c74cd133d97f445d77afb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 12:46:02 +0200 Subject: [PATCH 119/152] badges + styling --- .../block-list-entry.element.ts | 72 ++++++++++++++++++- .../property-editor-ui-block-list.element.ts | 14 +++- ...ck-editor-custom-view-element.interface.ts | 2 + .../validation/context/validation.context.ts | 1 + 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index 4d8764fec6..6079200551 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -10,6 +10,8 @@ import '../inline-list-block/index.js'; import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js'; import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js'; +import { UmbObserveValidationStateController } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathBlockElementDataFilter } from '@umbraco-cms/backoffice/block'; /** * @element umb-block-list-entry @@ -33,6 +35,16 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper if (!value) return; this._contentUdi = value; this.#context.setContentUdi(value); + + new UmbObserveValidationStateController( + this, + `$.contentData[${UmbDataPathBlockElementDataFilter({ udi: value })}]`, + (hasMessages) => { + this._contentInvalid = hasMessages; + this._blockViewProps.contentInvalid = hasMessages; + }, + 'observeMessagesForContent', + ); } private _contentUdi?: string | undefined; @@ -61,6 +73,14 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper @state() _inlineEditingMode?: boolean; + // 'content-invalid' attribute is used for styling purpose. + @property({ type: Boolean, attribute: 'content-invalid', reflect: true }) + _contentInvalid?: boolean; + + // 'settings-invalid' attribute is used for styling purpose. + @property({ type: Boolean, attribute: 'settings-invalid', reflect: true }) + _settingsInvalid?: boolean; + @state() _blockViewProps: UmbBlockEditorCustomViewProperties = { contentUdi: undefined!, @@ -141,6 +161,20 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper this.#context.settings, (settings) => { this.#updateBlockViewProps({ settings }); + + this.removeUmbControllerByAlias('observeMessagesForSettings'); + if (settings) { + // Observe settings validation state: + new UmbObserveValidationStateController( + this, + `$.settingsData[${UmbDataPathBlockElementDataFilter(settings)}]`, + (hasMessages) => { + this._settingsInvalid = hasMessages; + this._blockViewProps.settingsInvalid = hasMessages; + }, + 'observeMessagesForSettings', + ); + } }, null, ); @@ -218,16 +252,26 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper > ${this._showContentEdit && this._workspaceEditContentPath - ? html` + ? html` + ${this._contentInvalid + ? html`!` + : ''} ` : ''} ${this._hasSettings && this._workspaceEditSettingsPath - ? html` + ? html` + ${this._settingsInvalid + ? html`!` + : ''} ` : ''} - this.#context.requestDelete()}> + this.#context.requestDelete()}> @@ -248,11 +292,33 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper position: absolute; top: var(--uui-size-2); right: var(--uui-size-2); + opacity: 0; + transition: opacity 120ms; + } + :host(:hover) uui-action-bar, + uui-action-bar:focus, + :host(:focus-within) uui-action-bar { + opacity: 1; } :host([drag-placeholder]) { opacity: 0.2; } + + :host([settings-invalid])::before, + :host([content-invalid])::before { + content: ''; + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + border: 1px solid var(--uui-color-danger); + border-radius: var(--uui-border-radius); + } + + uui-badge { + z-index: 2; + } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index def96de8f3..61a09cea95 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -51,6 +51,7 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement #validationContext = new UmbValidationContext(this).provide(); #contentDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; + #settingsDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; //#catalogueModal: UmbModalRouteRegistrationController; @@ -133,9 +134,7 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement this.observe( context.dataPath, (dataPath) => { - // - // TODO: Make translator for settings. - + // Translate paths for content elements: this.#contentDataPathTranslator?.destroy(); if (dataPath) { // Set the data path for the local validation context: @@ -143,6 +142,15 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement this.#contentDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'contentData'); } + + // Translate paths for settings elements: + this.#settingsDataPathTranslator?.destroy(); + if (dataPath) { + // Set the data path for the local validation context: + this.#validationContext.setDataPath(dataPath); + + this.#settingsDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'settingsData'); + } }, 'observeDataPath', ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts index d9b7a73de0..35ed551b97 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts @@ -46,6 +46,8 @@ export interface UmbBlockEditorCustomViewProperties< layout?: LayoutType; content?: UmbBlockDataType; settings?: UmbBlockDataType; + contentInvalid?: boolean; + settingsInvalid?: boolean; } export interface UmbBlockEditorCustomViewElement< diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index cbf4266b63..73d6fb95d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -276,6 +276,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } override destroy(): void { + this.#providerCtrl = undefined; if (this.#parent) { this.#parent.removeValidator(this); } From 54007a4c6c28ee9bb0a0b4e980718219c9ec30f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 12:48:39 +0200 Subject: [PATCH 120/152] show actions on invalid --- .../block-list-entry/block-list-entry.element.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index 6079200551..ab6f887118 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -287,19 +287,23 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper :host { position: relative; display: block; + --umb-block-list-entry-actions-opacity: 0; } + + :host([settings-invalid]), + :host([content-invalid]), + :host(:hover), + :host(:focus-within) { + --umb-block-list-entry-actions-opacity: 1; + } + uui-action-bar { position: absolute; top: var(--uui-size-2); right: var(--uui-size-2); - opacity: 0; + opacity: var(--umb-block-list-entry-actions-opacity, 0); transition: opacity 120ms; } - :host(:hover) uui-action-bar, - uui-action-bar:focus, - :host(:focus-within) uui-action-bar { - opacity: 1; - } :host([drag-placeholder]) { opacity: 0.2; From f57613d7dc57d36472102c469cb26164106d8f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 12:52:24 +0200 Subject: [PATCH 121/152] color content edit button --- .../block-list-entry/block-list-entry.element.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index ab6f887118..d0c0dccfc6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -252,7 +252,11 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper > ${this._showContentEdit && this._workspaceEditContentPath - ? html` + ? html` ${this._contentInvalid ? html`!` @@ -307,14 +311,14 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper :host([drag-placeholder]) { opacity: 0.2; + --umb-block-list-entry-actions-opacity: 0; } - :host([settings-invalid])::before, - :host([content-invalid])::before { + :host([settings-invalid])::after, + :host([content-invalid])::after { content: ''; position: absolute; inset: 0; - z-index: 1; pointer-events: none; border: 1px solid var(--uui-color-danger); border-radius: var(--uui-border-radius); From 873a96f629343ccaf81043270c67b3772605a0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 13:22:09 +0200 Subject: [PATCH 122/152] comment on slot --- .../components/ref-list-block/ref-list-block.element.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts index 2c167d839a..4e82a91a05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts @@ -17,7 +17,7 @@ export class UmbRefListBlockElement extends UmbLitElement { @state() _content?: UmbBlockDataType; - @state() + @property() _workspaceEditPath?: string; constructor() { @@ -44,6 +44,7 @@ export class UmbRefListBlockElement extends UmbLitElement { } override render() { + // TODO: apply `slot="name"` to the `umb-ufm-render` element, when UUI supports it. [NL] return html` From 725ea33621b247c5d90e081b7af9b69a03ab1f86 Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:50:37 +0200 Subject: [PATCH 123/152] export import document types --- .../export/document-type-export.action.ts | 18 ++ .../entity-actions/export/manifests.ts | 20 ++ .../export/repository/constants.ts | 1 + .../document-type-export.repository.ts | 21 +++ ...document-type-export.server.data-source.ts | 33 ++++ .../entity-actions/export/repository/index.ts | 2 + .../export/repository/manifests.ts | 11 ++ .../import/document-type-import.action.ts | 15 ++ .../entity-actions/import/manifests.ts | 21 +++ .../document-type-import-modal.element.ts | 173 ++++++++++++++++++ .../modal/document-type-import-modal.token.ts | 17 ++ .../entity-actions/import/modal/index.ts | 1 + .../entity-actions/import/modal/manifests.ts | 10 + .../import/repository/constants.ts | 1 + .../document-type-import.repository.ts | 21 +++ ...document-type-import.server.data-source.ts | 37 ++++ .../entity-actions/import/repository/index.ts | 2 + .../import/repository/manifests.ts | 11 ++ .../entity-actions/manifests.ts | 4 + 19 files changed, 419 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.repository.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts new file mode 100644 index 0000000000..c1912fe902 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts @@ -0,0 +1,18 @@ +import { UmbExportDocumentTypeRepository } from './repository/index.js'; +import { blobDownload } from '@umbraco-cms/backoffice/utils'; +import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; + +export class UmbExportDocumentTypeEntityAction extends UmbEntityActionBase { + #repository = new UmbExportDocumentTypeRepository(this); + + override async execute() { + if (!this.args.unique) throw new Error('Unique is not available'); + + const data = await this.#repository.requestExport(this.args.unique); + if (!data) return; + + blobDownload(data, `${this.args.unique}.udt`, 'text/xml'); + } +} + +export default UmbExportDocumentTypeEntityAction; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/manifests.ts new file mode 100644 index 0000000000..621cd14233 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/manifests.ts @@ -0,0 +1,20 @@ +import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '../../entity.js'; +import { manifests as repositoryManifests } from './repository/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +const entityActions: Array = [ + { + type: 'entityAction', + kind: 'default', + alias: 'Umb.EntityAction.DocumentType.Export', + name: 'Export Document Type Entity Action', + forEntityTypes: [UMB_DOCUMENT_TYPE_ENTITY_TYPE], + api: () => import('./document-type-export.action.js'), + meta: { + icon: 'icon-download-alt', + label: '#actions_export', + }, + }, +]; + +export const manifests: Array = [...entityActions, ...repositoryManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/constants.ts new file mode 100644 index 0000000000..69e9b5543b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/constants.ts @@ -0,0 +1 @@ +export const UMB_EXPORT_DOCUMENT_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Export'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.repository.ts new file mode 100644 index 0000000000..21ba0be694 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.repository.ts @@ -0,0 +1,21 @@ +import { UmbExportDocumentTypeServerDataSource } from './document-type-export.server.data-source.js'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; + +export class UmbExportDocumentTypeRepository extends UmbRepositoryBase { + #exportSource = new UmbExportDocumentTypeServerDataSource(this); + + async requestExport(unique: string) { + const { data, error } = await this.#exportSource.export(unique); + + if (!error) { + const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + const notification = { data: { message: `Exported` } }; + notificationContext.peek('positive', notification); + } + + return { data, error }; + } +} + +export { UmbExportDocumentTypeRepository as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts new file mode 100644 index 0000000000..b8aba51188 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts @@ -0,0 +1,33 @@ +import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +/** + * Export Document Server Data Source + * @export + * @class UmbExportDocumentTypeServerDataSource + */ +export class UmbExportDocumentTypeServerDataSource { + #host: UmbControllerHost; + + /** + * Creates an instance of UmbExportDocumentTypeServerDataSource. + * @param {UmbControllerHost} host + * @memberof UmbExportDocumentTypeServerDataSource + */ + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Export an item for the given id to the destination unique + * @param {unique} unique + * @returns {*} + * @memberof UmbExportDocumentTypeServerDataSource + */ + async export(unique: string) { + if (!unique) throw new Error('Unique is missing'); + + return tryExecuteAndNotify(this.#host, DocumentTypeService.getDocumentTypeByIdExport({ id: unique })); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/index.ts new file mode 100644 index 0000000000..0aca422f31 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/index.ts @@ -0,0 +1,2 @@ +export { UmbExportDocumentTypeRepository } from './document-type-export.repository.js'; +export { UMB_EXPORT_DOCUMENT_TYPE_REPOSITORY_ALIAS } from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/manifests.ts new file mode 100644 index 0000000000..4c4475f974 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/manifests.ts @@ -0,0 +1,11 @@ +import { UMB_EXPORT_DOCUMENT_TYPE_REPOSITORY_ALIAS } from './constants.js'; +import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +const exportRepository: ManifestRepository = { + type: 'repository', + alias: UMB_EXPORT_DOCUMENT_TYPE_REPOSITORY_ALIAS, + name: 'Export Document Type Repository', + api: () => import('./document-type-export.repository.js'), +}; + +export const manifests: Array = [exportRepository]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts new file mode 100644 index 0000000000..636083f332 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts @@ -0,0 +1,15 @@ +import { UMB_DOCUMENT_TYPE_IMPORT_MODAL } from './modal/document-type-import-modal.token.js'; +import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export class UmbImportDocumentTypeEntityAction extends UmbEntityActionBase { + override async execute() { + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const modalContext = modalManager.open(this, UMB_DOCUMENT_TYPE_IMPORT_MODAL, { + data: { unique: this.args.unique }, + }); + await modalContext.onSubmit().catch(() => {}); + } +} + +export default UmbImportDocumentTypeEntityAction; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/manifests.ts new file mode 100644 index 0000000000..0a34f26496 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/manifests.ts @@ -0,0 +1,21 @@ +import { UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE } from '../../entity.js'; +import { manifests as repositoryManifests } from './repository/manifests.js'; +import { manifests as modalManifests } from './modal/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +const entityActions: Array = [ + { + type: 'entityAction', + kind: 'default', + alias: 'Umb.EntityAction.DocumentType.Import', + name: 'Export Document Type Entity Action', + forEntityTypes: [UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE], + api: () => import('./document-type-import.action.js'), + meta: { + icon: 'icon-page-up', + label: '#actions_import', + }, + }, +]; + +export const manifests: Array = [...entityActions, ...repositoryManifests, ...modalManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.element.ts new file mode 100644 index 0000000000..9bfef4ee36 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.element.ts @@ -0,0 +1,173 @@ +import { UmbDocumentTypeImportRepository } from '../repository/document-type-import.repository.js'; +import type { + UmbDocumentTypeImportModalData, + UmbDocumentTypeImportModalValue, +} from './document-type-import-modal.token.js'; +import { css, html, customElement, query, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import type { UmbDropzoneElement } from '@umbraco-cms/backoffice/media'; + +interface UmbDocumentTypePreview { + unique: string; + name: string; + alias: string; + icon: string; +} + +@customElement('umb-document-type-import-modal') +export class UmbDocumentTypeImportModalLayout extends UmbModalBaseElement< + UmbDocumentTypeImportModalData, + UmbDocumentTypeImportModalValue +> { + #documentTypeImportRepository = new UmbDocumentTypeImportRepository(this); + #temporaryUnique?: string; + #fileReader; + + @state() + private _fileContent: Array = []; + + @query('#dropzone') + private dropzone?: UmbDropzoneElement; + + constructor() { + super(); + this.#fileReader = new FileReader(); + this.#fileReader.onload = (e) => { + if (typeof e.target?.result === 'string') { + const fileContent = e.target.result; + this.#documentTypePreviewBuilder(fileContent); + } else { + this.#requestReset(); + } + }; + } + + #onFileDropped() { + const data = this.dropzone?.getFiles()[0]; + if (!data) return; + + this.#temporaryUnique = data.temporaryUnique; + this.#fileReader.readAsText(data.file); + } + + async #onFileImport() { + if (!this.#temporaryUnique) return; + const { error } = await this.#documentTypeImportRepository.requestImport(this.#temporaryUnique); + if (error) return; + this._submitModal(); + } + + #documentTypePreviewBuilder(htmlString: string) { + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlString, 'text/xml'); + const elements = doc.childNodes; + + const documentTypes: Array = []; + + elements.forEach((node) => { + if (node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'DocumentType') { + documentTypes.push(node as Element); + } + }); + + this._fileContent = this.#documentTypePreviewItemBuilder(documentTypes); + } + + #documentTypePreviewItemBuilder(elements: Array) { + const documentTypes: Array = []; + elements.forEach((documentType) => { + const info = documentType.getElementsByTagName('Info')[0]; + const unique = info.getElementsByTagName('Key')[0].textContent ?? ''; + const name = info.getElementsByTagName('Name')[0].textContent ?? ''; + const alias = info.getElementsByTagName('Alias')[0].textContent ?? ''; + const icon = info.getElementsByTagName('Icon')[0].textContent ?? ''; + + documentTypes.push({ unique, name, alias, icon }); + }); + return documentTypes; + } + + #requestReset() { + this._fileContent = []; + this.#temporaryUnique = undefined; + } + + async #onBrowse() { + this.dropzone?.browse(); + } + + override render() { + return html` + ${this.#renderUploadZone()} + + + `; + } + + #renderUploadZone() { + return html` + ${when( + this._fileContent.length, + () => + html` + + + `, + () => + /**TODO Add localizations */ + html`
+ Drag and drop your file here + + +
`, + )} + `; + } + + static override styles = [ + UmbTextStyles, + css` + #wrapper { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + position: relative; + gap: var(--uui-size-space-3); + border: 2px dashed var(--uui-color-divider-standalone); + background-color: var(--uui-color-surface-alt); + padding: var(--uui-size-space-6); + } + + #import { + margin-top: var(--uui-size-space-6); + } + `, + ]; +} + +export default UmbDocumentTypeImportModalLayout; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-type-import-modal': UmbDocumentTypeImportModalLayout; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.token.ts new file mode 100644 index 0000000000..1ec359be4c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/document-type-import-modal.token.ts @@ -0,0 +1,17 @@ +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export interface UmbDocumentTypeImportModalData { + unique: string | null; +} + +export interface UmbDocumentTypeImportModalValue {} + +export const UMB_DOCUMENT_TYPE_IMPORT_MODAL = new UmbModalToken< + UmbDocumentTypeImportModalData, + UmbDocumentTypeImportModalValue +>('Umb.Modal.DocumentType.Import', { + modal: { + type: 'sidebar', + size: 'small', + }, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/index.ts new file mode 100644 index 0000000000..7bada0a24e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/index.ts @@ -0,0 +1 @@ +export * from './document-type-import-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/manifests.ts new file mode 100644 index 0000000000..e4a851b196 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/modal/manifests.ts @@ -0,0 +1,10 @@ +import type { ManifestModal } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'modal', + alias: 'Umb.Modal.DocumentType.Import', + name: 'Document Type Import Modal', + element: () => import('./document-type-import-modal.element.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/constants.ts new file mode 100644 index 0000000000..0da908eb12 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/constants.ts @@ -0,0 +1 @@ +export const UMB_DOCUMENT_TYPE_IMPORT_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Import'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts new file mode 100644 index 0000000000..ea520402b1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts @@ -0,0 +1,21 @@ +import { UmbDocumentTypeImportServerDataSource } from './document-type-import.server.data-source.js'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; + +export class UmbDocumentTypeImportRepository extends UmbRepositoryBase { + #importSource = new UmbDocumentTypeImportServerDataSource(this); + + async requestImport(temporaryUnique: string) { + const { data, error } = await this.#importSource.import(temporaryUnique); + + if (!error) { + const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + const notification = { data: { message: `Imported` } }; + notificationContext.peek('positive', notification); + } + + return { data, error }; + } +} + +export { UmbDocumentTypeImportRepository as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts new file mode 100644 index 0000000000..252e5ca91d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts @@ -0,0 +1,37 @@ +import { DocumentTypeService, type PostDocumentTypeImportData } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +/** + * Document Type Import Server Data Source + * @Import + * @class UmbDocumentTypeImportServerDataSource + */ +export class UmbDocumentTypeImportServerDataSource { + #host: UmbControllerHost; + + /** + * Creates an instance of UmbDocumentTypeImportServerDataSource. + * @param {UmbControllerHost} host + * @memberof UmbDocumentTypeImportServerDataSource + */ + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Import an item for the given id to the destination unique + * @param {temporaryUnique} temporaryUnique + * @returns {*} + * @memberof UmbDocumentTypeImportServerDataSource + */ + async import(temporaryUnique: string) { + if (!temporaryUnique) throw new Error('Unique is missing'); + + const requestBody: PostDocumentTypeImportData = { + requestBody: { file: { id: temporaryUnique } }, + }; + + return tryExecuteAndNotify(this.#host, DocumentTypeService.postDocumentTypeImport(requestBody)); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/index.ts new file mode 100644 index 0000000000..51a3401f57 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/index.ts @@ -0,0 +1,2 @@ +export { UmbDocumentTypeImportRepository } from './document-type-import.repository.js'; +export { UMB_DOCUMENT_TYPE_IMPORT_REPOSITORY_ALIAS } from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/manifests.ts new file mode 100644 index 0000000000..b5c47f7170 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/manifests.ts @@ -0,0 +1,11 @@ +import { UMB_DOCUMENT_TYPE_IMPORT_REPOSITORY_ALIAS } from './constants.js'; +import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +const importRepository: ManifestRepository = { + type: 'repository', + alias: UMB_DOCUMENT_TYPE_IMPORT_REPOSITORY_ALIAS, + name: 'Import Document Type Repository', + api: () => import('./document-type-import.repository.js'), +}; + +export const manifests: Array = [importRepository]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/manifests.ts index 566d23d078..b23d2c7660 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/manifests.ts @@ -6,6 +6,8 @@ import { import { manifests as createManifests } from './create/manifests.js'; import { manifests as moveManifests } from './move-to/manifests.js'; import { manifests as duplicateManifests } from './duplicate/manifests.js'; +import { manifests as exportManifests } from './export/manifests.js'; +import { manifests as importManifests } from './import/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; const entityActions: Array = [ @@ -27,4 +29,6 @@ export const manifests: Array = [ ...createManifests, ...moveManifests, ...duplicateManifests, + ...exportManifests, + ...importManifests, ]; From 28313b40f7c97554c8aa3bb11d4f33f01c83febf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 13:55:50 +0200 Subject: [PATCH 124/152] umbDestroyOnDisconnect --- .../property-editor-ui-block-list.element.ts | 7 +++- .../directives/destroy.lit-directive.ts | 38 +++++++++++++++++++ .../core/lit-element/directives/index.ts | 1 + 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/destroy.lit-directive.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index 61a09cea95..a53f817d07 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -3,7 +3,7 @@ import type { UmbBlockListLayoutModel, UmbBlockListValueModel } from '../../type import type { UmbBlockListEntryElement } from '../../components/block-list-entry/index.js'; import { UmbBlockListManagerContext } from '../../context/block-list-manager.context.js'; import { UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS } from './manifests.js'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbLitElement, umbDestroyOnDisconnect } from '@umbraco-cms/backoffice/lit-element'; import { html, customElement, property, state, repeat, css } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyEditorUiElement, UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; @@ -206,7 +206,10 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement html` - + `, )} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/destroy.lit-directive.ts b/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/destroy.lit-directive.ts new file mode 100644 index 0000000000..3860d3d87a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/destroy.lit-directive.ts @@ -0,0 +1,38 @@ +import { AsyncDirective, directive, nothing, type ElementPart } from '@umbraco-cms/backoffice/external/lit'; + +/** + * The `focus` directive sets focus on the given element once its connected to the DOM. + */ +class UmbDestroyDirective extends AsyncDirective { + #el?: HTMLElement & { destroy: () => void }; + + override render() { + return nothing; + } + + override update(part: ElementPart) { + this.#el = part.element as any; + return nothing; + } + + override disconnected() { + if (this.#el) { + this.#el.destroy(); + } + this.#el = undefined; + } + + //override reconnected() {} +} + +/** + * @description + * A Lit directive, which destroys the element once its disconnected from the DOM. + * @example: + * ```js + * html``; + * ``` + */ +export const umbDestroyOnDisconnect = directive(UmbDestroyDirective); + +//export type { UmbDestroyDirective }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/index.ts index 0ccb1bc7eb..f6877c9c11 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/lit-element/directives/index.ts @@ -1 +1,2 @@ export * from './focus.lit-directive.js'; +export * from './destroy.lit-directive.js'; From 4bacf2d86ab4cf2c83ab78f661f4971204a656fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:03:09 +0200 Subject: [PATCH 125/152] remove todo --- .../components/workspace-editor/workspace-editor.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts index 15639d6ed7..79a7b63072 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts @@ -19,7 +19,6 @@ import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from * @class UmbWorkspaceEditor * @augments {UmbLitElement} */ -// TODO: This element has a bug in the tabs. After the url changes - for example a new entity/file is chosen in the tree and loaded to the workspace the links in the tabs still point to the previous url and therefore views do not change correctly @customElement('umb-workspace-editor') export class UmbWorkspaceEditorElement extends UmbLitElement { @property() From 1185faa0d31dca5b5223862a0589c9ab8e4e66a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:04:58 +0200 Subject: [PATCH 126/152] change variant text --- .../src/packages/core/property/property/property.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index c217bacb0f..69db32034b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -133,7 +133,7 @@ export class UmbPropertyContext extends UmbContextBase Date: Wed, 14 Aug 2024 21:05:36 +0200 Subject: [PATCH 127/152] remove logger --- .../core/validation/context/validation-messages.manager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 7b3f1fe625..14b2de0184 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -15,9 +15,9 @@ export class UmbValidationMessagesManager { #messages = new UmbArrayState([], (x) => x.key); messages = this.#messages.asObservable(); - constructor() { + /*constructor() { this.#messages.asObservable().subscribe((x) => console.log('all messages:', x)); - } + }*/ /* serializeMessages(fromPath: string, toPath: string): void { From ce53d539aaedc6601a1db59abfba6bc4e5b4acd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:06:29 +0200 Subject: [PATCH 128/152] linting --- .../validation/context/validation.context.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index 73d6fb95d8..627e7212d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -1,13 +1,20 @@ -import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; import type { UmbValidator } from '../interfaces/validator.interface.js'; +import type { UmbValidationMessageTranslator } from '../translators/index.js'; +import { GetValueByJsonPath } from '../utils/json-path.function.js'; import { type UmbValidationMessage, UmbValidationMessagesManager } from './validation-messages.manager.js'; import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; +import type { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; import { type UmbClassInterface, UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbValidationMessageTranslator } from '../translators/index.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; -import { GetValueByJsonPath } from '../utils/json-path.function.js'; +/** + * Helper method to replace the start of a string with another string. + * @param path {string} + * @param startFrom {string} + * @param startTo {string} + * @returns {string} + */ function ReplaceStartOfString(path: string, startFrom: string, startTo: string): string { if (path.startsWith(startFrom + '.')) { return startTo + path.slice(startFrom.length); @@ -95,7 +102,6 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida * * messages and data will be localizes accordingly to the given data path. * @param dataPath {string} - The data path to bind this validation context to. - * @returns * @example * ```ts * const validationContext = new UmbValidationContext(host); @@ -178,6 +184,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida /** * Get if this context is valid. * Notice this does not verify the validity. + * @returns {boolean} */ get isValid(): boolean { return this.#isValid; From d6072a26bc515a0d8eadf5829f41145043a4c462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:12:20 +0200 Subject: [PATCH 129/152] clean up --- ...y-validation-path-translator.controller.ts | 2 +- ...a-validation-path-translator.controller.ts | 4 ++-- .../data-path-element-data-filter.function.ts | 1 + .../context/validation-messages.manager.ts | 20 ------------------- .../bind-to-validation.lit-directive.ts | 5 +++-- .../validation/mixins/form-control.mixin.ts | 4 ---- ...idation-path-translator-base.controller.ts | 2 +- .../validation/utils/json-path.function.ts | 18 +++++++++++++++++ 8 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts index 572de655f9..868ed7f339 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts @@ -1,4 +1,4 @@ -import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { GetPropertyNameFromPath, UmbDataPathPropertyValueFilter, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts index d5c10401ab..7bb1e4e8fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts @@ -1,6 +1,6 @@ -import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbValidationPathTranslatorBase } from '@umbraco-cms/backoffice/validation'; import { UmbDataPathBlockElementDataFilter } from './data-path-element-data-filter.function.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbValidationPathTranslatorBase } from '@umbraco-cms/backoffice/validation'; export class UmbBlockElementDataValidationPathTranslator extends UmbValidationPathTranslatorBase { #propertyName: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts index 380058b22c..51c71b394e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts @@ -4,6 +4,7 @@ import type { UmbBlockDataType } from '../types.js'; * Validation Data Path filter for Block Element Data. * write a JSON-Path filter similar to `?(@.udi = 'my-udi://1234')` * @param udi {string} - The udi of the block Element data. + * @param data {{udi: string}} - A data object with the udi property. * @returns */ export function UmbDataPathBlockElementDataFilter(data: Pick): string { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 14b2de0184..87ee74e55c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -19,19 +19,6 @@ export class UmbValidationMessagesManager { this.#messages.asObservable().subscribe((x) => console.log('all messages:', x)); }*/ - /* - serializeMessages(fromPath: string, toPath: string): void { - this.#messages.setValue( - this.#messages.getValue().map((x) => { - if (x.path.indexOf(fromPath) === 0) { - x.path = toPath + x.path.substring(fromPath.length); - } - return x; - }), - ); - } - */ - getHasAnyMessages(): boolean { return this.#messages.getValue().length !== 0; } @@ -91,13 +78,6 @@ export class UmbValidationMessagesManager { this.#messages.append(newBodies.map((body) => ({ type, key: UmbId.new(), path, body }))); } - /* - appendMessage(msg: UmbValidationMessage): void { - const path = this.#translatePath(msg.path) ?? msg.path; - this.#messages.appendOne({ ...msg, path }); - } - */ - removeMessageByKey(key: string): void { this.#messages.removeOne(key); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts index 088c266842..f6d48f8981 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts @@ -1,6 +1,7 @@ -import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { AsyncDirective, directive, nothing, type ElementPart } from '@umbraco-cms/backoffice/external/lit'; -import { UmbFormControlMixinInterface, UmbFormControlValidator } from '@umbraco-cms/backoffice/validation'; +import type { UmbFormControlMixinInterface } from '@umbraco-cms/backoffice/validation'; +import { UmbFormControlValidator } from '@umbraco-cms/backoffice/validation'; /** * The `bind to validation` directive connects the Form Control Element to closets Validation Context. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index 29fc1a662e..4a634d197c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -144,10 +144,6 @@ export function UmbFormControlMixin< public set pristine(value: boolean) { if (this._pristine !== value) { this._pristine = value; - // Concept: maybe needed? Set all inner form controls to be 'touched' as well. [NL] - /*this.#formCtrlElements.forEach((el) => { - (el as any).pristine = value; - });*/ this.#dispatchValidationState(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts index 61cf4d2911..27dda12276 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/validation-path-translator-base.controller.ts @@ -1,7 +1,7 @@ +import { UMB_VALIDATION_CONTEXT } from '../index.js'; import type { UmbValidationMessageTranslator } from './validation-message-path-translator.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { UMB_VALIDATION_CONTEXT } from '../index.js'; export abstract class UmbValidationPathTranslatorBase extends UmbControllerBase diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts index e24bf87508..98606e2ee3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/json-path.function.ts @@ -1,3 +1,8 @@ +/** + * + * @param data + * @param path + */ export function GetValueByJsonPath(data: any, path: string): any { // strip $ from the path: const strippedPath = path.startsWith('$.') ? path.slice(2) : path; @@ -5,6 +10,10 @@ export function GetValueByJsonPath(data: any, path: string): any { return GetNextPropertyValueFromPath(data, strippedPath); } +/** + * + * @param path + */ export function GetPropertyNameFromPath(path: string): string { // find next '.' or '[' in the path, using regex: const match = path.match(/\.|\[/); @@ -15,6 +24,11 @@ export function GetPropertyNameFromPath(path: string): string { return path.slice(0, match.index); } +/** + * + * @param data + * @param path + */ function GetNextPropertyValueFromPath(data: any, path: string): any { if (!data) return undefined; // find next '.' or '[' in the path, using regex: @@ -75,6 +89,10 @@ function GetNextPropertyValueFromPath(data: any, path: string): any { } } +/** + * + * @param filter + */ function JsFilterFromJsonPathFilter(filter: string): any { // strip ?( and ) from the filter const jsFilter = filter.slice(2, -1); From 0a69c55a2c055ceed2245090bb53ac6e5991ba3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:13:11 +0200 Subject: [PATCH 130/152] remove todo --- .../src/packages/block/block/workspace/block-element-manager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts index 67ef88b64d..be65432e2b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts @@ -28,7 +28,6 @@ export class UmbBlockElementManager extends UmbControllerBase { readonly validation = new UmbValidationContext(this); constructor(host: UmbControllerHost, dataPathPropertyName: string) { - // TODO: Get Workspace Alias via Manifest. super(host); this.observe(this.contentTypeId, (id) => this.structure.loadType(id)); From eeb71900fc560ea4c0190250e7a4630ca2925c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:15:28 +0200 Subject: [PATCH 131/152] update todo --- .../packages/block/block/workspace/block-workspace.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index d3671ecc2a..0f1fb6eff3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -51,7 +51,7 @@ export class UmbBlockWorkspaceContext(undefined); readonly name = this.#label.asObservable(); From fabb3d6d9790aef3e7c5732e8b7e1e70f1077270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 14 Aug 2024 21:32:02 +0200 Subject: [PATCH 132/152] clean up --- .../src/packages/core/modal/token/workspace-modal.token.ts | 1 - .../settings/property-workspace-view-settings.element.ts | 1 - .../validation/controllers/server-model-validator.context.ts | 1 - .../workspace/contexts/submittable-workspace-context-base.ts | 4 +++- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts index c9b2f63681..27b52e61bf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/workspace-modal.token.ts @@ -5,7 +5,6 @@ export interface UmbWorkspaceModalData { baseDataPath?: string; } -// TODO: It would be good with a WorkspaceValueBaseType, to avoid the hardcoded type for unique here: export type UmbWorkspaceModalValue = | { unique: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts index 2315c179fc..182c6ed329 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts @@ -181,7 +181,6 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i return html`
- #rejectSubmit = () => { if (this.#submitPromise) { + // TODO: Capture the validation contexts messages on open, and then reset to them in this case. [NL] + this.#submitReject?.(); this.#submitPromise = undefined; this.#submitResolve = undefined; @@ -134,7 +136,7 @@ export abstract class UmbSubmittableWorkspaceContextBase this.#resolveSubmit(); // Calling reset on the validation context here. [NL] - // TODO: Consider if we really ant this, cause on save, we do not want to reset this... [NL] (Also adapt to concept of multiple validation contexts) + // TODO: Capture the validation messages on open, and then reset to that. //this.validation.reset(); }; From d9d6777a7134210bbadf8961d87663f5c8668dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 15 Aug 2024 11:31:27 +0200 Subject: [PATCH 133/152] fix lint issues --- .../bind-to-validation.lit-directive.ts | 2 ++ .../data-type-flow-input.element.ts | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts index f6d48f8981..b7c81abca7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts @@ -12,6 +12,8 @@ class UmbBindToValidationDirective extends AsyncDirective { #el?: UmbFormControlMixinInterface; #validator?: UmbFormControlValidator; + // For Directives their arguments have to be defined on the Render method, despite them, not being used by the render method. In this case they are used by the update method. + // eslint-disable-next-line @typescript-eslint/no-unused-vars override render(host: UmbControllerHost, dataPath: string) { return nothing; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index b0166029f4..fd054f2f3a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -1,6 +1,6 @@ import { UMB_DATA_TYPE_PICKER_FLOW_MODAL } from '../../modals/index.js'; import { UMB_DATATYPE_WORKSPACE_MODAL } from '../../workspace/index.js'; -import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, property, state, PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -47,12 +47,6 @@ export class UmbInputDataTypeElement extends UmbFormControlMixin(UmbLitElement, constructor() { super(); - this.addValidator( - 'valueMissing', - () => UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, - () => this.hasAttribute('required') && this.value === '', - ); - this.#editDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL); new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL) @@ -72,6 +66,16 @@ export class UmbInputDataTypeElement extends UmbFormControlMixin(UmbLitElement, }); } + protected override firstUpdated(_changedProperties: PropertyValueMap | Map): void { + super.firstUpdated(_changedProperties); + + this.addValidator( + 'valueMissing', + () => UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + () => this.hasAttribute('required') && this.value === '', + ); + } + override focus() { this.shadowRoot?.querySelector('umb-ref-data-type')?.focus(); } From 1eef31da68eb9166bc8947c7482eda97a59513f3 Mon Sep 17 00:00:00 2001 From: Rick Butterfield Date: Thu, 15 Aug 2024 11:43:24 +0100 Subject: [PATCH 134/152] Fix reference to BlockGrid `layoutStylesheet` and add mock --- .../src/mocks/data/data-type/data-type.data.ts | 4 ++++ .../block/block-grid/context/block-grid-manager.context.ts | 2 +- .../components/block-type-card/block-type-card.element.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts index 2f45cdca03..72fc1695ab 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts @@ -702,6 +702,10 @@ export const data: Array = [ alias: 'blockGroups', value: [{ key: 'demo-block-group-id', name: 'Demo Blocks' }], }, + { + alias: 'layoutStylesheet', + value: '/wwwroot/css/umbraco-blockgridlayout.css' + }, { alias: 'blocks', value: [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index 15ae596750..eb0a2282d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -29,7 +29,7 @@ export class UmbBlockGridManagerContext< if (layoutStylesheet) { // Cause we await initAppUrl in setting the _editorConfiguration, we can trust the appUrl begin here. - return this.#appUrl! + removeInitialSlashFromPath(transformServerPathToClientPath(layoutStylesheet)); + return this.#appUrl! + transformServerPathToClientPath(layoutStylesheet); } return undefined; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts index 6be3216d4b..aafe657b82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts @@ -28,7 +28,7 @@ export class UmbBlockTypeCardElement extends UmbLitElement { value = transformServerPathToClientPath(value); if (value) { this.#init.then(() => { - this._iconFile = this.#appUrl + removeInitialSlashFromPath(value); + this._iconFile = this.#appUrl + value; }); } else { this._iconFile = undefined; From 73dbd28e1103663fefeeb2207520e0b86fff7a3a Mon Sep 17 00:00:00 2001 From: Rick Butterfield Date: Thu, 15 Aug 2024 11:44:02 +0100 Subject: [PATCH 135/152] Fix reference to BlockGrid `layoutStylesheet` and add mock From 4f683e234d4be6d076602b2de66a83f4fb2a7339 Mon Sep 17 00:00:00 2001 From: Rick Butterfield Date: Thu, 15 Aug 2024 11:58:31 +0100 Subject: [PATCH 136/152] Fix build errors --- .../block/block-grid/context/block-grid-manager.context.ts | 2 +- .../components/block-type-card/block-type-card.element.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index eb0a2282d5..b4095da252 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -1,7 +1,7 @@ import type { UmbBlockGridLayoutModel, UmbBlockGridTypeModel } from '../types.js'; import type { UmbBlockGridWorkspaceData } from '../index.js'; import { UmbArrayState, appendToFrozenArray, pushAtToUniqueArray } from '@umbraco-cms/backoffice/observable-api'; -import { removeInitialSlashFromPath, transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; +import { transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts index aafe657b82..12a34c4c68 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts @@ -6,7 +6,7 @@ import { html, customElement, property, state, ifDefined } from '@umbraco-cms/ba import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; -import { removeInitialSlashFromPath, transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; +import { transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; @customElement('umb-block-type-card') export class UmbBlockTypeCardElement extends UmbLitElement { From d1bc1e4644d755204eec361a8d6a348f213a212f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 15 Aug 2024 12:59:00 +0200 Subject: [PATCH 137/152] import type --- .../data-type-flow-input/data-type-flow-input.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index fd054f2f3a..f77d5c090b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -1,6 +1,6 @@ import { UMB_DATA_TYPE_PICKER_FLOW_MODAL } from '../../modals/index.js'; import { UMB_DATATYPE_WORKSPACE_MODAL } from '../../workspace/index.js'; -import { css, html, customElement, property, state, PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, property, state, type PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; From 130b1667ff6de976e5361b05a36fa2b54fd6d83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 15 Aug 2024 13:33:39 +0200 Subject: [PATCH 138/152] remove last slash from appUrl --- .../block/block-grid/context/block-grid-manager.context.ts | 4 ++-- .../components/block-type-card/block-type-card.element.ts | 6 +++--- src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts | 1 + .../utils/path/remove-initial-slash-from-path.function.ts | 2 +- .../utils/path/remove-last-slash-from-path.function.ts | 7 +++++++ 5 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-last-slash-from-path.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index b4095da252..134e48ac35 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -1,7 +1,7 @@ import type { UmbBlockGridLayoutModel, UmbBlockGridTypeModel } from '../types.js'; import type { UmbBlockGridWorkspaceData } from '../index.js'; import { UmbArrayState, appendToFrozenArray, pushAtToUniqueArray } from '@umbraco-cms/backoffice/observable-api'; -import { transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; +import { removeLastSlashFromPath, transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; @@ -29,7 +29,7 @@ export class UmbBlockGridManagerContext< if (layoutStylesheet) { // Cause we await initAppUrl in setting the _editorConfiguration, we can trust the appUrl begin here. - return this.#appUrl! + transformServerPathToClientPath(layoutStylesheet); + return removeLastSlashFromPath(this.#appUrl!) + transformServerPathToClientPath(layoutStylesheet); } return undefined; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts index 12a34c4c68..9edc38b487 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts @@ -6,13 +6,13 @@ import { html, customElement, property, state, ifDefined } from '@umbraco-cms/ba import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; -import { transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; +import { removeLastSlashFromPath, transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils'; @customElement('umb-block-type-card') export class UmbBlockTypeCardElement extends UmbLitElement { // #init: Promise; - #appUrl?: string; + #appUrl: string = ''; #itemManager = new UmbRepositoryItemsManager( this, @@ -28,7 +28,7 @@ export class UmbBlockTypeCardElement extends UmbLitElement { value = transformServerPathToClientPath(value); if (value) { this.#init.then(() => { - this._iconFile = this.#appUrl + value; + this._iconFile = removeLastSlashFromPath(this.#appUrl) + value; }); } else { this._iconFile = undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 82505e62be..6a16aa67e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -12,6 +12,7 @@ export * from './path/path-decode.function.js'; export * from './path/path-encode.function.js'; export * from './path/path-folder-name.function.js'; export * from './path/remove-initial-slash-from-path.function.js'; +export * from './path/remove-last-slash-from-path.function.js'; export * from './path/stored-path.function.js'; export * from './path/transform-server-path-to-client-path.function.js'; export * from './path/umbraco-path.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-initial-slash-from-path.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-initial-slash-from-path.function.ts index 64ab8b51c8..f2b26ac1c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-initial-slash-from-path.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-initial-slash-from-path.function.ts @@ -1,5 +1,5 @@ /** - * + * Removes the initial slash from a path, if the first character is a slash. * @param path */ export function removeInitialSlashFromPath(path: string) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-last-slash-from-path.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-last-slash-from-path.function.ts new file mode 100644 index 0000000000..5973746c29 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/remove-last-slash-from-path.function.ts @@ -0,0 +1,7 @@ +/** + * Remove the last slash from a path, if the last character is a slash. + * @param path + */ +export function removeLastSlashFromPath(path: string) { + return path.endsWith('/') ? path.slice(undefined, -1) : path; +} From b78a18acda3a307ff5a504f2a876778491b29b96 Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:18:36 +0200 Subject: [PATCH 139/152] fix object object issue --- .../entity-actions/export/document-type-export.action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts index c1912fe902..c28abdf792 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/document-type-export.action.ts @@ -8,7 +8,7 @@ export class UmbExportDocumentTypeEntityAction extends UmbEntityActionBase Date: Fri, 16 Aug 2024 11:26:51 +0200 Subject: [PATCH 140/152] lower case path --- .../validation/context/validation-messages.manager.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts index 87ee74e55c..6104c31dc8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation-messages.manager.ts @@ -24,6 +24,7 @@ export class UmbValidationMessagesManager { } messagesOfPathAndDescendant(path: string): Observable> { + path = path.toLowerCase(); // Find messages that starts with the given path, if the path is longer then require a dot or [ as the next character. using a more performant way than Regex: return this.#messages.asObservablePart((msgs) => msgs.filter( @@ -35,11 +36,13 @@ export class UmbValidationMessagesManager { } messagesOfTypeAndPath(type: UmbValidationMessageType, path: string): Observable> { + path = path.toLowerCase(); // Find messages that matches the given type and path. return this.#messages.asObservablePart((msgs) => msgs.filter((x) => x.type === type && x.path === path)); } hasMessagesOfPathAndDescendant(path: string): Observable { + path = path.toLowerCase(); return this.#messages.asObservablePart((msgs) => // Find messages that starts with the given path, if the path is longer then require a dot or [ as the next character. Using a more performant way than Regex: [NL] msgs.some( @@ -50,6 +53,7 @@ export class UmbValidationMessagesManager { ); } getHasMessagesOfPathAndDescendant(path: string): boolean { + path = path.toLowerCase(); return this.#messages .getValue() .some( @@ -60,7 +64,7 @@ export class UmbValidationMessagesManager { } addMessage(type: UmbValidationMessageType, path: string, body: string, key: string = UmbId.new()): void { - path = this.#translatePath(path) ?? path; + path = this.#translatePath(path.toLowerCase()) ?? path.toLowerCase(); // check if there is an existing message with the same path and type, and append the new messages: [NL] if (this.#messages.getValue().find((x) => x.type === type && x.path === path && x.body === body)) { return; @@ -69,7 +73,7 @@ export class UmbValidationMessagesManager { } addMessages(type: UmbValidationMessageType, path: string, bodies: Array): void { - path = this.#translatePath(path) ?? path; + path = this.#translatePath(path.toLowerCase()) ?? path.toLowerCase(); // filter out existing messages with the same path and type, and append the new messages: [NL] const existingMessages = this.#messages.getValue(); const newBodies = bodies.filter( @@ -82,6 +86,7 @@ export class UmbValidationMessagesManager { this.#messages.removeOne(key); } removeMessagesByTypeAndPath(type: UmbValidationMessageType, path: string): void { + path = path.toLowerCase(); this.#messages.filter((x) => !(x.type === type && x.path === path)); } removeMessagesByType(type: UmbValidationMessageType): void { From f9cce493859f8a19c168c9c15acbe2e202f97e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 11:38:49 +0200 Subject: [PATCH 141/152] validate variant name --- .../server-model-validator.context.ts | 2 +- .../bind-to-validation.lit-directive.ts | 42 ++++++++++++++++--- .../core/validation/translators/index.ts | 5 ++- ...s-validation-path-translator.controller.ts | 11 +++-- ...s-validation-path-translator.controller.ts | 34 +++++++++++++++ ...ata-path-property-value-filter.function.ts | 8 ++-- .../data-path-variant-filter.function.ts | 20 +++++++++ .../packages/core/validation/utils/index.ts | 1 + ...ace-split-view-variant-selector.element.ts | 17 +++++--- .../workspace/document-workspace.context.ts | 2 + 10 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts index 410483e6d4..d3793a2311 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts @@ -1,5 +1,5 @@ import type { UmbValidator } from '../interfaces/validator.interface.js'; -import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; +import { UmbDataPathPropertyValueFilter } from '../utils/index.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../const.js'; import { UMB_SERVER_MODEL_VALIDATOR_CONTEXT } from './server-model-validator.context-token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts index b7c81abca7..a23e79e1bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/directives/bind-to-validation.lit-directive.ts @@ -1,7 +1,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { AsyncDirective, directive, nothing, type ElementPart } from '@umbraco-cms/backoffice/external/lit'; import type { UmbFormControlMixinInterface } from '@umbraco-cms/backoffice/validation'; -import { UmbFormControlValidator } from '@umbraco-cms/backoffice/validation'; +import { UmbBindServerValidationToFormControl, UmbFormControlValidator } from '@umbraco-cms/backoffice/validation'; /** * The `bind to validation` directive connects the Form Control Element to closets Validation Context. @@ -11,21 +11,33 @@ class UmbBindToValidationDirective extends AsyncDirective { #dataPath?: string; #el?: UmbFormControlMixinInterface; #validator?: UmbFormControlValidator; + #msgBinder?: UmbBindServerValidationToFormControl; // For Directives their arguments have to be defined on the Render method, despite them, not being used by the render method. In this case they are used by the update method. // eslint-disable-next-line @typescript-eslint/no-unused-vars - override render(host: UmbControllerHost, dataPath: string) { + override render(host: UmbControllerHost, dataPath?: string, value?: unknown) { return nothing; } - override update(part: ElementPart, args: [UmbControllerHost, string]) { - if (this.#el !== part.element) { + override update(part: ElementPart, args: Parameters) { + if (!part.element) return nothing; + if (this.#el !== part.element || this.#host !== args[0] || this.#dataPath !== args[1]) { this.#host = args[0]; this.#dataPath = args[1]; - if (!this.#host || !this.#dataPath || !part.element) return nothing; this.#el = part.element as UmbFormControlMixinInterface; + + if (!this.#msgBinder && this.#dataPath) { + this.#msgBinder = new UmbBindServerValidationToFormControl(this.#host, this.#el as any, this.#dataPath); + } + this.#validator = new UmbFormControlValidator(this.#host, this.#el, this.#dataPath); } + + // If we have a msgBinder, then lets update the value no matter the other conditions. + if (this.#msgBinder) { + this.#msgBinder.value = args[2]; + } + return nothing; } @@ -34,6 +46,10 @@ class UmbBindToValidationDirective extends AsyncDirective { this.#validator?.destroy(); this.#validator = undefined; } + if (this.#msgBinder) { + this.#msgBinder.destroy(); + this.#msgBinder = undefined; + } this.#el = undefined; } @@ -44,10 +60,24 @@ class UmbBindToValidationDirective extends AsyncDirective { /** * @description * A Lit directive, which binds the validation state of the element to the Validation Context. + * + * The minimal binding can be established by just providing a host element: * @example: * ```js - * html``; + * html``; * ``` + * But can be extended with a dataPath, which is the path to the data holding the value. (if no server validation in this context, then this can be fictive and is then just used internal for remembering the validation state despite the element begin removed from the DOM.) + * @example: + * ```js + * html``; + * ``` + * + * Additional the value can be provided, which is then used to remove a server validation state, if the value is changed. + * @example: + * ```js + * html``; + * ``` + * */ export const umbBindToValidation = directive(UmbBindToValidationDirective); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts index 661a080599..2bffe6a146 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts @@ -1,3 +1,4 @@ -export type * from './validation-message-path-translator.interface.js'; -export * from './variant-values-validation-path-translator.controller.js'; export * from './validation-path-translator-base.controller.js'; +export * from './variant-values-validation-path-translator.controller.js'; +export * from './variants-validation-path-translator.controller.js'; +export type * from './validation-message-path-translator.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts index cdbc8b992a..cf9c60e273 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts @@ -1,10 +1,13 @@ -import { UmbDataPathPropertyValueFilter } from '../utils/data-path-property-value-filter.function.js'; +import { UmbDataPathPropertyValueFilter } from '../utils/index.js'; import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; +const InitialPathToMatch = '$.values['; +const InitialPathToMatchLength = InitialPathToMatch.length; + export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathTranslatorBase { translate(path: string) { if (!this._context) return; - if (path.indexOf('$.values[') !== 0) { + if (path.indexOf(InitialPathToMatch) !== 0) { // We do not handle this path. return false; } @@ -14,7 +17,7 @@ export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathT return false; } // retrieve the number from the message values index: [NL] - const index = parseInt(path.substring(9, pathEnd)); + const index = parseInt(path.substring(InitialPathToMatchLength, pathEnd)); if (isNaN(index)) { // index is not a number, this means its not a path we want to translate. [NL] @@ -26,6 +29,6 @@ export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathT const specificValue = data.values[index]; // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: - return '$.values[' + UmbDataPathPropertyValueFilter(specificValue) + path.substring(path.indexOf(']')); + return InitialPathToMatch + UmbDataPathPropertyValueFilter(specificValue) + path.substring(path.indexOf(']')); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts new file mode 100644 index 0000000000..ec56831849 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts @@ -0,0 +1,34 @@ +import { UmbDataPathVariantFilter } from '../utils/index.js'; +import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; + +const InitialPathToMatch = '$.variants['; +const InitialPathToMatchLength = InitialPathToMatch.length; + +export class UmbVariantsValidationPathTranslator extends UmbValidationPathTranslatorBase { + translate(path: string) { + if (!this._context) return; + if (path.indexOf(InitialPathToMatch) !== 0) { + // We do not handle this path. + return false; + } + const pathEnd = path.indexOf(']'); + if (pathEnd === -1) { + // We do not handle this path. + return false; + } + // retrieve the number from the message values index: [NL] + const index = parseInt(path.substring(InitialPathToMatchLength, pathEnd)); + + if (isNaN(index)) { + // index is not a number, this means its not a path we want to translate. [NL] + return false; + } + + // Get the data from the validation request, the context holds that for us: [NL] + const data = this._context.getTranslationData(); + + const specificValue = data.values[index]; + // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: + return InitialPathToMatch + UmbDataPathVariantFilter(specificValue) + path.substring(path.indexOf(']')); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts index 52953837cd..d67cb968a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts @@ -13,11 +13,11 @@ export function UmbDataPathPropertyValueFilter( ): string { // write a array of strings for each property, where alias must be present and culture and segment are optional const filters: Array = [`@.alias = '${value.alias}'`]; - if (value.culture) { - filters.push(`@.culture = '${value.culture}'`); + if (value.culture !== undefined) { + filters.push(`@.culture = ${value.culture ? `'${value.culture}'` : 'null'}`); } - if (value.segment) { - filters.push(`@.segment = '${value.segment}'`); + if (value.segment !== undefined) { + filters.push(`@.segment = ${value.segment ? `'${value.segment}'` : 'null'}`); } return `?(${filters.join(' && ')})`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts new file mode 100644 index 0000000000..64d14a9e21 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts @@ -0,0 +1,20 @@ +import type { UmbPartialSome } from '@umbraco-cms/backoffice/utils'; +import type { UmbVariantPropertyValueModel } from '@umbraco-cms/backoffice/variant'; + +/** + * Validation Data Path filter for Variant. + * write a JSON-Path filter similar to `?(@.culture == 'en-us' && @.segment == 'mySegment')` + * where segment are optional. + * @param value + * @returns + */ +export function UmbDataPathVariantFilter( + value: UmbPartialSome, 'segment'>, +): string { + // write a array of strings for each property, where culture must be present and segment is optional + const filters: Array = [`@.culture = ${value.culture ? `'${value.culture}'` : 'null'}`]; + if (value.segment !== undefined) { + filters.push(`@.segment = ${value.segment ? `'${value.segment}'` : 'null'}`); + } + return `?(${filters.join(' && ')})`; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts index f67191b0ce..5459d4a3e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts @@ -1,2 +1,3 @@ export * from './data-path-property-value-filter.function.js'; +export * from './data-path-variant-filter.function.js'; export * from './json-path.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts index c4bfbf0cab..77aa28ec21 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts @@ -12,6 +12,7 @@ import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UMB_PROPERTY_DATASET_CONTEXT, isNameablePropertyDatasetContext } from '@umbraco-cms/backoffice/property'; import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbDataPathVariantFilter, umbBindToValidation } from '@umbraco-cms/backoffice/validation'; type UmbDocumentVariantOption = { culture: string | null; @@ -45,6 +46,9 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement { @state() private _name?: string; + @state() + private _variantId?: UmbVariantId; + @state() private _variantDisplayName = ''; @@ -132,11 +136,11 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement { const workspaceContext = this.#splitViewContext.getWorkspaceContext(); if (!workspaceContext) return; - const variantId = this.#datasetContext.getVariantId(); + this._variantId = this.#datasetContext.getVariantId(); // Find the variant option matching this, to get the language name... - const culture = variantId.culture; - const segment = variantId.segment; + const culture = this._variantId.culture; + const segment = this._variantId.segment; this.observe( workspaceContext.variantOptions, @@ -209,12 +213,14 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement { } override render() { - return html` + return this._variantId + ? html` ${ @@ -287,7 +293,8 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement { : nothing }
- `; + ` + : nothing; } static override styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 956366c908..145ac29372 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -56,6 +56,7 @@ import { UmbServerModelValidatorContext, UmbValidationContext, UmbVariantValuesValidationPathTranslator, + UmbVariantsValidationPathTranslator, } from '@umbraco-cms/backoffice/validation'; import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; @@ -166,6 +167,7 @@ export class UmbDocumentWorkspaceContext this.addValidationContext(new UmbValidationContext(this).provide()); new UmbVariantValuesValidationPathTranslator(this); + new UmbVariantsValidationPathTranslator(this); this.observe(this.contentTypeUnique, (unique) => this.structure.loadType(unique)); this.observe(this.varies, (varies) => (this.#varies = varies)); From 50924c73ee8cad8253563f78177cbcd5e02ff0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 12:25:16 +0200 Subject: [PATCH 142/152] mark required --- .../workspace-split-view-variant-selector.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts index 77aa28ec21..e6c0d58469 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts @@ -220,6 +220,7 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement { label=${this.localize.term('placeholders_entername')} .value=${this._name ?? ''} @input=${this.#handleInput} + required ${umbBindToValidation(this, `$.variants[${UmbDataPathVariantFilter(this._variantId)}].name`, this._name ?? '')} ${umbFocus()} > From bb0fc3bbb1929bf937869c81ea9876abd013b77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 13:30:50 +0200 Subject: [PATCH 143/152] validate data-type --- .../data-type-workspace-editor.element.ts | 5 +- ...ata-type-details-workspace-view.element.ts | 62 ++++++++++--------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts index cdea583b3c..413eca4ee8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts @@ -4,6 +4,7 @@ import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element'; import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; +import { umbBindToValidation } from '@umbraco-cms/backoffice/validation'; /** * @element umb-data-type-workspace-editor * @description - Element for displaying the Data Type Workspace edit route. @@ -54,8 +55,10 @@ export class UmbDataTypeWorkspaceEditorElement extends UmbLitElement { `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts index b107d79176..b6f5ba92a0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts @@ -4,6 +4,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UMB_MODAL_MANAGER_CONTEXT, UMB_PROPERTY_EDITOR_UI_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; +import { umbBindToValidation } from '@umbraco-cms/backoffice/validation'; @customElement('umb-data-type-details-workspace-view') export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement implements UmbWorkspaceViewElement { @@ -71,11 +72,9 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement im override render() { return html` - - ${this._propertyEditorUiAlias && this._propertyEditorSchemaAlias - ? this.#renderPropertyEditorReference() - : this.#renderChooseButton()} - + ${this._propertyEditorUiAlias && this._propertyEditorSchemaAlias + ? this.#renderPropertyEditorReference() + : this.#renderChooseButton()} ${this.#renderSettings()} `; @@ -90,37 +89,44 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement im `; } + // Notice, we have implemented a property-layout for each states of the property editor ui picker, in this way the validation message gets removed once the choose-button is gone. (As we are missing ability to detect if elements got removed from DOM)[NL] #renderChooseButton() { return html` - + + + `; } #renderPropertyEditorReference() { if (!this._propertyEditorUiAlias || !this._propertyEditorSchemaAlias) return nothing; return html` - - ${this._propertyEditorUiIcon - ? html`` - : nothing} - - - - + + + ${this._propertyEditorUiIcon + ? html`` + : nothing} + + + + + `; } From 7f0a0133bbfc3714a7f540baacee0e51fc1b690c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 13:42:14 +0200 Subject: [PATCH 144/152] refactor translators --- ...stract-array-path-translator.controller.ts | 41 +++++++++++++++++++ .../core/validation/translators/index.ts | 1 + ...s-validation-path-translator.controller.ts | 36 ++++------------ ...s-validation-path-translator.controller.ts | 36 ++++------------ 4 files changed, 58 insertions(+), 56 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts new file mode 100644 index 0000000000..61e07f23bf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts @@ -0,0 +1,41 @@ +import { UmbDataPathVariantFilter } from '../utils/index.js'; +import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export abstract class UmbAbstractArrayValidationPathTranslator extends UmbValidationPathTranslatorBase { + #initialPathToMatch: string; + + constructor(host: UmbControllerHost, initialPathToMatch: string) { + super(host); + + this.#initialPathToMatch = initialPathToMatch; + } + translate(path: string) { + if (!this._context) return; + if (path.indexOf(this.#initialPathToMatch) !== 0) { + // We do not handle this path. + return false; + } + const pathEnd = path.indexOf(']'); + if (pathEnd === -1) { + // We do not handle this path. + return false; + } + // retrieve the number from the message values index: [NL] + const index = parseInt(path.substring(this.#initialPathToMatch.length, pathEnd)); + + if (isNaN(index)) { + // index is not a number, this means its not a path we want to translate. [NL] + return false; + } + + // Get the data from the validation request, the context holds that for us: [NL] + const data = this.getDataFromIndex(index); + + if (!data) return false; + // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: + return this.#initialPathToMatch + UmbDataPathVariantFilter(data as any) + path.substring(path.indexOf(']')); + } + + abstract getDataFromIndex(index: number): unknown | undefined; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts index 2bffe6a146..9192c2b141 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/index.ts @@ -1,4 +1,5 @@ export * from './validation-path-translator-base.controller.js'; +export * from './abstract-array-path-translator.controller.js'; export * from './variant-values-validation-path-translator.controller.js'; export * from './variants-validation-path-translator.controller.js'; export type * from './validation-message-path-translator.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts index cf9c60e273..171b0388e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts @@ -1,34 +1,14 @@ -import { UmbDataPathPropertyValueFilter } from '../utils/index.js'; -import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; +import { UmbAbstractArrayValidationPathTranslator } from './abstract-array-path-translator.controller.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -const InitialPathToMatch = '$.values['; -const InitialPathToMatchLength = InitialPathToMatch.length; +export class UmbVariantValuesValidationPathTranslator extends UmbAbstractArrayValidationPathTranslator { + constructor(host: UmbControllerHost) { + super(host, '$.values['); + } -export class UmbVariantValuesValidationPathTranslator extends UmbValidationPathTranslatorBase { - translate(path: string) { + getDataFromIndex(index: number) { if (!this._context) return; - if (path.indexOf(InitialPathToMatch) !== 0) { - // We do not handle this path. - return false; - } - const pathEnd = path.indexOf(']'); - if (pathEnd === -1) { - // We do not handle this path. - return false; - } - // retrieve the number from the message values index: [NL] - const index = parseInt(path.substring(InitialPathToMatchLength, pathEnd)); - - if (isNaN(index)) { - // index is not a number, this means its not a path we want to translate. [NL] - return false; - } - - // Get the data from the validation request, the context holds that for us: [NL] const data = this._context.getTranslationData(); - - const specificValue = data.values[index]; - // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: - return InitialPathToMatch + UmbDataPathPropertyValueFilter(specificValue) + path.substring(path.indexOf(']')); + return data.values[index]; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts index ec56831849..c0e453d68c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts @@ -1,34 +1,14 @@ -import { UmbDataPathVariantFilter } from '../utils/index.js'; -import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; +import { UmbAbstractArrayValidationPathTranslator } from './abstract-array-path-translator.controller.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -const InitialPathToMatch = '$.variants['; -const InitialPathToMatchLength = InitialPathToMatch.length; +export class UmbVariantValuesValidationPathTranslator extends UmbAbstractArrayValidationPathTranslator { + constructor(host: UmbControllerHost) { + super(host, '$.variants['); + } -export class UmbVariantsValidationPathTranslator extends UmbValidationPathTranslatorBase { - translate(path: string) { + getDataFromIndex(index: number) { if (!this._context) return; - if (path.indexOf(InitialPathToMatch) !== 0) { - // We do not handle this path. - return false; - } - const pathEnd = path.indexOf(']'); - if (pathEnd === -1) { - // We do not handle this path. - return false; - } - // retrieve the number from the message values index: [NL] - const index = parseInt(path.substring(InitialPathToMatchLength, pathEnd)); - - if (isNaN(index)) { - // index is not a number, this means its not a path we want to translate. [NL] - return false; - } - - // Get the data from the validation request, the context holds that for us: [NL] const data = this._context.getTranslationData(); - - const specificValue = data.values[index]; - // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: - return InitialPathToMatch + UmbDataPathVariantFilter(specificValue) + path.substring(path.indexOf(']')); + return data.variants[index]; } } From 023cf84dea8d1df58b824bc508a3604fa24a6623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 14:18:02 +0200 Subject: [PATCH 145/152] refactor query + translators --- .../block-list-entry.element.ts | 6 +-- ...y-validation-path-translator.controller.ts | 4 +- ...a-validation-path-translator.controller.ts | 39 ++++--------------- ... data-path-element-data-query.function.ts} | 4 +- .../packages/block/block/validation/index.ts | 2 +- .../edit/content-editor-properties.element.ts | 4 +- .../server-model-validator.context.ts | 4 +- ...stract-array-path-translator.controller.ts | 7 ++-- ...s-validation-path-translator.controller.ts | 3 +- ...s-validation-path-translator.controller.ts | 5 ++- ...ata-path-property-value-query.function.ts} | 4 +- ...ts => data-path-variant-query.function.ts} | 4 +- .../packages/core/validation/utils/index.ts | 4 +- ...ace-split-view-variant-selector.element.ts | 4 +- .../property-editor-config.element.ts | 4 +- 15 files changed, 39 insertions(+), 59 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/block/block/validation/{data-path-element-data-filter.function.ts => data-path-element-data-query.function.ts} (76%) rename src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/{data-path-property-value-filter.function.ts => data-path-property-value-query.function.ts} (89%) rename src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/{data-path-variant-filter.function.ts => data-path-variant-query.function.ts} (89%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index d0c0dccfc6..ef2dd27628 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -11,7 +11,7 @@ import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js'; import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js'; import { UmbObserveValidationStateController } from '@umbraco-cms/backoffice/validation'; -import { UmbDataPathBlockElementDataFilter } from '@umbraco-cms/backoffice/block'; +import { UmbDataPathBlockElementDataQuery } from '@umbraco-cms/backoffice/block'; /** * @element umb-block-list-entry @@ -38,7 +38,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper new UmbObserveValidationStateController( this, - `$.contentData[${UmbDataPathBlockElementDataFilter({ udi: value })}]`, + `$.contentData[${UmbDataPathBlockElementDataQuery({ udi: value })}]`, (hasMessages) => { this._contentInvalid = hasMessages; this._blockViewProps.contentInvalid = hasMessages; @@ -167,7 +167,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper // Observe settings validation state: new UmbObserveValidationStateController( this, - `$.settingsData[${UmbDataPathBlockElementDataFilter(settings)}]`, + `$.settingsData[${UmbDataPathBlockElementDataQuery(settings)}]`, (hasMessages) => { this._settingsInvalid = hasMessages; this._blockViewProps.settingsInvalid = hasMessages; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts index 868ed7f339..096eec3967 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-property-validation-path-translator.controller.ts @@ -1,7 +1,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { GetPropertyNameFromPath, - UmbDataPathPropertyValueFilter, + UmbDataPathPropertyValueQuery, UmbValidationPathTranslatorBase, } from '@umbraco-cms/backoffice/validation'; @@ -23,6 +23,6 @@ export class UmbBlockElementDataValidationPathTranslator extends UmbValidationPa const specificValue = { alias: key }; // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: //return '$.values' + UmbVariantValuesValidationPathTranslator(specificValue) + path.substring(path.indexOf(']')); - return '$.values[' + UmbDataPathPropertyValueFilter(specificValue) + '.value'; + return '$.values[' + UmbDataPathPropertyValueQuery(specificValue) + '.value'; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts index 7bb1e4e8fb..4ba0cf60f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/block-data-validation-path-translator.controller.ts @@ -1,46 +1,23 @@ -import { UmbDataPathBlockElementDataFilter } from './data-path-element-data-filter.function.js'; +import { UmbDataPathBlockElementDataQuery } from './data-path-element-data-query.function.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbValidationPathTranslatorBase } from '@umbraco-cms/backoffice/validation'; +import { UmbAbstractArrayValidationPathTranslator } from '@umbraco-cms/backoffice/validation'; -export class UmbBlockElementDataValidationPathTranslator extends UmbValidationPathTranslatorBase { +export class UmbBlockElementDataValidationPathTranslator extends UmbAbstractArrayValidationPathTranslator { #propertyName: string; - #pathStart: string; constructor(host: UmbControllerHost, propertyName: 'contentData' | 'settingsData') { - super(host); + super(host, '$.' + propertyName + '[', UmbDataPathBlockElementDataQuery); this.#propertyName = propertyName; - this.#pathStart = '$.' + propertyName + '['; } - translate(path: string) { + getDataFromIndex(index: number) { if (!this._context) return; - if (path.indexOf(this.#pathStart) !== 0) { - // We do not handle this path. - return false; - } - const startLength = this.#pathStart.length; - const pathEnd = path.indexOf(']', startLength); - if (pathEnd === -1) { - // We do not handle this path. - return false; - } - // retrieve the number from the message values index: [NL] - const index = parseInt(path.substring(startLength, pathEnd)); - - if (isNaN(index)) { - // index is not a number, this means its not a path we want to translate. [NL] - return false; - } - - // Get the data from the validation request, the context holds that for us: [NL] const data = this._context.getTranslationData(); - - const specificValue = data[this.#propertyName][index]; - if (!specificValue || !specificValue.udi) { + const entry = data[this.#propertyName][index]; + if (!entry || !entry.udi) { console.log('block did not have UDI', this.#propertyName, index, data); return false; } - // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: - return this.#pathStart + UmbDataPathBlockElementDataFilter(specificValue) + path.substring(path.indexOf(']')); + return entry; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-query.function.ts similarity index 76% rename from src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-query.function.ts index 51c71b394e..c65d92fb82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/data-path-element-data-query.function.ts @@ -1,13 +1,13 @@ import type { UmbBlockDataType } from '../types.js'; /** - * Validation Data Path filter for Block Element Data. + * Validation Data Path Query generator for Block Element Data. * write a JSON-Path filter similar to `?(@.udi = 'my-udi://1234')` * @param udi {string} - The udi of the block Element data. * @param data {{udi: string}} - A data object with the udi property. * @returns */ -export function UmbDataPathBlockElementDataFilter(data: Pick): string { +export function UmbDataPathBlockElementDataQuery(data: Pick): string { // write a array of strings for each property, where alias must be present and culture and segment are optional //const filters: Array = [`@.udi = '${udi}'`]; //return `?(${filters.join(' && ')})`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts index 34a563d9d5..331352a0d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/validation/index.ts @@ -1,2 +1,2 @@ export * from './block-data-validation-path-translator.controller.js'; -export * from './data-path-element-data-filter.function.js'; +export * from './data-path-element-data-query.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts index a52eaf06d1..1399820201 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/content-editor-properties.element.ts @@ -7,7 +7,7 @@ import type { } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbDataPathPropertyValueFilter } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathPropertyValueQuery } from '@umbraco-cms/backoffice/validation'; import { UMB_PROPERTY_STRUCTURE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; @@ -58,7 +58,7 @@ export class UmbContentWorkspaceViewEditPropertiesElement extends UmbLitElement if (!this.#variantId || !this._propertyStructure) return; this._dataPaths = this._propertyStructure.map( (property) => - `$.values[${UmbDataPathPropertyValueFilter({ + `$.values[${UmbDataPathPropertyValueQuery({ alias: property.alias, culture: property.variesByCulture ? this.#variantId!.culture : null, segment: property.variesBySegment ? this.#variantId!.segment : null, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts index d3793a2311..bee1c2369c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/server-model-validator.context.ts @@ -1,5 +1,5 @@ import type { UmbValidator } from '../interfaces/validator.interface.js'; -import { UmbDataPathPropertyValueFilter } from '../utils/index.js'; +import { UmbDataPathPropertyValueQuery } from '../utils/index.js'; import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js'; import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../const.js'; import { UMB_SERVER_MODEL_VALIDATOR_CONTEXT } from './server-model-validator.context-token.js'; @@ -87,7 +87,7 @@ export class UmbServerModelValidatorContext const uniqueMissingProperties = [...new Set(errorBody.missingProperties)]; uniqueMissingProperties.forEach((alias) => { this.#data.variants.forEach((variant: any) => { - const path = `$.values[${UmbDataPathPropertyValueFilter({ + const path = `$.values[${UmbDataPathPropertyValueQuery({ alias: alias, culture: variant.culture, segment: variant.segment, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts index 61e07f23bf..121310af55 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/abstract-array-path-translator.controller.ts @@ -1,14 +1,15 @@ -import { UmbDataPathVariantFilter } from '../utils/index.js'; import { UmbValidationPathTranslatorBase } from './validation-path-translator-base.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export abstract class UmbAbstractArrayValidationPathTranslator extends UmbValidationPathTranslatorBase { #initialPathToMatch: string; + #queryMethod: (data: unknown) => string; - constructor(host: UmbControllerHost, initialPathToMatch: string) { + constructor(host: UmbControllerHost, initialPathToMatch: string, queryMethod: (data: any) => string) { super(host); this.#initialPathToMatch = initialPathToMatch; + this.#queryMethod = queryMethod; } translate(path: string) { if (!this._context) return; @@ -34,7 +35,7 @@ export abstract class UmbAbstractArrayValidationPathTranslator extends UmbValida if (!data) return false; // replace the values[ number ] with JSON-Path filter values[@.(...)], continues by the rest of the path: - return this.#initialPathToMatch + UmbDataPathVariantFilter(data as any) + path.substring(path.indexOf(']')); + return this.#initialPathToMatch + this.#queryMethod(data) + path.substring(path.indexOf(']')); } abstract getDataFromIndex(index: number): unknown | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts index 171b0388e1..f3e1020607 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variant-values-validation-path-translator.controller.ts @@ -1,9 +1,10 @@ +import { UmbDataPathPropertyValueQuery } from '../utils/data-path-property-value-query.function.js'; import { UmbAbstractArrayValidationPathTranslator } from './abstract-array-path-translator.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbVariantValuesValidationPathTranslator extends UmbAbstractArrayValidationPathTranslator { constructor(host: UmbControllerHost) { - super(host, '$.values['); + super(host, '$.values[', UmbDataPathPropertyValueQuery); } getDataFromIndex(index: number) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts index c0e453d68c..545e2a5488 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/translators/variants-validation-path-translator.controller.ts @@ -1,9 +1,10 @@ +import { UmbDataPathVariantQuery } from '../utils/data-path-variant-query.function.js'; import { UmbAbstractArrayValidationPathTranslator } from './abstract-array-path-translator.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export class UmbVariantValuesValidationPathTranslator extends UmbAbstractArrayValidationPathTranslator { +export class UmbVariantsValidationPathTranslator extends UmbAbstractArrayValidationPathTranslator { constructor(host: UmbControllerHost) { - super(host, '$.variants['); + super(host, '$.variants[', UmbDataPathVariantQuery); } getDataFromIndex(index: number) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-query.function.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-query.function.ts index d67cb968a9..d184f6e205 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-property-value-query.function.ts @@ -2,13 +2,13 @@ import type { UmbPartialSome } from '@umbraco-cms/backoffice/utils'; import type { UmbVariantPropertyValueModel } from '@umbraco-cms/backoffice/variant'; /** - * Validation Data Path filter for Property Value. + * Validation Data Path Query generator for Property Value. * write a JSON-Path filter similar to `?(@.alias = 'myAlias' && @.culture == 'en-us' && @.segment == 'mySegment')` * where culture and segment are optional * @param value * @returns */ -export function UmbDataPathPropertyValueFilter( +export function UmbDataPathPropertyValueQuery( value: UmbPartialSome, 'culture' | 'segment'>, ): string { // write a array of strings for each property, where alias must be present and culture and segment are optional diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-query.function.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-query.function.ts index 64d14a9e21..25666269cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-filter.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/data-path-variant-query.function.ts @@ -2,13 +2,13 @@ import type { UmbPartialSome } from '@umbraco-cms/backoffice/utils'; import type { UmbVariantPropertyValueModel } from '@umbraco-cms/backoffice/variant'; /** - * Validation Data Path filter for Variant. + * Validation Data Path query generator for Variant. * write a JSON-Path filter similar to `?(@.culture == 'en-us' && @.segment == 'mySegment')` * where segment are optional. * @param value * @returns */ -export function UmbDataPathVariantFilter( +export function UmbDataPathVariantQuery( value: UmbPartialSome, 'segment'>, ): string { // write a array of strings for each property, where culture must be present and segment is optional diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts index 5459d4a3e9..52e7d2c3c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/utils/index.ts @@ -1,3 +1,3 @@ -export * from './data-path-property-value-filter.function.js'; -export * from './data-path-variant-filter.function.js'; +export * from './data-path-property-value-query.function.js'; +export * from './data-path-variant-query.function.js'; export * from './json-path.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts index e6c0d58469..7a9bce0fc7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.ts @@ -12,7 +12,7 @@ import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UMB_PROPERTY_DATASET_CONTEXT, isNameablePropertyDatasetContext } from '@umbraco-cms/backoffice/property'; import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbDataPathVariantFilter, umbBindToValidation } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathVariantQuery, umbBindToValidation } from '@umbraco-cms/backoffice/validation'; type UmbDocumentVariantOption = { culture: string | null; @@ -221,7 +221,7 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement { .value=${this._name ?? ''} @input=${this.#handleInput} required - ${umbBindToValidation(this, `$.variants[${UmbDataPathVariantFilter(this._variantId)}].name`, this._name ?? '')} + ${umbBindToValidation(this, `$.variants[${UmbDataPathVariantQuery(this._variantId)}].name`, this._name ?? '')} ${umbFocus()} > ${ diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts index 0466721c24..2fb4bb1ffc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/property-editor-config/property-editor-config.element.ts @@ -3,7 +3,7 @@ import { html, customElement, state, ifDefined, repeat } from '@umbraco-cms/back import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyEditorSettingsProperty } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbDataPathPropertyValueFilter } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathPropertyValueQuery } from '@umbraco-cms/backoffice/validation'; /** * @element umb-property-editor-config @@ -46,7 +46,7 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement { (property) => property.alias, (property) => html` Date: Fri, 16 Aug 2024 14:33:38 +0200 Subject: [PATCH 146/152] runMandatoryValidationForSaveData --- .../workspace/document-workspace.context.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 145ac29372..0206833109 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -53,6 +53,9 @@ import { } from '@umbraco-cms/backoffice/entity-action'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { + UMB_VALIDATION_CONTEXT, + UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + UmbDataPathVariantQuery, UmbServerModelValidatorContext, UmbValidationContext, UmbVariantValuesValidationPathTranslator, @@ -635,6 +638,7 @@ export class UmbDocumentWorkspaceContext culture = selected[0]; const variantId = UmbVariantId.FromString(culture); const saveData = this.#buildSaveData([variantId]); + await this.#runMandatoryValidationForSaveData(saveData); await this.#performSaveOrCreate(saveData); } @@ -678,6 +682,7 @@ export class UmbDocumentWorkspaceContext } const saveData = this.#buildSaveData(variantIds); + await this.#runMandatoryValidationForSaveData(saveData); // Create the validation repository if it does not exist. (we first create this here when we need it) [NL] this.#validationRepository ??= new UmbDocumentValidationRepository(this); @@ -715,6 +720,24 @@ export class UmbDocumentWorkspaceContext ); } + async #runMandatoryValidationForSaveData(saveData: UmbDocumentDetailModel) { + // Check that the data is valid before we save it. + // Check variants have a name: + const variantWithoutAName = saveData.variants.filter((x) => !x.name); + console.log('variantWithoutAName', variantWithoutAName); + if (variantWithoutAName.length > 0) { + const validationContext = await this.getContext(UMB_VALIDATION_CONTEXT); + variantWithoutAName.forEach((variant) => { + validationContext.messages.addMessage( + 'client', + `$.variants[${UmbDataPathVariantQuery(variant)}].name`, + UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + ); + }); + throw new Error('All variants must have a name'); + } + } + async #performSaveAndPublish(variantIds: Array, saveData: UmbDocumentDetailModel): Promise { const unique = this.getUnique(); if (!unique) throw new Error('Unique is missing'); @@ -765,6 +788,7 @@ export class UmbDocumentWorkspaceContext } const saveData = this.#buildSaveData(variantIds); + await this.#runMandatoryValidationForSaveData(saveData); return await this.#performSaveOrCreate(saveData); } From 1d4868f88991721d46d13e3cf26ec141b2ec0d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 14:43:52 +0200 Subject: [PATCH 147/152] remove log --- .../documents/workspace/document-workspace.context.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 0206833109..719e6d6719 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -723,11 +723,10 @@ export class UmbDocumentWorkspaceContext async #runMandatoryValidationForSaveData(saveData: UmbDocumentDetailModel) { // Check that the data is valid before we save it. // Check variants have a name: - const variantWithoutAName = saveData.variants.filter((x) => !x.name); - console.log('variantWithoutAName', variantWithoutAName); - if (variantWithoutAName.length > 0) { + const variantsWithoutAName = saveData.variants.filter((x) => !x.name); + if (variantsWithoutAName.length > 0) { const validationContext = await this.getContext(UMB_VALIDATION_CONTEXT); - variantWithoutAName.forEach((variant) => { + variantsWithoutAName.forEach((variant) => { validationContext.messages.addMessage( 'client', `$.variants[${UmbDataPathVariantQuery(variant)}].name`, From d7b4b2a81c6cf10aae0b23b8c30a75f2a0044292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 16 Aug 2024 16:07:36 +0200 Subject: [PATCH 148/152] validate document-type --- .../input-with-alias.element.ts | 33 ++++++++++++++++--- .../content-type-structure-manager.class.ts | 6 ++-- .../data-type-flow-input.element.ts | 2 +- .../document-type-workspace-editor.element.ts | 7 ++-- .../document-type-workspace.context.ts | 3 ++ 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-with-alias/input-with-alias.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-with-alias/input-with-alias.element.ts index cc2bb9308e..36e616ae61 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-with-alias/input-with-alias.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-with-alias/input-with-alias.element.ts @@ -1,19 +1,24 @@ -import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { type PropertyValueMap, css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; -import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-input-with-alias') -export class UmbInputWithAliasElement extends UmbFormControlMixin(UmbLitElement) { +export class UmbInputWithAliasElement extends UmbFormControlMixin( + UmbLitElement, +) { @property({ type: String }) label: string = ''; @property({ type: String, reflect: false }) alias?: string; + @property({ type: Boolean, reflect: true }) + required: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'alias-readonly' }) aliasReadonly = false; @@ -23,7 +28,15 @@ export class UmbInputWithAliasElement extends UmbFormControlMixin | Map): void { + super.firstUpdated(_changedProperties); + + this.addValidator( + 'valueMissing', + () => UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + () => this.required && !this.value, + ); + this.shadowRoot?.querySelectorAll('uui-input').forEach((x) => this.addFormControlElement(x)); } @@ -64,6 +77,13 @@ export class UmbInputWithAliasElement extends UmbFormControlMixin + @input=${this.#onNameChange} + ?required=${this.required}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index f6511f43cc..268bea7841 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -112,12 +112,14 @@ export class UmbContentTypeStructureManager< if (!contentType || !contentType.unique) throw new Error('Could not find the Content Type to save'); const { error, data } = await this.#repository.save(contentType); - if (error || !data) return { error, data }; + if (error || !data) { + throw error?.message ?? 'Repository did not return data after save.'; + } // Update state with latest version: this.#contentTypes.updateOne(contentType.unique, data); - return { error, data }; + return data; } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index f77d5c090b..f30d2a64d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -72,7 +72,7 @@ export class UmbInputDataTypeElement extends UmbFormControlMixin(UmbLitElement, this.addValidator( 'valueMissing', () => UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, - () => this.hasAttribute('required') && this.value === '', + () => this.hasAttribute('required') && !this.value, ); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace-editor.element.ts index c41cabacbe..18f4a74ced 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace-editor.element.ts @@ -4,6 +4,7 @@ import { umbFocus, UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UMB_MODAL_MANAGER_CONTEXT, UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import type { UUITextareaElement } from '@umbraco-cms/backoffice/external/uui'; +import { umbBindToValidation } from '@umbraco-cms/backoffice/validation'; @customElement('umb-document-type-workspace-editor') export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement { @@ -86,10 +87,12 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts index d332df01e3..65ab3f5c5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -30,6 +30,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; import type { UmbRoutableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import type { UmbPathPatternTypeAsEncodedParamsType } from '@umbraco-cms/backoffice/router'; +import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; type EntityType = UmbDocumentTypeDetailModel; export class UmbDocumentTypeWorkspaceContext @@ -79,6 +80,8 @@ export class UmbDocumentTypeWorkspaceContext constructor(host: UmbControllerHost) { super(host, 'Umb.Workspace.DocumentType'); + this.addValidationContext(new UmbValidationContext(this).provide()); + // General for content types: //this.data = this.structure.ownerContentType; From f7c6d553658a464e6a9f0a09012411d8fba6eb85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sun, 18 Aug 2024 22:21:01 +0200 Subject: [PATCH 149/152] describe Binders --- .../src/packages/core/validation/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index b7d458a5c6..90bdcc7220 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -84,6 +84,8 @@ This Validator binds a Form Control Element with the Validation Context. When th Notice this one also comes as a Lit Directive called `umbBindToValidation`. +Also notice this does not bind server validation to the Form Control, see `UmbBindServerValidationToFormControl` + ### Server Model Validator This Validator can asks a end-point for validation of the model. @@ -117,3 +119,11 @@ This is completely fine, as messages can be partly translated and then enhanced This fact enables a property to observe if there is any Message Paths that start with the same path as the Data Path for the Property. In this was a property can know that it contains a Validation Message without the Message Path begin completely translated. + + +## Binders + +Validators represent a component of the Validation to be considered, but it does not represent other messages of its path. +To display messages from a given data-path, a Binder is needed. We bring a few to make this happen: + +UmbBindServerValidationToFormControl From 7828f0fb0274a78ff4de140c837b6470abc7f473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 19 Aug 2024 09:30:26 +0200 Subject: [PATCH 150/152] maxAllowed --- .../block/block-grid/context/block-grid-entries.context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts index 53aa90fd34..948c21b35a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts @@ -98,9 +98,9 @@ export class UmbBlockGridEntriesContext getMaxAllowed() { if (this.#areaKey) { - return this.#areaType?.minAllowed ?? Infinity; + return this.#areaType?.maxAllowed ?? Infinity; } - return this._manager?.getMinAllowed() ?? Infinity; + return this._manager?.getMaxAllowed() ?? Infinity; } getLayoutContainerElement() { From d43ed82652e9065da416addf7b386d61503f899d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 19 Aug 2024 10:19:11 +0200 Subject: [PATCH 151/152] change icons --- .../examples/icons/files/icon-bomb.ts | 1 - .../files/{icon-bones.ts => icon-heart.ts} | 6 +++--- .../examples/icons/files/icon-wand.ts | 19 +++++++++++++++++++ .../examples/icons/icons-dashboard.ts | 6 ++++-- .../examples/icons/icons-dictionary.ts | 8 ++++---- 5 files changed, 30 insertions(+), 10 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts rename src/Umbraco.Web.UI.Client/examples/icons/files/{icon-bones.ts => icon-heart.ts} (50%) create mode 100644 src/Umbraco.Web.UI.Client/examples/icons/files/icon-wand.ts diff --git a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts deleted file mode 100644 index fe1e2907ac..0000000000 --- a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bomb.ts +++ /dev/null @@ -1 +0,0 @@ -export default ``; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-heart.ts similarity index 50% rename from src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts rename to src/Umbraco.Web.UI.Client/examples/icons/files/icon-heart.ts index 1b37bf8a17..d8dd6c2be4 100644 --- a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-bones.ts +++ b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-heart.ts @@ -1,6 +1,6 @@ export default ` stroke-linecap="round" stroke-linejoin="round" > - + -`; \ No newline at end of file +`; diff --git a/src/Umbraco.Web.UI.Client/examples/icons/files/icon-wand.ts b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-wand.ts new file mode 100644 index 0000000000..c9425fbae4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/icons/files/icon-wand.ts @@ -0,0 +1,19 @@ +export default ` + + + + + + + + +`; diff --git a/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts b/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts index 5917503b48..5307bde407 100644 --- a/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts +++ b/src/Umbraco.Web.UI.Client/examples/icons/icons-dashboard.ts @@ -2,14 +2,16 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; +// eslint-disable-next-line local-rules/enforce-umb-prefix-on-element-name @customElement('example-icons-dashboard') +// eslint-disable-next-line local-rules/umb-class-prefix export class ExampleIconsDashboard extends UmbElementMixin(LitElement) { override render() { return html`

Custom icons:

- - + +
`; } diff --git a/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts b/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts index 2472d6bf5d..b91cd341ba 100644 --- a/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts +++ b/src/Umbraco.Web.UI.Client/examples/icons/icons-dictionary.ts @@ -1,10 +1,10 @@ export default [ { - name: 'my-icon-bomb', - path: () => import('./files/icon-bomb.js'), + name: 'my-icon-wand', + path: () => import('./files/icon-wand.js'), }, { - name: 'my-icon-bones', - path: () => import('./files/icon-bones.js'), + name: 'my-icon-heart', + path: () => import('./files/icon-heart.js'), }, ]; From 6fa2c1b5bb7b34c8315a61857bb40a8045cdc98b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 19 Aug 2024 11:10:42 +0200 Subject: [PATCH 152/152] request reload children after import --- .../import/document-type-import.action.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts index 636083f332..09cc224c30 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/document-type-import.action.ts @@ -1,5 +1,6 @@ import { UMB_DOCUMENT_TYPE_IMPORT_MODAL } from './modal/document-type-import-modal.token.js'; -import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbEntityActionBase, UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/entity-action'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; export class UmbImportDocumentTypeEntityAction extends UmbEntityActionBase { @@ -9,6 +10,14 @@ export class UmbImportDocumentTypeEntityAction extends UmbEntityActionBase {}); + + const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadChildrenOfEntityEvent({ + unique: this.args.unique, + entityType: this.args.entityType, + }); + + actionEventContext.dispatchEvent(event); } }