diff --git a/src/Umbraco.Web.UI.Client/devops/circular/index.js b/src/Umbraco.Web.UI.Client/devops/circular/index.js index 1a993925b9..ddcf1540f3 100644 --- a/src/Umbraco.Web.UI.Client/devops/circular/index.js +++ b/src/Umbraco.Web.UI.Client/devops/circular/index.js @@ -12,7 +12,7 @@ import { join } from 'path'; //const __dirname = import.meta.dirname; // Adjust this number as needed. -const MAX_CIRCULAR_DEPENDENCIES = 4; +const MAX_CIRCULAR_DEPENDENCIES = 1; const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true'; const IS_AZURE_PIPELINES = process.env.TF_BUILD === 'true'; diff --git a/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js b/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js index b18825233e..5ec7bf3944 100644 --- a/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js +++ b/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js @@ -3,7 +3,7 @@ import path from 'path'; import { createImportMap } from '../importmap/index.js'; const ILLEGAL_CORE_IMPORTS_THRESHOLD = 6; -const SELF_IMPORTS_THRESHOLD = 7; +const SELF_IMPORTS_THRESHOLD = 4; const clientProjectRoot = path.resolve(import.meta.dirname, '../../'); const modulePrefix = '@umbraco-cms/backoffice/'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-picker/media-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-picker/media-picker-modal.element.ts index 525c21d667..4704033a04 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-picker/media-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-picker/media-picker-modal.element.ts @@ -3,11 +3,11 @@ import { UmbMediaTreeRepository } from '../../tree/media-tree.repository.js'; import { UMB_MEDIA_ROOT_ENTITY_TYPE } from '../../entity.js'; import type { UmbMediaTreeItemModel, UmbMediaSearchItemModel, UmbMediaItemModel } from '../../types.js'; import { UmbMediaSearchProvider } from '../../search/index.js'; +import type { UmbDropzoneMediaElement } from '../../dropzone/index.js'; import type { UmbMediaPathModel } from './types.js'; import type { UmbMediaPickerFolderPathElement } from './components/media-picker-folder-path.element.js'; import type { UmbMediaPickerModalData, UmbMediaPickerModalValue } from './media-picker-modal.token.js'; import type { UmbDropzoneChangeEvent, UmbUploadableItem } from '@umbraco-cms/backoffice/dropzone'; -import type { UmbDropzoneMediaElement } from '@umbraco-cms/backoffice/media'; import { css, html, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/views/user-group-table-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/views/user-group-table-collection-view.element.ts index d59c91de01..9bcae08131 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/views/user-group-table-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/views/user-group-table-collection-view.element.ts @@ -12,13 +12,12 @@ import type { UmbTableItem, UmbTableSelectedEvent, } from '@umbraco-cms/backoffice/components'; -import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document'; -import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; import type { UmbUniqueItemModel } from '@umbraco-cms/backoffice/models'; import '../components/user-group-table-name-column-layout.element.js'; import '../components/user-group-table-sections-column-layout.element.js'; +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-user-group-collection-table-view') export class UmbUserGroupCollectionTableViewElement extends UmbLitElement { @@ -63,8 +62,8 @@ export class UmbUserGroupCollectionTableViewElement extends UmbLitElement { #collectionContext?: UmbUserGroupCollectionContext; // TODO: hardcoded dependencies on document and media modules. We should figure out how these dependencies can be added through extensions. - #documentItemRepository = new UmbDocumentItemRepository(this); - #mediaItemRepository = new UmbMediaItemRepository(this); + #documentItemRepository?: UmbItemRepository; + #mediaItemRepository?: UmbItemRepository; #documentStartNodeMap = new Map(); #mediaStartNodeMap = new Map(); @@ -81,7 +80,8 @@ export class UmbUserGroupCollectionTableViewElement extends UmbLitElement { ); this.observe( this.#collectionContext.items, - (items) => { + async (items) => { + await this.#initRepositories(); this._createTableItems(items); }, 'umbCollectionItemsObserver', @@ -89,7 +89,29 @@ export class UmbUserGroupCollectionTableViewElement extends UmbLitElement { }); } + async #initRepositories() { + if (this.#documentItemRepository && this.#mediaItemRepository) return; + + // TODO: get back to this when documents have been decoupled from users. + // The repository alias is hardcoded on purpose to avoid a document import in the user module. + this.#documentItemRepository = await createExtensionApiByAlias>( + this, + 'Umb.Repository.DocumentItem', + ); + + // TODO: get back to this when media have been decoupled from users. + // The repository alias is hardcoded on purpose to avoid a media import in the user module. + this.#mediaItemRepository = await createExtensionApiByAlias>( + this, + 'Umb.Repository.MediaItem', + ); + } + private async _createTableItems(userGroups: Array) { + if (!this.#documentItemRepository || !this.#mediaItemRepository) { + throw new Error('Document and media item repositories are not initialized.'); + } + await Promise.all([ this.#requestAndCacheStartNodes( userGroups, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/user-group-ref/user-group-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/user-group-ref/user-group-ref.element.ts index 47b7878429..0dfb70b6fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/user-group-ref/user-group-ref.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/user-group-ref/user-group-ref.element.ts @@ -1,9 +1,8 @@ import { UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document'; -import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media'; +import { createExtensionApiByAlias, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; /** * @element umb-user-group-ref @@ -12,8 +11,8 @@ import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media'; */ @customElement('umb-user-group-ref') export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) { - #documentItemRepository?: UmbDocumentItemRepository; - #mediaItemRepository?: UmbMediaItemRepository; + #documentItemRepository?: UmbItemRepository; + #mediaItemRepository?: UmbItemRepository; @property({ type: Boolean }) documentRootAccess: boolean = false; @@ -69,16 +68,21 @@ export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) { if (this.documentRootAccess) return; if (!unique) return; + // TODO: get back to this when documents have been decoupled from users. + // The repository alias is hardcoded on purpose to avoid a document import in the user module. if (!this.#documentItemRepository) { - this.#documentItemRepository = new UmbDocumentItemRepository(this); + this.#documentItemRepository = await createExtensionApiByAlias>( + this, + 'Umb.Repository.DocumentItem', + ); } const { error, asObservable } = await this.#documentItemRepository.requestItems([unique]); if (error) return; this.observe( - asObservable(), - (data) => (this._documentLabel = data[0].variants?.[0].name ?? unique), + asObservable?.(), + (data) => (this._documentLabel = data?.[0].variants?.[0].name ?? unique), '_observeDocumentStartNode', ); } @@ -87,16 +91,21 @@ export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) { if (this.mediaRootAccess) return; if (!unique) return; + // TODO: get back to this when media have been decoupled from users. + // The repository alias is hardcoded on purpose to avoid a media import in the user module. if (!this.#mediaItemRepository) { - this.#mediaItemRepository = new UmbMediaItemRepository(this); + this.#mediaItemRepository = await createExtensionApiByAlias>( + this, + 'Umb.Repository.MediaItem', + ); } const { error, asObservable } = await this.#mediaItemRepository.requestItems([unique]); if (error) return; this.observe( - asObservable(), - (data) => (this._mediaLabel = data[0].variants?.[0].name ?? unique), + asObservable?.(), + (data) => (this._mediaLabel = data?.[0].variants?.[0].name ?? unique), '_observeMediaStartNode', ); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/user-group-picker/user-group-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/user-group-picker/user-group-picker-modal.element.ts index dc356f6e31..5c25b7ac61 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/user-group-picker/user-group-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/user-group-picker/user-group-picker-modal.element.ts @@ -1,11 +1,11 @@ import { UmbUserGroupCollectionRepository } from '../../collection/repository/index.js'; import type { UmbUserGroupDetailModel } from '../../types.js'; +import type { UMB_USER_GROUP_PICKER_MODAL } from './user-group-picker-modal.token.js'; import { css, customElement, html, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { debounce, UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbDeselectedEvent, UmbSelectedEvent } from '@umbraco-cms/backoffice/event'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import type { UMB_USER_GROUP_PICKER_MODAL } from '@umbraco-cms/backoffice/user-group'; import type { UUIInputEvent, UUIMenuItemEvent } from '@umbraco-cms/backoffice/external/uui'; import '../../components/user-group-ref/user-group-ref.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts index b8a7de7a9a..701ff856e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts @@ -5,10 +5,8 @@ import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui' import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; import type { UmbInputSectionElement } from '@umbraco-cms/backoffice/section'; import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; -import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { UmbInputLanguageElement } from '@umbraco-cms/backoffice/language'; import { UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/icon'; @@ -145,7 +143,9 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { #onDocumentStartNodeChange(event: CustomEvent) { event.stopPropagation(); - const target = event.target as UmbInputDocumentElement; + // TODO: get back to this when documents have been decoupled from users. + // The event target is deliberately set to any to avoid an import cycle with documents. + const target = event.target as any; const selected = target.selection?.[0]; // TODO make contexts method this.#workspaceContext?.updateProperty('documentStartNode', selected ? { unique: selected } : null); @@ -161,7 +161,9 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { #onMediaStartNodeChange(event: CustomEvent) { event.stopPropagation(); - const target = event.target as UmbInputMediaElement; + // TODO: get back to this when media have been decoupled from users. + // The event target is deliberately set to any to avoid an import cycle with media. + const target = event.target as any; const selected = target.selection?.[0]; // TODO make contexts method this.#workspaceContext?.updateProperty('mediaStartNode', selected ? { unique: selected } : null); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-document-start-node/user-document-start-node.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-document-start-node/user-document-start-node.element.ts index 431c5e3b9f..b7e4773038 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-document-start-node/user-document-start-node.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-document-start-node/user-document-start-node.element.ts @@ -1,7 +1,7 @@ +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbDocumentItemModel } from '@umbraco-cms/backoffice/document'; -import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document'; +import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; @customElement('umb-user-document-start-node') export class UmbUserDocumentStartNodeElement extends UmbLitElement { @@ -22,14 +22,15 @@ export class UmbUserDocumentStartNodeElement extends UmbLitElement { readonly = false; @state() - _displayValue: Array = []; - - #itemRepository = new UmbDocumentItemRepository(this); + _displayValue: Array = []; async #observeItems() { - const { asObservable } = await this.#itemRepository.requestItems(this.#uniques); + // TODO: get back to this when documents have been decoupled from users. + // The repository alias is hardcoded on purpose to avoid a document import in the user module. + const itemRepository = await createExtensionApiByAlias>(this, 'Umb.Repository.DocumentItem'); + const { asObservable } = await itemRepository.requestItems(this.#uniques); - this.observe(asObservable(), (data) => { + this.observe(asObservable?.(), (data) => { this._displayValue = data || []; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-media-start-node/user-media-start-node.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-media-start-node/user-media-start-node.element.ts index 885cee68c4..2ed6c18da9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-media-start-node/user-media-start-node.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-media-start-node/user-media-start-node.element.ts @@ -1,7 +1,7 @@ +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property, repeat, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbMediaItemModel } from '@umbraco-cms/backoffice/media'; -import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media'; +import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; @customElement('umb-user-media-start-node') export class UmbUserMediaStartNodeElement extends UmbLitElement { @@ -22,14 +22,15 @@ export class UmbUserMediaStartNodeElement extends UmbLitElement { readonly = false; @state() - _displayValue: Array = []; - - #itemRepository = new UmbMediaItemRepository(this); + _displayValue: Array = []; async #observeItems() { - const { asObservable } = await this.#itemRepository.requestItems(this.#uniques); + // TODO: get back to this when documents have been decoupled from users. + // The repository alias is hardcoded on purpose to avoid a document import in the user module. + const itemRepository = await createExtensionApiByAlias>(this, 'Umb.Repository.MediaItem'); + const { asObservable } = await itemRepository.requestItems(this.#uniques); - this.observe(asObservable(), (data) => { + this.observe(asObservable?.(), (data) => { this._displayValue = data || []; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-picker/user-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-picker/user-picker-modal.token.ts index a4ef24bffc..a72fc779c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-picker/user-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-picker/user-picker-modal.token.ts @@ -1,4 +1,4 @@ -import type { UmbUserDetailModel } from '@umbraco-cms/backoffice/user'; +import type { UmbUserDetailModel } from '../../types.js'; import type { UmbPickerModalData } from '@umbraco-cms/backoffice/modal'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts index 9347d263fa..1065c91ff3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts @@ -1,3 +1,4 @@ +import type { UmbUserInputElement } from '../../components/index.js'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -5,7 +6,6 @@ import type { UmbPropertyEditorConfigCollection, UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbUserInputElement } from '@umbraco-cms/backoffice/user'; /** * @element umb-property-editor-ui-user-picker diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user/components/user-workspace-assign-access/user-workspace-assign-access.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user/components/user-workspace-assign-access/user-workspace-assign-access.element.ts index d32bb6e0fd..56ccaf2b27 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user/components/user-workspace-assign-access/user-workspace-assign-access.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user/components/user-workspace-assign-access/user-workspace-assign-access.element.ts @@ -3,8 +3,6 @@ import type { UmbUserDetailModel } from '../../../../types.js'; import { html, customElement, state, nothing, css } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; -import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; import type { UmbUserGroupInputElement } from '@umbraco-cms/backoffice/user-group'; import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; @@ -87,8 +85,10 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement { #onDocumentStartNodeChange(event: CustomEvent) { event.stopPropagation(); - const target = event.target as UmbInputDocumentElement; - const selection: Array = target.selection.map((unique) => { + // TODO: get back to this when media have been decoupled from users. + // The event target is deliberately set to any to avoid an import cycle with media. + const target = event.target as any; + const selection: Array = target.selection.map((unique: string) => { return { unique }; }); // TODO make contexts method @@ -105,8 +105,10 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement { #onMediaStartNodeChange(event: CustomEvent) { event.stopPropagation(); - const target = event.target as UmbInputMediaElement; - const selection: Array = target.selection.map((unique) => { + // TODO: get back to this when media have been decoupled from users. + // The event target is deliberately set to any to avoid an import cycle with media. + const target = event.target as any; + const selection: Array = target.selection.map((unique: string) => { return { unique }; }); // TODO make contexts method