diff --git a/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/value-translator/paste/clipboard-paste-translator-value-resolver.ts b/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/value-translator/paste/clipboard-paste-translator-value-resolver.ts index 5797083f14..9f5bcb9e28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/value-translator/paste/clipboard-paste-translator-value-resolver.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/value-translator/paste/clipboard-paste-translator-value-resolver.ts @@ -79,6 +79,7 @@ export class UmbClipboardPastePropertyValueTranslatorValueResolver< } // Pick the manifest with the highest priority + // TODO: This should have been handled in the extension registry, but until then we do it here: [NL] return supportedManifests.sort((a: ManifestBase, b: ManifestBase): number => (b.weight || 0) - (a.weight || 0))[0]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/default-item-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/default-item-ref.element.ts index 25c32cafd6..44c750fe70 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/default-item-ref.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/default-item-ref.element.ts @@ -7,9 +7,6 @@ export class UmbDefaultItemRefElement extends UmbLitElement { @property({ type: Object }) item?: UmbDefaultItemModel; - @property({ type: Boolean }) - readonly = false; - @property({ type: Boolean }) standalone = false; @@ -17,7 +14,7 @@ export class UmbDefaultItemRefElement extends UmbLitElement { if (!this.item) return nothing; return html` - + ${this.#renderIcon(this.item)} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.element.ts index a76e1c36c0..de07c6a3d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.element.ts @@ -7,6 +7,7 @@ import { UMB_MARK_ATTRIBUTE_NAME } from '@umbraco-cms/backoffice/const'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import './default-item-ref.element.js'; +import { UmbRoutePathAddendumContext } from '@umbraco-cms/backoffice/router'; @customElement('umb-entity-item-ref') export class UmbEntityItemRefElement extends UmbLitElement { @@ -33,6 +34,8 @@ export class UmbEntityItemRefElement extends UmbLitElement { return; } + this.#pathAddendum.setAddendum('ref/' + value.entityType + '/' + value.unique); + // If the component is already created, but the entity type is different, we need to destroy the component. this.#createController(value.entityType); } @@ -63,6 +66,8 @@ export class UmbEntityItemRefElement extends UmbLitElement { } } + #pathAddendum = new UmbRoutePathAddendumContext(this); + protected override firstUpdated(_changedProperties: PropertyValueMap | Map): void { super.firstUpdated(_changedProperties); this.setAttribute(UMB_MARK_ATTRIBUTE_NAME, 'entity-item-ref'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/constants.ts new file mode 100644 index 0000000000..72ec3b021a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/constants.ts @@ -0,0 +1 @@ +export * from './data-mapper/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/constants.ts new file mode 100644 index 0000000000..7b9ccdc345 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/constants.ts @@ -0,0 +1 @@ +export * from './management-api/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/data-mapper.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/data-mapper.ts new file mode 100644 index 0000000000..24de99cdd1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/data-mapper.ts @@ -0,0 +1,43 @@ +import { UmbDataMappingResolver } from './mapping/data-mapping-resolver.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; + +export interface UmbDataMapperMapArgs { + forDataModel: string; + forDataSource: string; + data: fromModelType; + fallback?: (data: fromModelType) => Promise; +} + +export class UmbDataMapper extends UmbControllerBase { + #dataMappingResolver = new UmbDataMappingResolver(this); + + async map(args: UmbDataMapperMapArgs) { + if (!args.forDataSource) { + throw new Error('data source identifier is required'); + } + + if (!args.forDataModel) { + throw new Error('data identifier is required'); + } + + if (!args.data) { + throw new Error('data is required'); + } + + const dataMapping = await this.#dataMappingResolver.resolve(args.forDataSource, args.forDataModel); + + if (!dataMapping && !args.fallback) { + throw new Error('Data mapping not found and no fallback provided.'); + } + + if (!dataMapping && args.fallback) { + return args.fallback(args.data); + } + + if (!dataMapping?.map) { + throw new Error('Data mapping does not have a map method.'); + } + + return dataMapping.map(args.data); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/index.ts new file mode 100644 index 0000000000..f709dce859 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/index.ts @@ -0,0 +1,3 @@ +export * from './data-mapper.js'; +export * from './mapping/index.js'; +export * from './management-api/management-api-data-mapper.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/management-api/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/management-api/constants.ts new file mode 100644 index 0000000000..25d3b5812f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/management-api/constants.ts @@ -0,0 +1 @@ +export const UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER = 'Umb.ManagementApi'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/management-api/management-api-data-mapper.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/management-api/management-api-data-mapper.ts new file mode 100644 index 0000000000..d220ee390d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/management-api/management-api-data-mapper.ts @@ -0,0 +1,19 @@ +import { UmbDataMapper, type UmbDataMapperMapArgs } from '../data-mapper.js'; +import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from './constants.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbManagementApiDataMapper extends UmbControllerBase { + #dataMapper = new UmbDataMapper(this); + + constructor(host: UmbControllerHost) { + super(host); + } + + map(args: Omit) { + return this.#dataMapper.map({ + ...args, + forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER, + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts new file mode 100644 index 0000000000..0f7b1c46e1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts @@ -0,0 +1,64 @@ +import type { UmbDataMapping } from './types.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { createExtensionApi, type ManifestBase } from '@umbraco-cms/backoffice/extension-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +export class UmbDataMappingResolver extends UmbControllerBase { + #apiCache = new Map(); + + async resolve(forDataSource: string, forDataModel: string): Promise { + if (!forDataSource) { + throw new Error('data source identifier is required'); + } + + if (!forDataModel) { + throw new Error('data identifier is required'); + } + + const manifest = this.#getManifestWithBestFit(forDataSource, forDataModel); + + if (!manifest) { + return undefined; + } + + // Check the cache before creating a new instance + if (this.#apiCache.has(manifest.alias)) { + return this.#apiCache.get(manifest.alias)!; + } + + const dataMapping = await createExtensionApi(this, manifest); + + if (!dataMapping) { + return undefined; + } + + if (!dataMapping.map) { + throw new Error('Data Mapping does not have a map method.'); + } + + // Cache the api instance for future use + this.#apiCache.set(manifest.alias, dataMapping); + + return dataMapping; + } + + #getManifestWithBestFit(forDataSource: string, forDataModel: string) { + const supportedManifests = this.#getSupportedManifests(forDataSource, forDataModel); + + if (!supportedManifests.length) { + return undefined; + } + + // Pick the manifest with the highest priority + // TODO: This should have been handled in the extension registry, but until then we do it here: [NL] + return supportedManifests.sort((a: ManifestBase, b: ManifestBase): number => (b.weight || 0) - (a.weight || 0))[0]; + } + + #getSupportedManifests(forDataSource: string, forDataModel: string) { + const supportedManifests = umbExtensionsRegistry.getByTypeAndFilter('dataMapping', (manifest) => { + return manifest.forDataSource === forDataSource && manifest.forDataModel === forDataModel; + }); + + return supportedManifests; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts new file mode 100644 index 0000000000..64e85b2722 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts @@ -0,0 +1,19 @@ +import type { UmbDataMapping } from './types.js'; +import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface ManifestDataMapping + extends ManifestApi { + type: 'dataMapping'; + forDataSource: string; + forDataModel: string; + meta: MetaType; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface MetaDataMapping {} + +declare global { + interface UmbExtensionManifestMap { + umbManifestDataMapping: ManifestDataMapping; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/index.ts new file mode 100644 index 0000000000..2a1055c6f2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/index.ts @@ -0,0 +1 @@ +export * from './data-mapping-resolver.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts new file mode 100644 index 0000000000..4dc1a4883e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts @@ -0,0 +1,6 @@ +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +export type * from './data-mapping.extension.js'; + +export interface UmbDataMapping extends UmbApi { + map: (data: fromModelType) => Promise; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/types.ts new file mode 100644 index 0000000000..bb05f6a27d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/types.ts @@ -0,0 +1 @@ +export type * from './mapping/types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/index.ts index 3d2bf2606a..a344a151a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/index.ts @@ -1,8 +1,9 @@ -export * from './repository-items.manager.js'; -export * from './repository-base.js'; - -export * from './item/index.js'; +export * from './constants.js'; +export * from './data-mapper/index.js'; export * from './detail/index.js'; +export * from './item/index.js'; +export * from './repository-base.js'; +export * from './repository-items.manager.js'; export type { UmbDataSourceResponse, UmbDataSourceErrorResponse } from './data-source-response.interface.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts index 958077f2aa..c75d422538 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts @@ -14,3 +14,5 @@ export interface UmbRepositoryErrorResponse extends UmbDataSourceErrorResponse { export interface UmbRepositoryResponseWithAsObservable extends UmbRepositoryResponse { asObservable: () => Observable; } + +export type * from './data-mapper/mapping/types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-ref.element.ts index 35b2085640..9bee87cc6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-ref.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-ref.element.ts @@ -16,19 +16,7 @@ export class UmbDocumentItemRefElement extends UmbLitElement { return this.#item.getData(); } public set item(value: UmbDocumentItemModel | undefined) { - const oldValue = this.#item.getData(); this.#item.setData(value); - - if (!value) { - this.#modalRoute?.destroy(); - return; - } - - if (oldValue?.unique === value.unique) { - return; - } - - this.#modalRoute?.setUniquePathValue('unique', value.unique); } @property({ type: Boolean }) @@ -55,13 +43,10 @@ export class UmbDocumentItemRefElement extends UmbLitElement { @state() _editPath = ''; - #modalRoute?: any; - constructor() { super(); - this.#modalRoute = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath(UMB_DOCUMENT_ENTITY_TYPE) + new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) .addUniquePaths(['unique']) .onSetup(() => { return { data: { entityType: UMB_DOCUMENT_ENTITY_TYPE, preset: {} } }; @@ -78,6 +63,7 @@ export class UmbDocumentItemRefElement extends UmbLitElement { } #getHref() { + if (!this._unique) return; const path = UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN.generateLocal({ unique: this._unique }); return `${this._editPath}/${path}`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/types.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/types.ts index 281be3f829..e32ac4b889 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/types.ts @@ -1 +1 @@ -export type { UmbDocumentItemModel } from './repository/types.js'; +export type * from './repository/types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/info-app/document-references-workspace-view-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/info-app/document-references-workspace-view-info.element.ts index def6b92a80..61910918bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/info-app/document-references-workspace-view-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/info-app/document-references-workspace-view-info.element.ts @@ -1,20 +1,14 @@ import { UmbDocumentReferenceRepository } from '../repository/index.js'; import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../../constants.js'; -import { css, customElement, html, nothing, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; -import { isDefaultReference, isDocumentReference, isMediaReference } from '@umbraco-cms/backoffice/relations'; +import { css, customElement, html, nothing, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; -import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; -import type { UmbReferenceModel } from '@umbraco-cms/backoffice/relations'; +import type { UmbReferenceItemModel } from '@umbraco-cms/backoffice/relations'; import type { UUIPaginationEvent } from '@umbraco-cms/backoffice/external/uui'; import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; @customElement('umb-document-references-workspace-info-app') export class UmbDocumentReferencesWorkspaceInfoAppElement extends UmbLitElement { - @state() - private _editDocumentPath = ''; - @state() private _currentPage = 1; @@ -22,7 +16,7 @@ export class UmbDocumentReferencesWorkspaceInfoAppElement extends UmbLitElement private _total = 0; @state() - private _items?: Array = []; + private _items?: Array = []; #itemsPerPage = 10; #referenceRepository = new UmbDocumentReferenceRepository(this); @@ -32,15 +26,6 @@ export class UmbDocumentReferencesWorkspaceInfoAppElement extends UmbLitElement constructor() { super(); - new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath('document') - .onSetup(() => { - return { data: { entityType: 'document', preset: {} } }; - }) - .observeRouteBuilder((routeBuilder) => { - this._editDocumentPath = routeBuilder({}); - }); - this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (context) => { this.#workspaceContext = context; this.#observeDocumentUnique(); @@ -91,98 +76,28 @@ export class UmbDocumentReferencesWorkspaceInfoAppElement extends UmbLitElement this.#getReferences(); } - #getIcon(item: UmbReferenceModel) { - if (isDocumentReference(item)) { - return item.documentType.icon ?? 'icon-document'; - } - if (isMediaReference(item)) { - return item.mediaType.icon ?? 'icon-picture'; - } - if (isDefaultReference(item)) { - return item.icon ?? 'icon-document'; - } - return 'icon-document'; - } - - #getPublishedStatus(item: UmbReferenceModel) { - return isDocumentReference(item) ? item.published : true; - } - - #getContentTypeName(item: UmbReferenceModel) { - if (isDocumentReference(item)) { - return item.documentType.name; - } - if (isMediaReference(item)) { - return item.mediaType.name; - } - if (isDefaultReference(item)) { - return item.type; - } - return ''; - } - - #getContentType(item: UmbReferenceModel) { - if (isDocumentReference(item)) { - return item.documentType.alias; - } - if (isMediaReference(item)) { - return item.mediaType.alias; - } - if (isDefaultReference(item)) { - return item.type; - } - return ''; - } - override render() { if (!this._items?.length) return nothing; return html` - - - - Name - Status - Type Name - Type - - ${repeat( - this._items, - (item) => item.id, - (item) => html` - - - - - - ${when( - isDocumentReference(item), - () => html` - - ${item.name} - - `, - () => item.name, - )} - - - ${this.#getPublishedStatus(item) - ? this.localize.term('content_published') - : this.localize.term('content_unpublished')} - - ${this.#getContentTypeName(item)} - ${this.#getContentType(item)} - - `, - )} - - ${this.#renderReferencePagination()} + ${this.#renderItems()} ${this.#renderReferencePagination()} `; } + #renderItems() { + if (!this._items) return; + return html` + + ${repeat( + this._items, + (item) => item.unique, + (item) => html``, + )} + + `; + } + #renderReferencePagination() { if (!this._total) return nothing; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference-response.management-api.mapping.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference-response.management-api.mapping.ts new file mode 100644 index 0000000000..97baecf652 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference-response.management-api.mapping.ts @@ -0,0 +1,38 @@ +import type { UmbDocumentReferenceModel } from '../types.js'; +import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js'; +import { + DocumentVariantStateModel, + type DocumentReferenceResponseModel, +} from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbDataMapping } from '@umbraco-cms/backoffice/repository'; + +export class UmbDocumentReferenceResponseManagementApiDataMapping + extends UmbControllerBase + implements UmbDataMapping +{ + async map(data: DocumentReferenceResponseModel): Promise { + return { + documentType: { + alias: data.documentType.alias, + icon: data.documentType.icon, + name: data.documentType.name, + }, + entityType: UMB_DOCUMENT_ENTITY_TYPE, + id: data.id, + name: data.name, + published: data.published, + // TODO: this is a hardcoded array until the server can return the correct variants array + variants: [ + { + culture: null, + name: data.name ?? '', + state: data.published ? DocumentVariantStateModel.PUBLISHED : null, + }, + ], + unique: data.id, + }; + } +} + +export { UmbDocumentReferenceResponseManagementApiDataMapping as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts index 13a7b725a5..0773945240 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts @@ -1,30 +1,47 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbManagementApiDataMapper } from '@umbraco-cms/backoffice/repository'; /** * @class UmbDocumentReferenceServerDataSource * @implements {RepositoryDetailDataSource} */ -export class UmbDocumentReferenceServerDataSource { - #host: UmbControllerHost; - - /** - * Creates an instance of UmbDocumentReferenceServerDataSource. - * @param {UmbControllerHost} host - The controller host for this controller to be appended to - * @memberof UmbDocumentReferenceServerDataSource - */ - constructor(host: UmbControllerHost) { - this.#host = host; - } +export class UmbDocumentReferenceServerDataSource extends UmbControllerBase { + #dataMapper = new UmbManagementApiDataMapper(this); /** * Fetches the item for the given unique from the server - * @param {string} id + * @param {string} unique - The unique identifier of the item to fetch * @returns {*} * @memberof UmbDocumentReferenceServerDataSource */ - async getReferencedBy(id: string, skip = 0, take = 20) { - return await tryExecuteAndNotify(this.#host, DocumentService.getDocumentByIdReferencedBy({ id, skip, take })); + async getReferencedBy(unique: string, skip = 0, take = 20) { + const { data, error } = await tryExecuteAndNotify( + this, + DocumentService.getDocumentByIdReferencedBy({ id: unique, skip, take }), + ); + + if (data) { + const promises = data.items.map(async (item) => { + return this.#dataMapper.map({ + forDataModel: item.$type, + data: item, + fallback: async () => { + return { + ...item, + unique: item.id, + entityType: 'unknown', + }; + }, + }); + }); + + const items = await Promise.all(promises); + + return { data: { items, total: data.total } }; + } + + return { data, error }; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/manifests.ts index 0527cc53bc..8e2dc6740b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/manifests.ts @@ -1,4 +1,5 @@ import { UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS } from './constants.js'; +import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from '@umbraco-cms/backoffice/repository'; export const manifests: Array = [ { @@ -7,4 +8,12 @@ export const manifests: Array = [ name: 'Document Reference Repository', api: () => import('./document-reference.repository.js'), }, + { + type: 'dataMapping', + alias: 'Umb.DataMapping.ManagementApi.DocumentReferenceResponse', + name: 'Document Reference Response Management Api Data Mapping', + api: () => import('./document-reference-response.management-api.mapping.js'), + forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER, + forDataModel: 'DocumentReferenceResponseModel', + }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/types.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/types.ts new file mode 100644 index 0000000000..90496f5d87 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/types.ts @@ -0,0 +1,28 @@ +import type { UmbDocumentItemVariantModel } from '../item/types.js'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import type { TrackedReferenceDocumentTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; + +export interface UmbDocumentReferenceModel extends UmbEntityModel { + /** + * @deprecated use unique instead + * @type {string} + * @memberof UmbDocumentReferenceModel + */ + id: string; + + /** + * @deprecated use name on the variant array instead + * @type {(string | null)} + * @memberof UmbDocumentReferenceModel + */ + name?: string | null; + + /** + * @deprecated use state on variant array instead + * @type {boolean} + * @memberof UmbDocumentReferenceModel + */ + published?: boolean | null; + documentType: TrackedReferenceDocumentTypeModel; + variants: Array; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts index f6ecca6dae..b668b5ae9d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts @@ -60,7 +60,7 @@ export class UmbDocumentWorkspaceViewInfoElement extends UmbLitElement { super(); new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath(':entityType') + .addAdditionalPath('general/:entityType') .onSetup((params) => { return { data: { entityType: params.entityType, preset: {} } }; }) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/item/manifests.ts new file mode 100644 index 0000000000..ceb7c57fb0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/item/manifests.ts @@ -0,0 +1,11 @@ +import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js'; + +export const manifests: Array = [ + { + type: 'entityItemRef', + alias: 'Umb.EntityItemRef.Media', + name: 'Member Entity Item Reference', + element: () => import('./media-item-ref.element.js'), + forEntityTypes: [UMB_MEDIA_ENTITY_TYPE], + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/item/media-item-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/item/media-item-ref.element.ts new file mode 100644 index 0000000000..d948326a53 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/item/media-item-ref.element.ts @@ -0,0 +1,93 @@ +import type { UmbMediaItemModel } from '../repository/types.js'; +import { UMB_MEDIA_SECTION_ALIAS } from '../../media-section/constants.js'; +import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js'; +import { UMB_EDIT_MEDIA_WORKSPACE_PATH_PATTERN } from '../paths.js'; +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; +import { customElement, html, ifDefined, nothing, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; +import { UMB_SECTION_USER_PERMISSION_CONDITION_ALIAS } from '@umbraco-cms/backoffice/section'; +import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; + +@customElement('umb-media-item-ref') +export class UmbMediaItemRefElement extends UmbLitElement { + #item?: UmbMediaItemModel | undefined; + + @property({ type: Object }) + public get item(): UmbMediaItemModel | undefined { + return this.#item; + } + public set item(value: UmbMediaItemModel | undefined) { + this.#item = value; + } + + @property({ type: Boolean }) + readonly = false; + + @property({ type: Boolean }) + standalone = false; + + @state() + _editPath = ''; + + @state() + _userHasSectionAccess = false; + + constructor() { + super(); + + createExtensionApiByAlias(this, UMB_SECTION_USER_PERMISSION_CONDITION_ALIAS, [ + { + config: { + match: UMB_MEDIA_SECTION_ALIAS, + }, + onChange: (permitted: boolean) => { + this._userHasSectionAccess = permitted; + }, + }, + ]); + + new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) + .addUniquePaths(['unique']) + .onSetup(() => { + return { data: { entityType: UMB_MEDIA_ENTITY_TYPE, preset: {} } }; + }) + .observeRouteBuilder((routeBuilder) => { + this._editPath = routeBuilder({}); + }); + } + + #getHref(item: UmbMediaItemModel) { + if (!this._editPath) return; + const path = UMB_EDIT_MEDIA_WORKSPACE_PATH_PATTERN.generateLocal({ unique: item.unique }); + return `${this._editPath}/${path}`; + } + + override render() { + if (!this.item) return nothing; + + return html` + + + ${this.#renderIcon(this.item)} + + `; + } + + #renderIcon(item: UmbMediaItemModel) { + if (!item.mediaType.icon) return; + return html``; + } +} + +export { UmbMediaItemRefElement as element }; + +declare global { + interface HTMLElementTagNameMap { + 'umb-media-item-ref': UmbMediaItemRefElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts index 1b61cda607..69738473e5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts @@ -4,6 +4,7 @@ import { manifests as dropzoneManifests } from './dropzone/manifests.js'; import { manifests as entityActionsManifests } from './entity-actions/manifests.js'; import { manifests as entityBulkActionsManifests } from './entity-bulk-actions/manifests.js'; import { manifests as fileUploadPreviewManifests } from './components/input-upload-field/manifests.js'; +import { manifests as itemManifests } from './item/manifests.js'; import { manifests as menuManifests } from './menu/manifests.js'; import { manifests as modalManifests } from './modals/manifests.js'; import { manifests as propertyEditorsManifests } from './property-editors/manifests.js'; @@ -23,6 +24,7 @@ export const manifests: Array = [ ...entityActionsManifests, ...entityBulkActionsManifests, ...fileUploadPreviewManifests, + ...itemManifests, ...menuManifests, ...modalManifests, ...propertyEditorsManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/info-app/media-references-workspace-info-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/info-app/media-references-workspace-info-app.element.ts index e552b85540..b1f46e6fd9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/info-app/media-references-workspace-info-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/info-app/media-references-workspace-info-app.element.ts @@ -1,13 +1,9 @@ import { UmbMediaReferenceRepository } from '../repository/index.js'; import { UMB_MEDIA_WORKSPACE_CONTEXT } from '../../workspace/constants.js'; import { css, customElement, html, nothing, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; -import { isDefaultReference, isDocumentReference, isMediaReference } from '@umbraco-cms/backoffice/relations'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; -import type { UmbReferenceModel } from '@umbraco-cms/backoffice/relations'; -import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; +import type { UmbReferenceItemModel } from '@umbraco-cms/backoffice/relations'; import type { UUIPaginationEvent } from '@umbraco-cms/backoffice/external/uui'; import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; @@ -17,8 +13,6 @@ export class UmbMediaReferencesWorkspaceInfoAppElement extends UmbLitElement { #referenceRepository; - #routeBuilder?: UmbModalRouteBuilder; - @state() private _currentPage = 1; @@ -26,7 +20,7 @@ export class UmbMediaReferencesWorkspaceInfoAppElement extends UmbLitElement { private _total = 0; @state() - private _items?: Array = []; + private _items?: Array = []; @state() private _loading = true; @@ -38,15 +32,6 @@ export class UmbMediaReferencesWorkspaceInfoAppElement extends UmbLitElement { super(); this.#referenceRepository = new UmbMediaReferenceRepository(this); - new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath(':entityType') - .onSetup((params) => { - return { data: { entityType: params.entityType, preset: {} } }; - }) - .observeRouteBuilder((routeBuilder) => { - this.#routeBuilder = routeBuilder; - }); - this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (context) => { this.#workspaceContext = context; this.#observeMediaUnique(); @@ -106,54 +91,6 @@ export class UmbMediaReferencesWorkspaceInfoAppElement extends UmbLitElement { this.#getReferences(); } - #getEditPath(item: UmbReferenceModel) { - const entityType = this.#getEntityType(item); - return this.#routeBuilder && entityType ? `${this.#routeBuilder({ entityType })}edit/${item.id}` : '#'; - } - - #getIcon(item: UmbReferenceModel) { - if (isDocumentReference(item)) { - return item.documentType.icon ?? 'icon-document'; - } - if (isMediaReference(item)) { - return item.mediaType.icon ?? 'icon-picture'; - } - if (isDefaultReference(item)) { - return item.icon ?? 'icon-document'; - } - return 'icon-document'; - } - - #getPublishedStatus(item: UmbReferenceModel) { - return isDocumentReference(item) ? item.published : true; - } - - #getContentTypeName(item: UmbReferenceModel) { - if (isDocumentReference(item)) { - return item.documentType.name; - } - if (isMediaReference(item)) { - return item.mediaType.name; - } - if (isDefaultReference(item)) { - return item.type; - } - return null; - } - - #getEntityType(item: UmbReferenceModel) { - if (isDocumentReference(item)) { - return 'document'; - } - if (isMediaReference(item)) { - return 'media'; - } - if (isDefaultReference(item)) { - return item.type; - } - return null; - } - override render() { if (!this._items?.length) return nothing; return html` @@ -168,44 +105,15 @@ export class UmbMediaReferencesWorkspaceInfoAppElement extends UmbLitElement { } #renderItems() { - if (!this._items?.length) return nothing; + if (!this._items) return; return html` - - - Name - Status - Type Name - Type - + ${repeat( this._items, - (item) => item.id, - (item) => html` - - - - - - - - ${when( - this.#getPublishedStatus(item), - () => - html`${this.localize.term('content_published')}`, - () => - html`${this.localize.term('content_unpublished')}`, - )} - - ${this.#getContentTypeName(item)} - ${this.#getEntityType(item)} - - `, + (item) => item.unique, + (item) => html``, )} - + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/manifests.ts index 6981b55152..c885ccb85c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/manifests.ts @@ -1,4 +1,5 @@ import { UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS } from './constants.js'; +import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from '@umbraco-cms/backoffice/repository'; export const manifests: Array = [ { @@ -7,4 +8,12 @@ export const manifests: Array = [ name: 'Media Reference Repository', api: () => import('./media-reference.repository.js'), }, + { + type: 'dataMapping', + alias: 'Umb.DataMapping.ManagementApi.MediaReferenceResponse', + name: 'Media Reference Response Management Api Data Mapping', + api: () => import('./media-reference-response.management-api.mapping.js'), + forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER, + forDataModel: 'MediaReferenceResponseModel', + }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference-response.management-api.mapping.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference-response.management-api.mapping.ts new file mode 100644 index 0000000000..f00cf503c2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference-response.management-api.mapping.ts @@ -0,0 +1,33 @@ +import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js'; +import type { UmbMediaReferenceModel } from './types.js'; +import type { MediaReferenceResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbDataMapping } from '@umbraco-cms/backoffice/repository'; + +export class UmbMediaReferenceResponseManagementApiDataMapping + extends UmbControllerBase + implements UmbDataMapping +{ + async map(data: MediaReferenceResponseModel): Promise { + return { + entityType: UMB_MEDIA_ENTITY_TYPE, + id: data.id, + mediaType: { + alias: data.mediaType.alias, + icon: data.mediaType.icon, + name: data.mediaType.name, + }, + name: data.name, + // TODO: this is a hardcoded array until the server can return the correct variants array + variants: [ + { + culture: null, + name: data.name ?? '', + }, + ], + unique: data.id, + }; + } +} + +export { UmbMediaReferenceResponseManagementApiDataMapping as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts index 5ff12c10e5..5c5eac9ea5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts @@ -1,30 +1,47 @@ -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbManagementApiDataMapper } from '@umbraco-cms/backoffice/repository'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; /** * @class UmbMediaReferenceServerDataSource * @implements {RepositoryDetailDataSource} */ -export class UmbMediaReferenceServerDataSource { - #host: UmbControllerHost; - - /** - * Creates an instance of UmbMediaReferenceServerDataSource. - * @param {UmbControllerHost} host - The controller host for this controller to be appended to - * @memberof UmbMediaReferenceServerDataSource - */ - constructor(host: UmbControllerHost) { - this.#host = host; - } +export class UmbMediaReferenceServerDataSource extends UmbControllerBase { + #dataMapper = new UmbManagementApiDataMapper(this); /** * Fetches the item for the given id from the server - * @param {Array} ids + * @param {string} unique - The unique identifier of the item to fetch * @returns {*} * @memberof UmbMediaReferenceServerDataSource */ - async getReferencedBy(id: string, skip = 0, take = 20) { - return await tryExecuteAndNotify(this.#host, MediaService.getMediaByIdReferencedBy({ id, skip, take })); + async getReferencedBy(unique: string, skip = 0, take = 20) { + const { data, error } = await tryExecuteAndNotify( + this, + MediaService.getMediaByIdReferencedBy({ id: unique, skip, take }), + ); + + if (data) { + const promises = data.items.map(async (item) => { + return this.#dataMapper.map({ + forDataModel: item.$type, + data: item, + fallback: async () => { + return { + ...item, + unique: item.id, + entityType: 'unknown', + }; + }, + }); + }); + + const items = await Promise.all(promises); + + return { data: { items, total: data.total } }; + } + + return { data, error }; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/types.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/types.ts new file mode 100644 index 0000000000..69220e6301 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/types.ts @@ -0,0 +1,28 @@ +import type { UmbMediaItemVariantModel } from '../../repository/item/types.js'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import type { TrackedReferenceMediaTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; + +export interface UmbMediaReferenceModel extends UmbEntityModel { + /** + * @deprecated use unique instead + * @type {string} + * @memberof UmbMediaReferenceModel + */ + id: string; + + /** + * @deprecated use name on the variant array instead + * @type {(string | null)} + * @memberof UmbMediaReferenceModel + */ + name?: string | null; + + /** + * @deprecated use state on variant array instead + * @type {boolean} + * @memberof UmbMediaReferenceModel + */ + published?: boolean | null; + mediaType: TrackedReferenceMediaTypeModel; + variants: Array; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/item/member-item-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/item/member-item-ref.element.ts index 2dbfb4848b..a097ea138e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/item/member-item-ref.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/item/member-item-ref.element.ts @@ -1,5 +1,6 @@ import { UMB_MEMBER_ENTITY_TYPE } from '../entity.js'; import { UMB_MEMBER_MANAGEMENT_SECTION_ALIAS } from '../../section/constants.js'; +import { UMB_EDIT_MEMBER_WORKSPACE_PATH_PATTERN } from '../paths.js'; import type { UmbMemberItemModel } from './repository/types.js'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { customElement, html, ifDefined, nothing, property, state } from '@umbraco-cms/backoffice/external/lit'; @@ -17,18 +18,7 @@ export class UmbMemberItemRefElement extends UmbLitElement { return this.#item; } public set item(value: UmbMemberItemModel | undefined) { - const oldValue = this.#item; this.#item = value; - - if (!this.#item) { - this.#modalRoute?.destroy(); - return; - } - if (oldValue?.unique === this.#item.unique) { - return; - } - - this.#modalRoute?.setUniquePathValue('unique', this.#item.unique); } @property({ type: Boolean }) @@ -43,8 +33,6 @@ export class UmbMemberItemRefElement extends UmbLitElement { @state() _userHasSectionAccess = false; - #modalRoute?: any; - constructor() { super(); @@ -59,9 +47,7 @@ export class UmbMemberItemRefElement extends UmbLitElement { }, ]); - this.#modalRoute = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath(UMB_MEMBER_ENTITY_TYPE) - .addUniquePaths(['unique']) + new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) .onSetup(() => { return { data: { entityType: UMB_MEMBER_ENTITY_TYPE, preset: {} } }; }) @@ -71,7 +57,9 @@ export class UmbMemberItemRefElement extends UmbLitElement { } #getHref(item: UmbMemberItemModel) { - return `${this._editPath}/edit/${item.unique}`; + if (!this._editPath) return; + const path = UMB_EDIT_MEMBER_WORKSPACE_PATH_PATTERN.generateLocal({ unique: item.unique }); + return `${this._editPath}/${path}`; } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/paths.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/paths.ts index 65d1ab8787..5d5f51d8f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/paths.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/paths.ts @@ -16,3 +16,5 @@ export const UMB_MEMBER_ROOT_WORKSPACE_PATH = UMB_WORKSPACE_PATH_PATTERN.generat export const UMB_CREATE_MEMBER_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{ memberTypeUnique: string; }>('create/:memberTypeUnique', UMB_MEMBER_WORKSPACE_PATH); + +export const UMB_EDIT_MEMBER_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{ unique: string }>('edit/:unique'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/types.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/types.ts index 08536e1a3f..cf7867b4dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/types.ts @@ -1,4 +1,5 @@ import type { UmbRelationEntityType } from './entity.js'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import type { DefaultReferenceResponseModel, DocumentReferenceResponseModel, @@ -23,6 +24,9 @@ export interface UmbRelationDetailModel { comment: string | null; } +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface UmbReferenceItemModel extends UmbEntityModel {} + export type UmbReferenceModel = | DefaultReferenceResponseModel | DocumentReferenceResponseModel diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/item/user-item-ref.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/item/user-item-ref.element.ts index f910931ba4..f61cc1bec9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/item/user-item-ref.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/item/user-item-ref.element.ts @@ -1,7 +1,8 @@ import { UMB_USER_ENTITY_TYPE } from '../entity.js'; import type { UmbUserItemModel } from '../repository/index.js'; import { UMB_USER_MANAGEMENT_SECTION_ALIAS } from '../../section/constants.js'; -import { css, customElement, html, nothing, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UMB_EDIT_USER_WORKSPACE_PATH_PATTERN } from '../paths.js'; +import { css, customElement, html, ifDefined, nothing, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UMB_SECTION_USER_PERMISSION_CONDITION_ALIAS } from '@umbraco-cms/backoffice/section'; @@ -17,19 +18,7 @@ export class UmbUserItemRefElement extends UmbLitElement { return this.#item; } public set item(value: UmbUserItemModel | undefined) { - const oldValue = this.#item; this.#item = value; - - if (!this.#item) { - this.#modalRoute?.destroy(); - return; - } - - if (oldValue?.unique === this.#item.unique) { - return; - } - - this.#modalRoute?.setUniquePathValue('unique', this.#item.unique); } @property({ type: Boolean }) @@ -44,8 +33,6 @@ export class UmbUserItemRefElement extends UmbLitElement { @state() _userHasSectionAccess = false; - #modalRoute?: any; - constructor() { super(); @@ -60,9 +47,7 @@ export class UmbUserItemRefElement extends UmbLitElement { }, ]); - this.#modalRoute = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath(UMB_USER_ENTITY_TYPE) - .addUniquePaths(['unique']) + new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) .onSetup(() => { return { data: { entityType: UMB_USER_ENTITY_TYPE, preset: {} } }; }) @@ -72,7 +57,9 @@ export class UmbUserItemRefElement extends UmbLitElement { } #getHref(item: UmbUserItemModel) { - return `${this._editPath}/edit/${item.unique}`; + if (!this._editPath) return; + const path = UMB_EDIT_USER_WORKSPACE_PATH_PATTERN.generateLocal({ unique: item.unique }); + return `${this._editPath}/${path}`; } override render() { @@ -81,7 +68,7 @@ export class UmbUserItemRefElement extends UmbLitElement { return html` ('edit/:unique');