diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index f012dd2ac180..369297a9bb02 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -45,6 +45,7 @@ "./entity-bulk-action": "./dist-cms/packages/core/entity-bulk-action/index.js", "./entity-create-option-action": "./dist-cms/packages/core/entity-create-option-action/index.js", "./entity": "./dist-cms/packages/core/entity/index.js", + "./entity-item": "./dist-cms/packages/core/entity-item/index.js", "./event": "./dist-cms/packages/core/event/index.js", "./extension-registry": "./dist-cms/packages/core/extension-registry/index.js", "./health-check": "./dist-cms/packages/health-check/index.js", diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index 675909459358..310807b8bb8e 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts @@ -511,6 +511,10 @@ export default { confirmlogout: 'Are you sure?', confirmSure: 'Are you sure?', confirmTrash: (name: string) => `Are you sure you want to move ${name} to the Recycle Bin?`, + confirmBulkTrash: (total: number) => + `Are you sure you want to move ${total} ${total === 1 ? 'item' : 'items'} to the Recycle Bin?`, + confirmBulkDelete: (total: number) => + `Are you sure you want to delete ${total} ${total === 1 ? 'item' : 'items'}?`, cut: 'Cut', editDictionary: 'Edit dictionary item', editLanguage: 'Edit language', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-deleted.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-deleted.event.ts new file mode 100644 index 000000000000..9e2bfa0548ad --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-deleted.event.ts @@ -0,0 +1,10 @@ +import type { UmbEntityActionEventArgs } from './entity-action.event.js'; +import { UmbEntityActionEvent } from './entity-action.event.js'; + +export class UmbEntityDeletedEvent extends UmbEntityActionEvent { + static readonly TYPE = 'entity-deleted'; + + constructor(args: UmbEntityActionEventArgs) { + super(UmbEntityDeletedEvent.TYPE, args); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts index 37d12fa9f0da..886f167a4269 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts @@ -6,6 +6,7 @@ export * from './entity-action-base.js'; export * from './entity-action-list.element.js'; export * from './entity-action.event.js'; export * from './entity-updated.event.js'; +export * from './entity-deleted.event.js'; export type * from './types.js'; export { UmbRequestReloadStructureForEntityEvent } from './request-reload-structure-for-entity.event.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.kind.ts new file mode 100644 index 000000000000..c0a727b3e019 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.kind.ts @@ -0,0 +1,24 @@ +import { UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST } from '../../default/default.action.kind.js'; +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_ENTITY_BULK_ACTION_DELETE_KIND = 'delete'; + +export const UMB_ENTITY_BULK_ACTION_DELETE_KIND_MANIFEST: UmbExtensionManifestKind = { + type: 'kind', + alias: 'Umb.Kind.EntityBulkAction.Delete', + matchKind: UMB_ENTITY_BULK_ACTION_DELETE_KIND, + matchType: 'entityBulkAction', + manifest: { + ...UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST.manifest, + type: 'entityBulkAction', + kind: UMB_ENTITY_BULK_ACTION_DELETE_KIND, + api: () => import('./bulk-delete.action.js'), + weight: 1100, + meta: { + icon: 'icon-trash', + label: '#actions_delete', + }, + }, +}; + +export const manifest = UMB_ENTITY_BULK_ACTION_DELETE_KIND_MANIFEST; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.ts new file mode 100644 index 000000000000..8da90bffc561 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.ts @@ -0,0 +1,121 @@ +import { UmbEntityBulkActionBase } from '../../entity-bulk-action-base.js'; +import type { MetaEntityBulkActionDeleteKind } from './types.js'; +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; +import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { + UmbEntityDeletedEvent, + UmbRequestReloadChildrenOfEntityEvent, + UmbRequestReloadStructureForEntityEvent, +} from '@umbraco-cms/backoffice/entity-action'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import { UMB_ENTITY_CONTEXT, type UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import type { UmbDetailRepository, UmbItemRepository } from '@umbraco-cms/backoffice/repository'; + +export class UmbDeleteEntityBulkAction< + MetaKindType extends MetaEntityBulkActionDeleteKind = MetaEntityBulkActionDeleteKind, +> extends UmbEntityBulkActionBase { + #localize = new UmbLocalizationController(this); + _items: Array = []; + + override async execute() { + if (this.selection?.length === 0) { + throw new Error('No items selected.'); + } + + // TODO: Move item look up to a future bulk action context + await this.#requestItems(); + await this._confirmDelete(this._items); + await this.#requestBulkDelete(this.selection); + } + + protected async _confirmDelete(items: Array) { + const headline = '#actions_delete'; + const message = '#defaultdialogs_confirmBulkDelete'; + + // TODO: consider showing more details about the items being deleted + await umbConfirmModal(this._host, { + headline, + content: this.#localize.string(message, items.length), + color: 'danger', + confirmLabel: '#actions_delete', + }); + } + + async #requestItems() { + const itemRepository = await createExtensionApiByAlias>( + this, + this.args.meta.itemRepositoryAlias, + ); + + const { data } = await itemRepository.requestItems(this.selection); + + this._items = data ?? []; + } + + async #requestBulkDelete(uniques: Array) { + const detailRepository = await createExtensionApiByAlias>( + this, + this.args.meta.detailRepositoryAlias, + ); + + const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + + const succeeded: Array = []; + + for (const unique of uniques) { + const { error } = await detailRepository.delete(unique); + + if (error) { + const notification = { data: { message: error.message } }; + notificationContext?.peek('danger', notification); + } else { + succeeded.push(unique); + } + } + + if (succeeded.length > 0) { + const notification = { + data: { message: `Deleted ${succeeded.length} ${succeeded.length === 1 ? 'item' : 'items'}` }, + }; + notificationContext?.peek('positive', notification); + } + + await this.#notify(succeeded); + } + + async #notify(succeeded: Array) { + const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + if (!entityContext) throw new Error('Entity Context is not available'); + + const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event Context is not available'); + + const entityType = entityContext.getEntityType(); + const unique = entityContext.getUnique(); + + if (entityType && unique !== undefined) { + const args = { entityType, unique }; + + const reloadChildren = new UmbRequestReloadChildrenOfEntityEvent(args); + eventContext.dispatchEvent(reloadChildren); + + const reloadStructure = new UmbRequestReloadStructureForEntityEvent(args); + eventContext.dispatchEvent(reloadStructure); + } + + const succeededItems = this._items.filter((item) => succeeded.includes(item.unique)); + + succeededItems.forEach((item) => { + const deletedEvent = new UmbEntityDeletedEvent({ + unique: item.unique, + entityType: item.entityType, + }); + + eventContext.dispatchEvent(deletedEvent); + }); + } +} + +export { UmbDeleteEntityBulkAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/constants.ts new file mode 100644 index 000000000000..b88413e675f9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/constants.ts @@ -0,0 +1,4 @@ +export { + UMB_ENTITY_BULK_ACTION_DELETE_KIND_MANIFEST, + UMB_ENTITY_BULK_ACTION_DELETE_KIND, +} from './bulk-delete.action.kind.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/index.ts new file mode 100644 index 000000000000..07e2f108e454 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/index.ts @@ -0,0 +1,2 @@ +export * from './bulk-delete.action.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/manifests.ts new file mode 100644 index 000000000000..558f12b16c96 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/manifests.ts @@ -0,0 +1,4 @@ +import { manifest as deleteKindManifest } from './bulk-delete.action.kind.js'; +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [deleteKindManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/types.ts new file mode 100644 index 000000000000..17ff0c6f4834 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/types.ts @@ -0,0 +1,20 @@ +import type { + ManifestEntityBulkAction, + MetaEntityBulkActionDefaultKind, +} from '@umbraco-cms/backoffice/extension-registry'; + +export interface ManifestEntityBulkActionDeleteKind extends ManifestEntityBulkAction { + type: 'entityBulkAction'; + kind: 'delete'; +} + +export interface MetaEntityBulkActionDeleteKind extends MetaEntityBulkActionDefaultKind { + detailRepositoryAlias: string; + itemRepositoryAlias: string; +} + +declare global { + interface UmbExtensionManifestMap { + umbManifestEntityBulkActionDeleteKind: ManifestEntityBulkActionDeleteKind; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/constants.ts new file mode 100644 index 000000000000..f919f4aea61b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/constants.ts @@ -0,0 +1 @@ +export const UMB_ENTITY_BULK_ACTION_TRASH_KIND = 'trash'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts index 195b2f772480..8570bfc8ad77 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts @@ -1,7 +1,12 @@ import type { UmbRepositoryErrorResponse } from '../../../repository/types.js'; import type { UmbBulkTrashRequestArgs } from './types.js'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; - +/** + * @deprecated since 15.3.0. Will be removed in 17.0.0. Call trash method on UmbRecycleBin repositories instead. + * @exports + * @interface UmbBulkTrashRepository + * @augments UmbApi + */ export interface UmbBulkTrashRepository extends UmbApi { requestBulkTrash(args: UmbBulkTrashRequestArgs): Promise; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts index 2c36d6dc35f3..dd6d35119250 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts @@ -1,15 +1,16 @@ import { UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST } from '../../default/default.action.kind.js'; +import { UMB_ENTITY_BULK_ACTION_TRASH_KIND } from './constants.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifest: UmbExtensionManifestKind = { type: 'kind', alias: 'Umb.Kind.EntityBulkAction.Trash', - matchKind: 'trash', + matchKind: UMB_ENTITY_BULK_ACTION_TRASH_KIND, matchType: 'entityBulkAction', manifest: { ...UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST.manifest, type: 'entityBulkAction', - kind: 'trash', + kind: UMB_ENTITY_BULK_ACTION_TRASH_KIND, api: () => import('./trash.action.js'), weight: 700, forEntityTypes: [], diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.ts index 70cbc7ffd501..84d27501ccb5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/trash.action.ts @@ -1,6 +1,6 @@ import type { UmbBulkTrashRepository } from './trash-repository.interface.js'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action'; +import { UmbEntityBulkActionBase, type UmbEntityBulkActionArgs } from '@umbraco-cms/backoffice/entity-bulk-action'; import { UmbRequestReloadChildrenOfEntityEvent, UmbRequestReloadStructureForEntityEvent, @@ -9,8 +9,26 @@ import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; import type { MetaEntityBulkActionTrashKind } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; +/** + * @deprecated since v15.3.0. Will be removed in v17.0.0. import `UmbMediaTrashEntityBulkAction` from @umbraco-cms/backoffice/recycle-bin instead. + * @exports + * @class UmbMediaTrashEntityBulkAction + * @augments {UmbEntityBulkActionBase} + */ export class UmbMediaTrashEntityBulkAction extends UmbEntityBulkActionBase { + constructor(host: UmbControllerHost, args: UmbEntityBulkActionArgs) { + super(host, args); + + new UmbDeprecation({ + removeInVersion: '17.0.0', + deprecated: 'UmbMediaTrashEntityBulkAction', + solution: 'import UmbMediaTrashEntityBulkAction from @umbraco-cms/backoffice/recycle-bin instead.', + }).warn(); + } + async execute() { if (this.selection?.length === 0) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/types.ts similarity index 73% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/types.ts index e3330f3081fa..7dd2feb9d193 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/types.ts @@ -1,3 +1,4 @@ export type * from './duplicate-to/index.js'; export type * from './move-to/index.js'; export type * from './trash/index.js'; +export type * from './bulk-delete/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/index.ts index bcc30c388e3f..91126a472751 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/index.ts @@ -1,8 +1,12 @@ -export type * from './common/index.js'; export * from './entity-bulk-action-base.js'; export * from './entity-bulk-action.element.js'; -export type * from './entity-bulk-action.interface.js'; -export type * from './entity-bulk-action-element.interface.js'; +export * from './common/bulk-delete/index.js'; export type * from './types.js'; export { UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST } from './default/default.action.kind.js'; +export { + UMB_ENTITY_BULK_ACTION_DELETE_KIND_MANIFEST, + UMB_ENTITY_BULK_ACTION_DELETE_KIND, +} from './common/bulk-delete/bulk-delete.action.kind.js'; + +export { UMB_ENTITY_BULK_ACTION_TRASH_KIND } from './common/trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/manifests.ts index 57fab22a3d4c..2d3fde2e9296 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/manifests.ts @@ -2,6 +2,8 @@ import { manifests as defaultEntityBulkActionManifests } from './default/manifes import { manifests as duplicateEntityBulkActionManifests } from './common/duplicate-to/manifests.js'; import { manifests as moveToEntityBulkActionManifests } from './common/move-to/manifests.js'; import { manifests as trashEntityBulkActionManifests } from './common/trash/manifests.js'; +import { manifests as deleteEntityBulkActionManifests } from './common/bulk-delete/manifests.js'; + import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ @@ -9,4 +11,5 @@ export const manifests: Array = ...duplicateEntityBulkActionManifests, ...moveToEntityBulkActionManifests, ...trashEntityBulkActionManifests, + ...deleteEntityBulkActionManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/types.ts index 49faf0bca553..6e7241553175 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/types.ts @@ -1,5 +1,9 @@ import type { MetaEntityBulkAction } from '../extension-registry/extensions/entity-bulk-action.extension.js'; +export type * from './common/types.js'; +export type * from './entity-bulk-action.interface.js'; +export type * from './entity-bulk-action-element.interface.js'; + export interface UmbEntityBulkActionArgs { entityType: string; meta: MetaArgsType; 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-item/entity-item-ref/default-item-ref.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/default-item-ref.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/default-item-ref.element.ts 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-item/entity-item-ref/entity-item-ref.element.ts similarity index 98% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/entity-item-ref.element.ts index de07c6a3d5f1..f239f3ebe474 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-item/entity-item-ref/entity-item-ref.element.ts @@ -1,13 +1,13 @@ -import type { UmbEntityModel } from '../types.js'; import type { ManifestEntityItemRef } from './entity-item-ref.extension.js'; import { customElement, property, type PropertyValueMap, state, css, html } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbExtensionsElementInitializer } from '@umbraco-cms/backoffice/extension-api'; import { UMB_MARK_ATTRIBUTE_NAME } from '@umbraco-cms/backoffice/const'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbRoutePathAddendumContext } from '@umbraco-cms/backoffice/router'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import './default-item-ref.element.js'; -import { UmbRoutePathAddendumContext } from '@umbraco-cms/backoffice/router'; @customElement('umb-entity-item-ref') export class UmbEntityItemRefElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/entity-item-ref.extension.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/entity-item-ref.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/entity-item-ref.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/global-components.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/global-components.ts new file mode 100644 index 000000000000..b04a6b66a05e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/global-components.ts @@ -0,0 +1 @@ +import './entity-item-ref.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/index.ts similarity index 53% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/index.ts index 338849e06997..0dfe5acf102a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/index.ts @@ -1,3 +1 @@ -import './entity-item-ref.element.js'; - export * from './entity-item-ref.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/types.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity/entity-item-ref/types.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/global-components.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/global-components.ts new file mode 100644 index 000000000000..a518da723360 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/global-components.ts @@ -0,0 +1 @@ +import './entity-item-ref/global-components.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/index.ts new file mode 100644 index 000000000000..5ada9eda75c7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/index.ts @@ -0,0 +1 @@ +export * from './entity-item-ref/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/types.ts new file mode 100644 index 000000000000..31240f7f67d9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-item/types.ts @@ -0,0 +1,5 @@ +import type { UmbNamedEntityModel } from '@umbraco-cms/backoffice/entity'; + +export interface UmbDefaultItemModel extends UmbNamedEntityModel { + icon?: string; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity/index.ts index 7fbfc9d7f634..737d7bf4828c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity/index.ts @@ -1,4 +1,3 @@ export { UMB_ENTITY_CONTEXT } from './entity.context-token.js'; export { UmbEntityContext } from './entity.context.js'; -export * from './entity-item-ref/index.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity/types.ts index 09ec5529e207..f2fcb7dde994 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity/types.ts @@ -8,7 +8,3 @@ export interface UmbEntityModel { export interface UmbNamedEntityModel extends UmbEntityModel { name: string; } - -export interface UmbDefaultItemModel extends UmbNamedEntityModel { - icon?: string; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts index df77e8a18b13..cf71700dced6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts @@ -9,6 +9,7 @@ import { UmbExtensionsApiInitializer, type UmbEntryPointOnInit } from '@umbraco- import './property-action/components/index.js'; import './menu/components/index.js'; import './extension-registry/components/index.js'; +import './entity-item/global-components.js'; export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => { new UmbExtensionsApiInitializer(host, extensionRegistry, 'globalContext', [host]); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extensions/entity-bulk-action.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extensions/entity-bulk-action.extension.ts index 969a1e28a638..4d5b2adcb532 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extensions/entity-bulk-action.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extensions/entity-bulk-action.extension.ts @@ -17,7 +17,9 @@ export interface ManifestEntityBulkAction { +export interface ManifestEntityBulkActionDefaultKind< + MetaKindType extends MetaEntityBulkActionDefaultKind = MetaEntityBulkActionDefaultKind, +> extends ManifestEntityBulkAction { type: 'entityBulkAction'; kind: 'default'; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/constants.ts index 27f0cb615081..19138819b598 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/constants.ts @@ -2,3 +2,4 @@ export * from './conditions/is-not-trashed/constants.js'; export * from './conditions/is-trashed/constants.js'; export * from './contexts/is-trashed/constants.js'; export * from './entity-action/constants.js'; +export * from './entity-bulk-action/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.kind.ts new file mode 100644 index 000000000000..1e659cc7e839 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.kind.ts @@ -0,0 +1,29 @@ +import { UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST } from '@umbraco-cms/backoffice/entity-bulk-action'; +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +/* TODO: v17: rename kind to trash + this is named v2 to avoid a name clash. The original trash kind is deprecated. +We have added a constant to try and prevent too big a breaking change when renaming. */ +export const UMB_ENTITY_BULK_ACTION_TRASH_KIND = 'trashV2'; + +export const UMB_ENTITY_BULK_ACTION_TRASH_KIND_MANIFEST: UmbExtensionManifestKind = { + type: 'kind', + alias: 'Umb.Kind.EntityBulkAction.Trash', + matchKind: UMB_ENTITY_BULK_ACTION_TRASH_KIND, + matchType: 'entityBulkAction', + manifest: { + ...UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST.manifest, + type: 'entityBulkAction', + kind: UMB_ENTITY_BULK_ACTION_TRASH_KIND, + api: () => import('./bulk-trash.action.js'), + weight: 1150, + meta: { + icon: 'icon-trash', + label: '#actions_trash', + itemRepositoryAlias: '', + recycleBinRepositoryAlias: '', + }, + }, +}; + +export const manifest = UMB_ENTITY_BULK_ACTION_TRASH_KIND_MANIFEST; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.ts new file mode 100644 index 000000000000..384e111ed5eb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.ts @@ -0,0 +1,122 @@ +import type { UmbRecycleBinRepository } from '../../recycle-bin-repository.interface.js'; +import { UmbEntityTrashedEvent } from '../../entity-action/trash/index.js'; +import type { MetaEntityBulkActionTrashKind } from './types.js'; +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; +import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { + UmbRequestReloadChildrenOfEntityEvent, + UmbRequestReloadStructureForEntityEvent, +} from '@umbraco-cms/backoffice/entity-action'; +import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; +import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; + +export class UmbTrashEntityBulkAction< + MetaKindType extends MetaEntityBulkActionTrashKind = MetaEntityBulkActionTrashKind, +> extends UmbEntityBulkActionBase { + #localize = new UmbLocalizationController(this); + _items: Array = []; + + override async execute() { + if (this.selection?.length === 0) { + throw new Error('No items selected.'); + } + + // TODO: Move item look up to a future bulk action context + await this.#requestItems(); + await this._confirmTrash(this._items); + await this.#requestBulkTrash(this.selection); + } + + protected async _confirmTrash(items: Array) { + const headline = '#actions_trash'; + const message = '#defaultdialogs_confirmBulkTrash'; + + // TODO: consider showing more details about the items being trashed + await umbConfirmModal(this._host, { + headline, + content: this.#localize.string(message, items.length), + color: 'danger', + confirmLabel: '#actions_trash', + }); + } + + async #requestItems() { + const itemRepository = await createExtensionApiByAlias>( + this, + this.args.meta.itemRepositoryAlias, + ); + + const { data } = await itemRepository.requestItems(this.selection); + + this._items = data ?? []; + } + + async #requestBulkTrash(uniques: Array) { + const recycleBinRepository = await createExtensionApiByAlias( + this, + this.args.meta.recycleBinRepositoryAlias, + ); + + const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + + const succeeded: Array = []; + + for (const unique of uniques) { + const { error } = await recycleBinRepository.requestTrash({ unique }); + + if (error) { + const notification = { data: { message: error.message } }; + notificationContext?.peek('danger', notification); + } else { + succeeded.push(unique); + } + } + + if (succeeded.length > 0) { + const notification = { + data: { message: `Trashed ${succeeded.length} ${succeeded.length === 1 ? 'item' : 'items'}` }, + }; + notificationContext?.peek('positive', notification); + } + + await this.#notify(succeeded); + } + + async #notify(succeeded: Array) { + const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + if (!entityContext) throw new Error('Entity Context is not available'); + + const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event Context is not available'); + + const entityType = entityContext.getEntityType(); + const unique = entityContext.getUnique(); + + if (entityType && unique !== undefined) { + const args = { entityType, unique }; + + const reloadChildren = new UmbRequestReloadChildrenOfEntityEvent(args); + eventContext.dispatchEvent(reloadChildren); + + const reloadStructure = new UmbRequestReloadStructureForEntityEvent(args); + eventContext.dispatchEvent(reloadStructure); + } + + const succeededItems = this._items.filter((item) => succeeded.includes(item.unique)); + + succeededItems.forEach((item) => { + const trashedEvent = new UmbEntityTrashedEvent({ + unique: item.unique, + entityType: item.entityType, + }); + + eventContext.dispatchEvent(trashedEvent); + }); + } +} + +export { UmbTrashEntityBulkAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/constants.ts new file mode 100644 index 000000000000..65c101c9fbc7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/constants.ts @@ -0,0 +1,4 @@ +export { + UMB_ENTITY_BULK_ACTION_TRASH_KIND_MANIFEST, + UMB_ENTITY_BULK_ACTION_TRASH_KIND, +} from './bulk-trash.action.kind.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/index.ts new file mode 100644 index 000000000000..a281a0235157 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/index.ts @@ -0,0 +1,2 @@ +export * from './bulk-trash.action.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/manifests.ts new file mode 100644 index 000000000000..066b290bebb2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/manifests.ts @@ -0,0 +1,4 @@ +import { manifest as trashKindManifest } from './bulk-trash.action.kind.js'; +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [trashKindManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/types.ts new file mode 100644 index 000000000000..8a8492c24ce1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/types.ts @@ -0,0 +1,20 @@ +import type { + ManifestEntityBulkAction, + MetaEntityBulkActionDefaultKind, +} from '@umbraco-cms/backoffice/extension-registry'; + +export interface ManifestEntityBulkActionTrashKind extends ManifestEntityBulkAction { + type: 'entityBulkAction'; + kind: 'trash'; +} + +export interface MetaEntityBulkActionTrashKind extends MetaEntityBulkActionDefaultKind { + recycleBinRepositoryAlias: string; + itemRepositoryAlias: string; +} + +declare global { + interface UmbExtensionManifestMap { + umbManifestEntityBulkActionTrashKind: ManifestEntityBulkActionTrashKind; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/constants.ts new file mode 100644 index 000000000000..e96d7ef38da7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/constants.ts @@ -0,0 +1 @@ +export * from './bulk-trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/index.ts new file mode 100644 index 000000000000..b88b7cd9add9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/index.ts @@ -0,0 +1 @@ +export * from './bulk-trash/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/types.ts new file mode 100644 index 000000000000..83d23b4b0c82 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/types.ts @@ -0,0 +1 @@ +export type * from './bulk-trash/types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/index.ts index e27c2c13ac53..83cdb218b85a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/index.ts @@ -1,5 +1,6 @@ -export * from './entity-action/index.js'; export * from './constants.js'; +export * from './entity-action/index.js'; +export * from './entity-bulk-action/index.js'; export type * from './types.js'; export { UmbRecycleBinRepositoryBase } from './recycle-bin-repository-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/manifests.ts index f5fc0ea7da8c..b9eabac4a733 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/manifests.ts @@ -2,6 +2,7 @@ import { manifests as conditionManifests } from './conditions/manifests.js'; import { manifests as emptyRecycleBinEntityActionManifests } from './entity-action/empty-recycle-bin/manifests.js'; import { manifests as restoreFromRecycleBinEntityActionManifests } from './entity-action/restore-from-recycle-bin/manifests.js'; import { manifests as trashEntityActionManifests } from './entity-action/trash/manifests.js'; +import { manifests as trashEntityBulkActionManifests } from './entity-bulk-action/bulk-trash/manifests.js'; import { manifests as treeManifests } from './tree/manifests.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; @@ -11,5 +12,6 @@ export const manifests: Array = ...emptyRecycleBinEntityActionManifests, ...restoreFromRecycleBinEntityActionManifests, ...trashEntityActionManifests, + ...trashEntityBulkActionManifests, ...treeManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/recycle-bin-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/recycle-bin-repository-base.ts index b7c101717f5b..e246dac49d33 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/recycle-bin-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/recycle-bin-repository-base.ts @@ -8,6 +8,7 @@ import type { UmbRecycleBinRestoreRequestArgs, UmbRecycleBinTrashRequestArgs, } from './types.js'; +import type { UmbNotificationHandler } from '@umbraco-cms/backoffice/notification'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; @@ -21,6 +22,9 @@ import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; */ export abstract class UmbRecycleBinRepositoryBase extends UmbRepositoryBase implements UmbRecycleBinRepository { #recycleBinSource: UmbRecycleBinDataSource; + #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; + #requestTrashSuccessNotification?: UmbNotificationHandler; + #requestRestoreSuccessNotification?: UmbNotificationHandler; /** * Creates an instance of UmbRecycleBinRepositoryBase. @@ -31,6 +35,10 @@ export abstract class UmbRecycleBinRepositoryBase extends UmbRepositoryBase impl constructor(host: UmbControllerHost, recycleBinSource: UmbRecycleBinDataSourceConstructor) { super(host); this.#recycleBinSource = new recycleBinSource(this); + + this.consumeContext(UMB_NOTIFICATION_CONTEXT, (context) => { + this.#notificationContext = context; + }); } /** @@ -43,9 +51,9 @@ export abstract class UmbRecycleBinRepositoryBase extends UmbRepositoryBase impl const { error } = await this.#recycleBinSource.trash(args); if (!error) { - const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + this.#requestTrashSuccessNotification?.close(); const notification = { data: { message: `Trashed` } }; - notificationContext.peek('positive', notification); + this.#requestTrashSuccessNotification = this.#notificationContext?.peek('positive', notification); } return { error }; @@ -61,9 +69,9 @@ export abstract class UmbRecycleBinRepositoryBase extends UmbRepositoryBase impl const { error } = await this.#recycleBinSource.restore(args); if (!error) { - const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + this.#requestRestoreSuccessNotification?.close(); const notification = { data: { message: `Restored` } }; - notificationContext.peek('positive', notification); + this.#requestRestoreSuccessNotification = this.#notificationContext?.peek('positive', notification); } return { error }; @@ -78,9 +86,8 @@ export abstract class UmbRecycleBinRepositoryBase extends UmbRepositoryBase impl const { error } = await this.#recycleBinSource.empty(); if (!error) { - const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); const notification = { data: { message: `Recycle Bin Emptied` } }; - notificationContext.peek('positive', notification); + this.#notificationContext?.peek('positive', notification); } return this.#recycleBinSource.empty(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/tree/tree-item/recycle-bin-tree-item.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/tree/tree-item/recycle-bin-tree-item.context.ts index 50c6a06dfb64..ff1156ed0bbc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/tree/tree-item/recycle-bin-tree-item.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/tree/tree-item/recycle-bin-tree-item.context.ts @@ -4,6 +4,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbActionEventContext } from '@umbraco-cms/backoffice/action'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbEntityTrashedEvent } from '@umbraco-cms/backoffice/recycle-bin'; +import { debounce } from '@umbraco-cms/backoffice/utils'; export class UmbRecycleBinTreeItemContext< RecycleBinTreeItemModelType extends UmbTreeItemModel, @@ -25,6 +26,8 @@ export class UmbRecycleBinTreeItemContext< }); } + #debounceLoadChildren = debounce(() => this.loadChildren(), 100); + #onEntityTrashed = (event: UmbEntityTrashedEvent) => { const entityType = event.getEntityType(); if (!entityType) throw new Error('Entity type is required'); @@ -36,7 +39,7 @@ export class UmbRecycleBinTreeItemContext< } if (supportedEntityTypes.includes(entityType)) { - this.loadChildren(); + this.#debounceLoadChildren(); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/types.ts index 587bcc35d1b7..52db4d7f185a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/types.ts @@ -1,5 +1,6 @@ export type * from './conditions/types.js'; export type * from './entity-action/types.js'; +export type * from './entity-bulk-action/types.js'; export type { UmbRecycleBinDataSource } from './recycle-bin-data-source.interface.js'; export type { UmbRecycleBinRepository } from './recycle-bin-repository.interface.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 index 25d3b5812fab..52e1d472b861 100644 --- 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 @@ -1 +1 @@ -export const UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER = 'Umb.ManagementApi'; +export const UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS = '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 index d220ee390dd8..9df1eb8da1b5 100644 --- 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 @@ -1,5 +1,5 @@ import { UmbDataMapper, type UmbDataMapperMapArgs } from '../data-mapper.js'; -import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from './constants.js'; +import { UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS } from './constants.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -13,7 +13,7 @@ export class UmbManagementApiDataMapper extends UmbControllerBase { map(args: Omit) { return this.#dataMapper.map({ ...args, - forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER, + forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS, }); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts index 12a77e5437a8..831cdab778a8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts @@ -3,7 +3,7 @@ import type { UmbRepositoryResponse, UmbRepositoryResponseWithAsObservable } fro import type { UmbDetailDataSource, UmbDetailDataSourceConstructor } from './detail-data-source.interface.js'; import type { UmbDetailRepository } from './detail-repository.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; +import type { UmbNotificationContext, UmbNotificationHandler } from '@umbraco-cms/backoffice/notification'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import type { UmbDetailStore } from '@umbraco-cms/backoffice/store'; @@ -22,6 +22,9 @@ export abstract class UmbDetailRepositoryBase< #detailStore?: UmbDetailStore; protected detailDataSource: UmbDetailDataSourceType; #notificationContext?: UmbNotificationContext; + #createSuccessNotification?: UmbNotificationHandler; + #updateSuccessNotification?: UmbNotificationHandler; + #deleteSuccessNotification?: UmbNotificationHandler; constructor( host: UmbControllerHost, @@ -94,10 +97,10 @@ export abstract class UmbDetailRepositoryBase< if (createdData) { this.#detailStore?.append(createdData); - + this.#createSuccessNotification?.close(); // TODO: how do we handle generic notifications? Is this the correct place to do it? const notification = { data: { message: `Created` } }; - this.#notificationContext!.peek('positive', notification); + this.#createSuccessNotification = this.#notificationContext!.peek('positive', notification); } return { data: createdData, error }; @@ -120,8 +123,9 @@ export abstract class UmbDetailRepositoryBase< this.#detailStore!.updateItem(model.unique, updatedData); // TODO: how do we handle generic notifications? Is this the correct place to do it? + this.#updateSuccessNotification?.close(); const notification = { data: { message: `Saved` } }; - this.#notificationContext!.peek('positive', notification); + this.#updateSuccessNotification = this.#notificationContext!.peek('positive', notification); } return { data: updatedData, error }; @@ -142,9 +146,10 @@ export abstract class UmbDetailRepositoryBase< if (!error) { this.#detailStore!.removeItem(unique); + this.#deleteSuccessNotification?.close(); // TODO: how do we handle generic notifications? Is this the correct place to do it? const notification = { data: { message: `Deleted` } }; - this.#notificationContext!.peek('positive', notification); + this.#deleteSuccessNotification = this.#notificationContext!.peek('positive', notification); } return { error }; 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 893def16fc7f..691e31b0b315 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 @@ -26,6 +26,7 @@ export default defineConfig({ 'entity-bulk-action/index': './entity-bulk-action/index.ts', 'entity-create-option-action/index': './entity-create-option-action/index.ts', 'entity/index': './entity/index.ts', + 'entity-item/index': './entity-item/index.ts', 'entry-point': 'entry-point.ts', 'event/index': './event/index.ts', 'extension-registry/index': './extension-registry/index.ts', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/constants.ts index 8d305a645829..8d2371e6f323 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/constants.ts @@ -1,3 +1,2 @@ export * from './duplicate-to/constants.js'; export * from './move-to/constants.js'; -export * from './trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/manifests.ts index 65ee8658fc9a..b67ea4c1a08f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/manifests.ts @@ -1,5 +1,4 @@ import { manifests as duplicateToManifests } from './duplicate-to/manifests.js'; import { manifests as moveToManifests } from './move-to/manifests.js'; -import { manifests as trashManifests } from './trash/manifests.js'; -export const manifests: Array = [...duplicateToManifests, ...moveToManifests, ...trashManifests]; +export const manifests: Array = [...duplicateToManifests, ...moveToManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/manifests.ts deleted file mode 100644 index d1ff3f6058e5..000000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/manifests.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { UMB_DOCUMENT_COLLECTION_ALIAS } from '../../collection/constants.js'; -import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js'; -import { UMB_USER_PERMISSION_DOCUMENT_DELETE } from '../../user-permissions/constants.js'; -import { UMB_BULK_TRASH_DOCUMENT_REPOSITORY_ALIAS } from './repository/constants.js'; -import { manifests as repositoryManifests } from './repository/manifests.js'; -import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; - -export const manifests: Array = [ - { - type: 'entityBulkAction', - kind: 'trash', - alias: 'Umb.EntityBulkAction.Document.Trash', - name: 'Trash Document Entity Bulk Action', - weight: 10, - forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE], - meta: { - bulkTrashRepositoryAlias: UMB_BULK_TRASH_DOCUMENT_REPOSITORY_ALIAS, - }, - conditions: [ - { - alias: UMB_COLLECTION_ALIAS_CONDITION, - match: UMB_DOCUMENT_COLLECTION_ALIAS, - }, - { - alias: 'Umb.Condition.UserPermission.Document', - allOf: [UMB_USER_PERMISSION_DOCUMENT_DELETE], - }, - ], - }, - ...repositoryManifests, -]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/constants.ts index 0002782c2b1c..9af32d7bb73a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/constants.ts @@ -1,3 +1,4 @@ +export * from './entity-action/constants.js'; export * from './repository/constants.js'; export * from './tree/constants.js'; export const UMB_DOCUMENT_RECYCLE_BIN_ROOT_ENTITY_TYPE = 'document-recycle-bin-root'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/constants.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/constants.ts rename to src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/manifests.ts new file mode 100644 index 000000000000..2ac56f94f52d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/manifests.ts @@ -0,0 +1,36 @@ +import { UMB_USER_PERMISSION_DOCUMENT_DELETE } from '../../../user-permissions/constants.js'; +import { UMB_DOCUMENT_ENTITY_TYPE } from '../../../entity.js'; +import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../../item/constants.js'; +import { UMB_DOCUMENT_RECYCLE_BIN_REPOSITORY_ALIAS } from '../../repository/constants.js'; +import { UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS } from '../../../reference/constants.js'; +import { UMB_DOCUMENT_COLLECTION_ALIAS } from '../../../collection/constants.js'; +import { manifests as repositoryManifests } from './repository/manifests.js'; +import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; +import { UMB_ENTITY_BULK_ACTION_TRASH_WITH_RELATION_KIND } from '@umbraco-cms/backoffice/relations'; + +export const manifests: Array = [ + { + type: 'entityBulkAction', + kind: UMB_ENTITY_BULK_ACTION_TRASH_WITH_RELATION_KIND, + alias: 'Umb.EntityBulkAction.Document.Trash', + name: 'Trash Document Entity Bulk Action', + weight: 10, + forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE], + meta: { + itemRepositoryAlias: UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS, + recycleBinRepositoryAlias: UMB_DOCUMENT_RECYCLE_BIN_REPOSITORY_ALIAS, + referenceRepositoryAlias: UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS, + }, + conditions: [ + { + alias: UMB_COLLECTION_ALIAS_CONDITION, + match: UMB_DOCUMENT_COLLECTION_ALIAS, + }, + { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_DELETE], + }, + ], + }, + ...repositoryManifests, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/constants.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/constants.ts rename to src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/trash.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/trash.repository.ts similarity index 78% rename from src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/trash.repository.ts rename to src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/trash.repository.ts index edd03d8400fd..43c2ed8c1dc9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/repository/trash.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/repository/trash.repository.ts @@ -1,10 +1,14 @@ -import { UmbDocumentRecycleBinServerDataSource } from '../../../recycle-bin/repository/document-recycle-bin.server.data-source.js'; +import { UmbDocumentRecycleBinServerDataSource } from '../../../repository/document-recycle-bin.server.data-source.js'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbBulkTrashRepository, UmbBulkTrashRequestArgs } from '@umbraco-cms/backoffice/entity-bulk-action'; import type { UmbRepositoryErrorResponse } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; +/** + * @deprecated since 15.3.0. Will be removed in 17.0.0. Call trash method on UmbDocumentRecycleBinRepository instead. + */ export class UmbBulkTrashDocumentRepository extends UmbRepositoryBase implements UmbBulkTrashRepository { #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; #recycleBinSource = new UmbDocumentRecycleBinServerDataSource(this); @@ -12,6 +16,12 @@ export class UmbBulkTrashDocumentRepository extends UmbRepositoryBase implements constructor(host: UmbControllerHost) { super(host); + new UmbDeprecation({ + removeInVersion: '17.0.0', + deprecated: 'UmbBulkTrashDocumentRepository', + solution: 'Call trash method on UmbDocumentRecycleBinRepository instead.', + }).warn(); + this.consumeContext(UMB_NOTIFICATION_CONTEXT, (notificationContext) => { this.#notificationContext = notificationContext; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/constants.ts new file mode 100644 index 000000000000..e96d7ef38da7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/constants.ts @@ -0,0 +1 @@ +export * from './bulk-trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/manifests.ts index db98e6e0bbef..6c4cf7b4b217 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/manifests.ts @@ -7,6 +7,7 @@ import { } from '../../constants.js'; import { UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS } from '../../reference/constants.js'; import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../item/constants.js'; +import { manifests as bulkTrashManifests } from './bulk-trash/manifests.js'; import { UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS, UMB_ENTITY_IS_TRASHED_CONDITION_ALIAS, @@ -71,4 +72,5 @@ export const manifests: Array = [ }, ], }, + ...bulkTrashManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.repository.ts index fa3b1f6dbe3a..0f1ae193f249 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.repository.ts @@ -16,6 +16,11 @@ export class UmbDocumentReferenceRepository extends UmbControllerBase implements return this.#referenceSource.getReferencedBy(unique, skip, take); } + async requestAreReferenced(uniques: Array, skip = 0, take = 20) { + if (!uniques || uniques.length === 0) throw new Error(`uniques is required`); + return this.#referenceSource.getAreReferenced(uniques, skip, take); + } + async requestDescendantsWithReferences(unique: string, skip = 0, take = 20) { if (!unique) throw new Error(`unique is required`); return this.#referenceSource.getReferencedDescendants(unique, skip, take); 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 dd19c40ddf65..3b2f7d3de705 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 @@ -9,7 +9,7 @@ import { UmbManagementApiDataMapper } from '@umbraco-cms/backoffice/repository'; /** * @class UmbDocumentReferenceServerDataSource - * @implements {RepositoryDetailDataSource} + * @implements {UmbEntityReferenceDataSource} */ export class UmbDocumentReferenceServerDataSource extends UmbControllerBase implements UmbEntityReferenceDataSource { #dataMapper = new UmbManagementApiDataMapper(this); @@ -55,6 +55,38 @@ export class UmbDocumentReferenceServerDataSource extends UmbControllerBase impl return { data, error }; } + /** + * Checks if the items are referenced by other items + * @param {Array} uniques - The unique identifiers of the items to fetch + * @param {number} skip - The number of items to skip + * @param {number} take - The number of items to take + * @returns {Promise>>} - Items that are referenced by other items + * @memberof UmbDocumentReferenceServerDataSource + */ + async getAreReferenced( + uniques: Array, + skip: number = 0, + take: number = 20, + ): Promise>> { + const { data, error } = await tryExecuteAndNotify( + this, + DocumentService.getDocumentAreReferenced({ id: uniques, skip, take }), + ); + + if (data) { + const items: Array = data.items.map((item) => { + return { + unique: item.id, + entityType: UMB_DOCUMENT_ENTITY_TYPE, + }; + }); + + return { data: { items, total: data.total } }; + } + + return { data, error }; + } + /** * Returns any descendants of the given unique that is referenced by other items * @param {string} unique - The unique identifier of the item to fetch descendants for 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 8e2dc6740b88..ce1cd3712f82 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,5 +1,5 @@ import { UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS } from './constants.js'; -import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from '@umbraco-cms/backoffice/repository'; +import { UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS } from '@umbraco-cms/backoffice/repository'; export const manifests: Array = [ { @@ -13,7 +13,7 @@ export const manifests: Array = [ 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, + forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS, forDataModel: 'DocumentReferenceResponseModel', }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/constants.ts index 35432ce6cd17..5e9a915209f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/constants.ts @@ -1,2 +1 @@ export * from './move-to/constants.js'; -export * from './trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/manifests.ts index 4930ca05302c..397a31367809 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/manifests.ts @@ -1,4 +1,3 @@ import { manifests as moveToManifests } from './move-to/manifests.js'; -import { manifests as trashManifests } from './trash/manifests.js'; -export const manifests: Array = [...moveToManifests, ...trashManifests]; +export const manifests: Array = [...moveToManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/manifests.ts deleted file mode 100644 index 48bc80af0007..000000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/manifests.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { UMB_MEDIA_COLLECTION_ALIAS } from '../../constants.js'; -import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js'; -import { UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './constants.js'; -import { manifests as repositoryManifests } from './repository/manifests.js'; -import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; - -const bulkTrashAction: UmbExtensionManifest = { - type: 'entityBulkAction', - kind: 'trash', - alias: 'Umb.EntityBulkAction.Media.Trash', - name: 'Trash Media Entity Bulk Action', - weight: 10, - forEntityTypes: [UMB_MEDIA_ENTITY_TYPE], - meta: { - bulkTrashRepositoryAlias: UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS, - }, - conditions: [ - { - alias: UMB_COLLECTION_ALIAS_CONDITION, - match: UMB_MEDIA_COLLECTION_ALIAS, - }, - ], -}; - -export const manifests: Array = [bulkTrashAction, ...repositoryManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/constants.ts index e05617eb5acb..a77985d16439 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/constants.ts @@ -1,3 +1,4 @@ export * from './tree/constants.js'; export * from './repository/constants.js'; +export * from './entity-action/constants.js'; export const UMB_MEDIA_RECYCLE_BIN_ROOT_ENTITY_TYPE = 'media-recycle-bin-root'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/constants.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/constants.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/manifests.ts new file mode 100644 index 000000000000..f1fccda364cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/manifests.ts @@ -0,0 +1,30 @@ +import { UMB_MEDIA_ENTITY_TYPE } from '../../../entity.js'; +import { UMB_MEDIA_ITEM_REPOSITORY_ALIAS } from '../../../repository/constants.js'; +import { UMB_MEDIA_RECYCLE_BIN_REPOSITORY_ALIAS } from '../../repository/constants.js'; +import { UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS } from '../../../reference/constants.js'; +import { UMB_MEDIA_COLLECTION_ALIAS } from '../../../collection/constants.js'; +import { manifests as repositoryManifests } from './repository/manifests.js'; +import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; +import { UMB_ENTITY_BULK_ACTION_TRASH_WITH_RELATION_KIND } from '@umbraco-cms/backoffice/relations'; + +const bulkTrashAction: UmbExtensionManifest = { + type: 'entityBulkAction', + kind: UMB_ENTITY_BULK_ACTION_TRASH_WITH_RELATION_KIND, + alias: 'Umb.EntityBulkAction.Media.Trash', + name: 'Trash Media Entity Bulk Action', + weight: 10, + forEntityTypes: [UMB_MEDIA_ENTITY_TYPE], + meta: { + itemRepositoryAlias: UMB_MEDIA_ITEM_REPOSITORY_ALIAS, + recycleBinRepositoryAlias: UMB_MEDIA_RECYCLE_BIN_REPOSITORY_ALIAS, + referenceRepositoryAlias: UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS, + }, + conditions: [ + { + alias: UMB_COLLECTION_ALIAS_CONDITION, + match: UMB_MEDIA_COLLECTION_ALIAS, + }, + ], +}; + +export const manifests: Array = [bulkTrashAction, ...repositoryManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/constants.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/constants.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/constants.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/trash.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/trash.repository.ts similarity index 75% rename from src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/trash.repository.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/trash.repository.ts index d84cd3e95603..f75ccf79897e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/repository/trash.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/repository/trash.repository.ts @@ -1,10 +1,14 @@ -import { UmbMediaRecycleBinServerDataSource } from '../../../recycle-bin/repository/media-recycle-bin.server.data-source.js'; +import { UmbMediaRecycleBinServerDataSource } from '../../../repository/media-recycle-bin.server.data-source.js'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbBulkTrashRepository, UmbBulkTrashRequestArgs } from '@umbraco-cms/backoffice/entity-bulk-action'; import type { UmbRepositoryErrorResponse } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; +/** + * @deprecated since 15.3.0. Will be removed in 17.0.0. Call trash method on UmbMediaRecycleBinRepository instead. + */ export class UmbBulkTrashMediaRepository extends UmbRepositoryBase implements UmbBulkTrashRepository { #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; #recycleBinSource = new UmbMediaRecycleBinServerDataSource(this); @@ -12,6 +16,12 @@ export class UmbBulkTrashMediaRepository extends UmbRepositoryBase implements Um constructor(host: UmbControllerHost) { super(host); + new UmbDeprecation({ + removeInVersion: '17.0.0', + deprecated: 'UmbBulkTrashDocumentRepository', + solution: 'Call trash method on UmbMediaRecycleBinRepository instead.', + }).warn(); + this.consumeContext(UMB_NOTIFICATION_CONTEXT, (notificationContext) => { this.#notificationContext = notificationContext; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/constants.ts new file mode 100644 index 000000000000..e96d7ef38da7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/constants.ts @@ -0,0 +1 @@ +export * from './bulk-trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/manifests.ts index cf87461c8924..65b3693975d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/manifests.ts @@ -5,6 +5,7 @@ import { } from '../../constants.js'; import { UMB_MEDIA_RECYCLE_BIN_ROOT_ENTITY_TYPE, UMB_MEDIA_RECYCLE_BIN_REPOSITORY_ALIAS } from '../constants.js'; import { UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS } from '../../reference/constants.js'; +import { manifests as bulkTrashManifests } from './bulk-trash/manifests.js'; import { UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS, UMB_ENTITY_IS_TRASHED_CONDITION_ALIAS, @@ -55,4 +56,5 @@ export const manifests: Array = [ recycleBinRepositoryAlias: UMB_MEDIA_RECYCLE_BIN_REPOSITORY_ALIAS, }, }, + ...bulkTrashManifests, ]; 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 c885ccb85c61..b623096738d7 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,5 +1,5 @@ import { UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS } from './constants.js'; -import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from '@umbraco-cms/backoffice/repository'; +import { UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS } from '@umbraco-cms/backoffice/repository'; export const manifests: Array = [ { @@ -13,7 +13,7 @@ export const manifests: Array = [ 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, + forDataSource: UMB_MANAGEMENT_API_DATA_SOURCE_ALIAS, forDataModel: 'MediaReferenceResponseModel', }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.repository.ts index ab5ac83a5fc7..53ed8b45e1d3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.repository.ts @@ -2,6 +2,8 @@ import { UmbMediaReferenceServerDataSource } from './media-reference.server.data import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbEntityReferenceRepository } from '@umbraco-cms/backoffice/relations'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import type { UmbRepositoryResponse, UmbPagedModel } from '@umbraco-cms/backoffice/repository'; export class UmbMediaReferenceRepository extends UmbControllerBase implements UmbEntityReferenceRepository { #referenceSource: UmbMediaReferenceServerDataSource; @@ -20,6 +22,15 @@ export class UmbMediaReferenceRepository extends UmbControllerBase implements Um if (!unique) throw new Error(`unique is required`); return this.#referenceSource.getReferencedDescendants(unique, skip, take); } + + async requestAreReferenced( + uniques: Array, + skip?: number, + take?: number, + ): Promise>> { + if (!uniques || uniques.length === 0) throw new Error(`uniques is required`); + return this.#referenceSource.getAreReferenced(uniques, skip, take); + } } export default UmbMediaReferenceRepository; 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 c3ea9207534d..6f169f00c41a 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 @@ -9,7 +9,7 @@ import type { UmbPagedModel, UmbDataSourceResponse } from '@umbraco-cms/backoffi /** * @class UmbMediaReferenceServerDataSource - * @implements {RepositoryDetailDataSource} + * @implements {UmbEntityReferenceDataSource} */ export class UmbMediaReferenceServerDataSource extends UmbControllerBase implements UmbEntityReferenceDataSource { #dataMapper = new UmbManagementApiDataMapper(this); @@ -55,6 +55,38 @@ export class UmbMediaReferenceServerDataSource extends UmbControllerBase impleme return { data, error }; } + /** + * Checks if the items are referenced by other items + * @param {Array} uniques - The unique identifiers of the items to fetch + * @param {number} skip - The number of items to skip + * @param {number} take - The number of items to take + * @returns {Promise>>} - Items that are referenced by other items + * @memberof UmbMediaReferenceServerDataSource + */ + async getAreReferenced( + uniques: Array, + skip: number = 0, + take: number = 20, + ): Promise>> { + const { data, error } = await tryExecuteAndNotify( + this, + MediaService.getMediaAreReferenced({ id: uniques, skip, take }), + ); + + if (data) { + const items: Array = data.items.map((item) => { + return { + unique: item.id, + entityType: UMB_MEDIA_ENTITY_TYPE, + }; + }); + + return { data: { items, total: data.total } }; + } + + return { data, error }; + } + /** * Returns any descendants of the given unique that is referenced by other items * @param {string} unique - The unique identifier of the item to fetch descendants for diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/constants.ts index 1ad52da7ec8a..6d7855c1514e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/constants.ts @@ -1,4 +1,6 @@ export { UMB_RELATION_ENTITY_TYPE } from './entity.js'; export * from './collection/constants.js'; +export * from './entity-actions/bulk-delete/constants.js'; +export * from './entity-actions/bulk-trash/constants.js'; export * from './entity-actions/delete/constants.js'; export * from './entity-actions/trash/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.kind.ts new file mode 100644 index 000000000000..177f452f1784 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.kind.ts @@ -0,0 +1,17 @@ +import { UMB_ENTITY_BULK_ACTION_DELETE_KIND_MANIFEST } from '@umbraco-cms/backoffice/entity-bulk-action'; +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_ENTITY_BULK_ACTION_DELETE_WITH_RELATION_KIND = 'deleteWithRelation'; + +export const manifest: UmbExtensionManifestKind = { + type: 'kind', + alias: 'Umb.Kind.EntityBulkAction.DeleteWithRelation', + matchKind: UMB_ENTITY_BULK_ACTION_DELETE_WITH_RELATION_KIND, + matchType: 'entityBulkAction', + manifest: { + ...UMB_ENTITY_BULK_ACTION_DELETE_KIND_MANIFEST.manifest, + type: 'entityBulkAction', + kind: UMB_ENTITY_BULK_ACTION_DELETE_WITH_RELATION_KIND, + api: () => import('./bulk-delete-with-relation.action.js'), + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts new file mode 100644 index 000000000000..71b26bf65337 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts @@ -0,0 +1,22 @@ +import type { MetaEntityBulkActionDeleteWithRelationKind } from './types.js'; +import { UMB_BULK_DELETE_WITH_RELATION_CONFIRM_MODAL } from './modal/bulk-delete-with-relation-modal.token.js'; +import { UmbDeleteEntityBulkAction } from '@umbraco-cms/backoffice/entity-bulk-action'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export class UmbBulkDeleteWithRelationEntityAction extends UmbDeleteEntityBulkAction { + override async _confirmDelete() { + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + + const modal = modalManager.open(this, UMB_BULK_DELETE_WITH_RELATION_CONFIRM_MODAL, { + data: { + uniques: this.selection, + itemRepositoryAlias: this.args.meta.itemRepositoryAlias, + referenceRepositoryAlias: this.args.meta.referenceRepositoryAlias, + }, + }); + + await modal.onSubmit(); + } +} + +export { UmbBulkDeleteWithRelationEntityAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/constants.ts new file mode 100644 index 000000000000..255321445a42 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/constants.ts @@ -0,0 +1,2 @@ +export * from './modal/constants.js'; +export { UMB_ENTITY_BULK_ACTION_DELETE_WITH_RELATION_KIND } from './bulk-delete-with-relation.action.kind.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/index.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/index.ts new file mode 100644 index 000000000000..64a7503c0f05 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/index.ts @@ -0,0 +1,2 @@ +export * from './bulk-delete-with-relation.action.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/manifests.ts new file mode 100644 index 000000000000..9d922686b089 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/manifests.ts @@ -0,0 +1,9 @@ +import { manifest as deleteKindManifest } from './bulk-delete-with-relation.action.kind.js'; +import { manifests as modalManifests } from './modal/manifests.js'; + +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + deleteKindManifest, + ...modalManifests, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.element.ts new file mode 100644 index 000000000000..c7d2a22c79c0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.element.ts @@ -0,0 +1,86 @@ +import type { + UmbBulkDeleteWithRelationConfirmModalData, + UmbBulkDeleteWithRelationConfirmModalValue, +} from './bulk-delete-with-relation-modal.token.js'; +import { + html, + customElement, + css, + state, + type PropertyValues, + nothing, + unsafeHTML, +} from '@umbraco-cms/backoffice/external/lit'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; + +// import of local component +import '../../local-components/confirm-bulk-action-entity-references.element.js'; + +@customElement('umb-bulk-delete-with-relation-confirm-modal') +export class UmbBulkDeleteWithRelationConfirmModalElement extends UmbModalBaseElement< + UmbBulkDeleteWithRelationConfirmModalData, + UmbBulkDeleteWithRelationConfirmModalValue +> { + @state() + _referencesConfig?: any; + + protected override firstUpdated(_changedProperties: PropertyValues): void { + super.firstUpdated(_changedProperties); + this.#initData(); + } + + async #initData() { + if (!this.data) return; + + this._referencesConfig = { + uniques: this.data.uniques, + itemRepositoryAlias: this.data.itemRepositoryAlias, + referenceRepositoryAlias: this.data.referenceRepositoryAlias, + }; + } + + override render() { + const headline = this.localize.string('#actions_delete'); + const message = '#defaultdialogs_confirmBulkDelete'; + + return html` + +

${unsafeHTML(this.localize.string(message, this.data?.uniques.length))}

+ ${this._referencesConfig + ? html`` + : nothing} + + + + +
+ `; + } + + static override styles = [ + UmbTextStyles, + css` + uui-dialog-layout { + max-inline-size: 60ch; + } + `, + ]; +} + +export { UmbBulkDeleteWithRelationConfirmModalElement as element }; + +declare global { + interface HTMLElementTagNameMap { + 'umb-bulk-delete-with-relation-confirm-modal': UmbBulkDeleteWithRelationConfirmModalElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.token.ts new file mode 100644 index 000000000000..b66ee86cb194 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.token.ts @@ -0,0 +1,18 @@ +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export interface UmbBulkDeleteWithRelationConfirmModalData { + uniques: Array; + itemRepositoryAlias: string; + referenceRepositoryAlias: string; +} + +export type UmbBulkDeleteWithRelationConfirmModalValue = undefined; + +export const UMB_BULK_DELETE_WITH_RELATION_CONFIRM_MODAL = new UmbModalToken< + UmbBulkDeleteWithRelationConfirmModalData, + UmbBulkDeleteWithRelationConfirmModalValue +>('Umb.Modal.BulkDeleteWithRelation', { + modal: { + type: 'dialog', + }, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/constants.ts new file mode 100644 index 000000000000..8fcf44dcdad8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/constants.ts @@ -0,0 +1 @@ +export * from './bulk-delete-with-relation-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/manifests.ts new file mode 100644 index 000000000000..13a881cd09dc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/manifests.ts @@ -0,0 +1,8 @@ +export const manifests: Array = [ + { + type: 'modal', + alias: 'Umb.Modal.BulkDeleteWithRelation', + name: 'Bulk Delete With Relation Modal', + element: () => import('./bulk-delete-with-relation-modal.element.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/types.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/types.ts new file mode 100644 index 000000000000..b8388273627d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/types.ts @@ -0,0 +1,18 @@ +import type { MetaEntityBulkActionDeleteKind } from '@umbraco-cms/backoffice/entity-bulk-action'; +import type { ManifestEntityBulkAction } from '@umbraco-cms/backoffice/extension-registry'; + +export interface ManifestEntityBulkActionDeleteWithRelationKind + extends ManifestEntityBulkAction { + type: 'entityBulkAction'; + kind: 'deleteWithRelation'; +} + +export interface MetaEntityBulkActionDeleteWithRelationKind extends MetaEntityBulkActionDeleteKind { + referenceRepositoryAlias: string; +} + +declare global { + interface UmbExtensionManifestMap { + umbManifestEntityBulkActionDeleteWithRelationKind: ManifestEntityBulkActionDeleteWithRelationKind; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.kind.ts new file mode 100644 index 000000000000..88b74d93c86e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.kind.ts @@ -0,0 +1,17 @@ +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; +import { UMB_ENTITY_BULK_ACTION_TRASH_KIND_MANIFEST } from '@umbraco-cms/backoffice/recycle-bin'; + +export const UMB_ENTITY_BULK_ACTION_TRASH_WITH_RELATION_KIND = 'trashWithRelation'; + +export const manifest: UmbExtensionManifestKind = { + type: 'kind', + alias: 'Umb.Kind.EntityBulkAction.TrashWithRelation', + matchKind: 'trashWithRelation', + matchType: 'entityBulkAction', + manifest: { + ...UMB_ENTITY_BULK_ACTION_TRASH_KIND_MANIFEST.manifest, + type: 'entityBulkAction', + kind: 'trashWithRelation', + api: () => import('./bulk-trash-with-relation.action.js'), + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts new file mode 100644 index 000000000000..40383894fd3f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts @@ -0,0 +1,22 @@ +import type { MetaEntityBulkActionTrashWithRelationKind } from './types.js'; +import { UMB_BULK_TRASH_WITH_RELATION_CONFIRM_MODAL } from './modal/constants.js'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UmbTrashEntityBulkAction } from '@umbraco-cms/backoffice/recycle-bin'; + +export class UmbBulkTrashWithRelationEntityAction extends UmbTrashEntityBulkAction { + override async _confirmTrash() { + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + + const modal = modalManager.open(this, UMB_BULK_TRASH_WITH_RELATION_CONFIRM_MODAL, { + data: { + uniques: this.selection, + itemRepositoryAlias: this.args.meta.itemRepositoryAlias, + referenceRepositoryAlias: this.args.meta.referenceRepositoryAlias, + }, + }); + + await modal.onSubmit(); + } +} + +export { UmbBulkTrashWithRelationEntityAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/constants.ts new file mode 100644 index 000000000000..98307596b7ba --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/constants.ts @@ -0,0 +1,2 @@ +export * from './modal/constants.js'; +export { UMB_ENTITY_BULK_ACTION_TRASH_WITH_RELATION_KIND } from './bulk-trash-with-relation.action.kind.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/index.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/index.ts new file mode 100644 index 000000000000..5f67a28b7978 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/index.ts @@ -0,0 +1,2 @@ +export * from './bulk-trash-with-relation.action.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/manifests.ts new file mode 100644 index 000000000000..d51d0e198106 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/manifests.ts @@ -0,0 +1,6 @@ +import { manifest as trashKindManifest } from './bulk-trash-with-relation.action.kind.js'; +import { manifests as modalManifests } from './modal/manifests.js'; + +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [trashKindManifest, ...modalManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.element.ts new file mode 100644 index 000000000000..8785fe42e13d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.element.ts @@ -0,0 +1,86 @@ +import type { + UmbBulkTrashWithRelationConfirmModalData, + UmbBulkTrashWithRelationConfirmModalValue, +} from './bulk-trash-with-relation-modal.token.js'; +import { + html, + customElement, + css, + state, + type PropertyValues, + nothing, + unsafeHTML, +} from '@umbraco-cms/backoffice/external/lit'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; + +// import of local component +import '../../local-components/confirm-bulk-action-entity-references.element.js'; + +@customElement('umb-bulk-trash-with-relation-confirm-modal') +export class UmbBulkTrashWithRelationConfirmModalElement extends UmbModalBaseElement< + UmbBulkTrashWithRelationConfirmModalData, + UmbBulkTrashWithRelationConfirmModalValue +> { + @state() + _referencesConfig?: any; + + protected override firstUpdated(_changedProperties: PropertyValues): void { + super.firstUpdated(_changedProperties); + this.#initData(); + } + + async #initData() { + if (!this.data) return; + + this._referencesConfig = { + uniques: this.data.uniques, + itemRepositoryAlias: this.data.itemRepositoryAlias, + referenceRepositoryAlias: this.data.referenceRepositoryAlias, + }; + } + + override render() { + const headline = this.localize.string('#actions_trash'); + const message = '#defaultdialogs_confirmBulkTrash'; + + return html` + +

${unsafeHTML(this.localize.string(message, this.data?.uniques.length))}

+ ${this._referencesConfig + ? html`` + : nothing} + + + + +
+ `; + } + + static override styles = [ + UmbTextStyles, + css` + uui-dialog-layout { + max-inline-size: 60ch; + } + `, + ]; +} + +export { UmbBulkTrashWithRelationConfirmModalElement as element }; + +declare global { + interface HTMLElementTagNameMap { + 'umb-bulk-trash-with-relation-confirm-modal': UmbBulkTrashWithRelationConfirmModalElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.token.ts new file mode 100644 index 000000000000..2726358d2100 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.token.ts @@ -0,0 +1,18 @@ +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export interface UmbBulkTrashWithRelationConfirmModalData { + uniques: Array; + itemRepositoryAlias: string; + referenceRepositoryAlias: string; +} + +export type UmbBulkTrashWithRelationConfirmModalValue = undefined; + +export const UMB_BULK_TRASH_WITH_RELATION_CONFIRM_MODAL = new UmbModalToken< + UmbBulkTrashWithRelationConfirmModalData, + UmbBulkTrashWithRelationConfirmModalValue +>('Umb.Modal.BulkTrashWithRelation', { + modal: { + type: 'dialog', + }, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/constants.ts new file mode 100644 index 000000000000..6d6d2883f90e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/constants.ts @@ -0,0 +1 @@ +export * from './bulk-trash-with-relation-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/manifests.ts new file mode 100644 index 000000000000..8252cb163dcf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/manifests.ts @@ -0,0 +1,8 @@ +export const manifests: Array = [ + { + type: 'modal', + alias: 'Umb.Modal.BulkTrashWithRelation', + name: 'Bulk Trash With Relation Modal', + element: () => import('./bulk-trash-with-relation-modal.element.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/types.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/types.ts new file mode 100644 index 000000000000..e173e1bf97c7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/types.ts @@ -0,0 +1,18 @@ +import type { ManifestEntityBulkAction } from '@umbraco-cms/backoffice/extension-registry'; +import type { MetaEntityBulkActionTrashKind } from '@umbraco-cms/backoffice/recycle-bin'; + +export interface ManifestEntityBulkActionTrashWithRelationKind + extends ManifestEntityBulkAction { + type: 'entityBulkAction'; + kind: 'trashWithRelation'; +} + +export interface MetaEntityBulkActionTrashWithRelationKind extends MetaEntityBulkActionTrashKind { + referenceRepositoryAlias: string; +} + +declare global { + interface UmbExtensionManifestMap { + umbManifestEntityBulkActionTrashWithRelationKind: ManifestEntityBulkActionTrashWithRelationKind; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/local-components/confirm-bulk-action-entity-references.element.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/local-components/confirm-bulk-action-entity-references.element.ts new file mode 100644 index 000000000000..4d07370a05a9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/local-components/confirm-bulk-action-entity-references.element.ts @@ -0,0 +1,134 @@ +import type { UmbEntityReferenceRepository } from '../../reference/types.js'; +import { + html, + customElement, + css, + state, + nothing, + type PropertyValues, + property, +} from '@umbraco-cms/backoffice/external/lit'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +@customElement('umb-confirm-bulk-action-modal-entity-references') +export class UmbConfirmBulkActionModalEntityReferencesElement extends UmbLitElement { + @property({ type: Object, attribute: false }) + config?: { + uniques: Array; + itemRepositoryAlias: string; + referenceRepositoryAlias: string; + }; + + @state() + _items: Array = []; + + @state() + _totalItems: number = 0; + + #itemRepository?: UmbItemRepository; + #referenceRepository?: UmbEntityReferenceRepository; + + #limitItems = 5; + + protected override firstUpdated(_changedProperties: PropertyValues): void { + super.firstUpdated(_changedProperties); + this.#initData(); + } + + async #initData() { + if (!this.config) { + this.#itemRepository?.destroy(); + this.#referenceRepository?.destroy(); + return; + } + + if (!this.config?.referenceRepositoryAlias) { + throw new Error('Missing referenceRepositoryAlias in config.'); + } + + this.#referenceRepository = await createExtensionApiByAlias( + this, + this.config?.referenceRepositoryAlias, + ); + + if (!this.config?.itemRepositoryAlias) { + throw new Error('Missing itemRepositoryAlias in config.'); + } + + this.#itemRepository = await createExtensionApiByAlias>( + this, + this.config.itemRepositoryAlias, + ); + + this.#loadAreReferenced(); + } + + async #loadAreReferenced() { + if (!this.#referenceRepository) { + throw new Error('Failed to create reference repository.'); + } + + if (!this.#itemRepository) { + throw new Error('Failed to create item repository.'); + } + + if (!this.config?.uniques) { + throw new Error('Missing uniques in config.'); + } + + const { data } = await this.#referenceRepository.requestAreReferenced(this.config.uniques, 0, this.#limitItems); + + if (data) { + this._totalItems = data.total; + const uniques = data.items.map((item) => item.unique).filter((unique) => unique) as Array; + const { data: items } = await this.#itemRepository.requestItems(uniques); + this._items = items ?? []; + } + } + + override render() { + if (this._totalItems === 0) return nothing; + + return html` +
The following items are used by other content.
+ + ${this._items.map( + (item) => + html` `, + )} + + ${this._totalItems > this.#limitItems + ? html`${this.localize.term('references_labelMoreReferences', this._totalItems - this.#limitItems)}` + : nothing} + `; + } + + static override styles = [ + UmbTextStyles, + css` + #reference-headline { + margin-bottom: var(--uui-size-3); + } + + uui-ref-list { + margin-bottom: var(--uui-size-2); + } + `, + ]; +} + +export { UmbConfirmBulkActionModalEntityReferencesElement as element }; + +declare global { + interface HTMLElementTagNameMap { + 'umb-confirm-bulk-action-modal-entity-references': UmbConfirmBulkActionModalEntityReferencesElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/manifests.ts index 574ed691cfbd..fceac92145e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/manifests.ts @@ -1,9 +1,13 @@ +import { manifests as bulkDeleteManifests } from './entity-actions/bulk-delete/manifests.js'; +import { manifests as bulkTrashManifests } from './entity-actions/bulk-trash/manifests.js'; import { manifests as collectionManifests } from './collection/manifests.js'; import { manifests as deleteManifests } from './entity-actions/delete/manifests.js'; import { manifests as trashManifests } from './entity-actions/trash/manifests.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ + ...bulkDeleteManifests, + ...bulkTrashManifests, ...collectionManifests, ...deleteManifests, ...trashManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/reference/types.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/reference/types.ts index a84034a6421b..599ee7559b9b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/reference/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/reference/types.ts @@ -21,6 +21,13 @@ export interface UmbEntityReferenceRepository extends UmbApi { skip?: number, take?: number, ): Promise>>; + + requestAreReferenced( + uniques: Array, + skip?: number, + take?: number, + ): Promise>>; + requestDescendantsWithReferences?( unique: string, skip?: number, @@ -34,6 +41,13 @@ export interface UmbEntityReferenceDataSource { skip?: number, take?: number, ): Promise>>; + + getAreReferenced( + uniques: Array, + skip?: number, + take?: number, + ): Promise>>; + getReferencedDescendants?( unique: string, skip?: number, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/entity-bulk-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/entity-bulk-actions/manifests.ts index f39f8550377d..3bae91ba418f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/entity-bulk-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/entity-bulk-actions/manifests.ts @@ -1,17 +1,20 @@ import { UMB_USER_GROUP_COLLECTION_ALIAS } from '../collection/index.js'; +import { UMB_USER_GROUP_DETAIL_REPOSITORY_ALIAS, UMB_USER_GROUP_ITEM_REPOSITORY_ALIAS } from '../constants.js'; import { UMB_USER_GROUP_ENTITY_TYPE } from '../entity.js'; +import { UMB_ENTITY_BULK_ACTION_DELETE_KIND } from '@umbraco-cms/backoffice/entity-bulk-action'; import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; export const manifests: Array = [ { type: 'entityBulkAction', + kind: UMB_ENTITY_BULK_ACTION_DELETE_KIND, alias: 'Umb.EntityBulkAction.UserGroup.Delete', name: 'Delete User Group Entity Bulk Action', weight: 400, - api: () => import('./delete/delete.action.js'), forEntityTypes: [UMB_USER_GROUP_ENTITY_TYPE], meta: { - label: 'Delete', + itemRepositoryAlias: UMB_USER_GROUP_ITEM_REPOSITORY_ALIAS, + detailRepositoryAlias: UMB_USER_GROUP_DETAIL_REPOSITORY_ALIAS, }, conditions: [ { diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index da094f92b187..512ce70e9a7b 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -74,6 +74,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "./src/packages/core/entity-create-option-action/index.ts" ], "@umbraco-cms/backoffice/entity": ["./src/packages/core/entity/index.ts"], + "@umbraco-cms/backoffice/entity-item": ["./src/packages/core/entity-item/index.ts"], "@umbraco-cms/backoffice/event": ["./src/packages/core/event/index.ts"], "@umbraco-cms/backoffice/extension-registry": ["./src/packages/core/extension-registry/index.ts"], "@umbraco-cms/backoffice/health-check": ["./src/packages/health-check/index.ts"], diff --git a/src/Umbraco.Web.UI.Client/utils/all-umb-consts/imports.ts b/src/Umbraco.Web.UI.Client/utils/all-umb-consts/imports.ts index 4d0720208b39..0cd1f3878320 100644 --- a/src/Umbraco.Web.UI.Client/utils/all-umb-consts/imports.ts +++ b/src/Umbraco.Web.UI.Client/utils/all-umb-consts/imports.ts @@ -38,71 +38,72 @@ import * as import35 from '@umbraco-cms/backoffice/entity-action'; import * as import36 from '@umbraco-cms/backoffice/entity-bulk-action'; import * as import37 from '@umbraco-cms/backoffice/entity-create-option-action'; import * as import38 from '@umbraco-cms/backoffice/entity'; -import * as import39 from '@umbraco-cms/backoffice/event'; -import * as import40 from '@umbraco-cms/backoffice/extension-registry'; -import * as import41 from '@umbraco-cms/backoffice/health-check'; -import * as import42 from '@umbraco-cms/backoffice/help'; -import * as import43 from '@umbraco-cms/backoffice/icon'; -import * as import44 from '@umbraco-cms/backoffice/id'; -import * as import45 from '@umbraco-cms/backoffice/imaging'; -import * as import46 from '@umbraco-cms/backoffice/language'; -import * as import47 from '@umbraco-cms/backoffice/lit-element'; -import * as import48 from '@umbraco-cms/backoffice/localization'; -import * as import49 from '@umbraco-cms/backoffice/log-viewer'; -import * as import50 from '@umbraco-cms/backoffice/media-type'; -import * as import51 from '@umbraco-cms/backoffice/media'; -import * as import52 from '@umbraco-cms/backoffice/member-group'; -import * as import53 from '@umbraco-cms/backoffice/member-type'; -import * as import54 from '@umbraco-cms/backoffice/member'; -import * as import55 from '@umbraco-cms/backoffice/menu'; -import * as import56 from '@umbraco-cms/backoffice/modal'; -import * as import57 from '@umbraco-cms/backoffice/multi-url-picker'; -import * as import58 from '@umbraco-cms/backoffice/notification'; -import * as import59 from '@umbraco-cms/backoffice/object-type'; -import * as import60 from '@umbraco-cms/backoffice/package'; -import * as import61 from '@umbraco-cms/backoffice/partial-view'; -import * as import62 from '@umbraco-cms/backoffice/picker-input'; -import * as import63 from '@umbraco-cms/backoffice/picker'; -import * as import64 from '@umbraco-cms/backoffice/property-action'; -import * as import65 from '@umbraco-cms/backoffice/property-editor'; -import * as import66 from '@umbraco-cms/backoffice/property-type'; -import * as import67 from '@umbraco-cms/backoffice/property'; -import * as import68 from '@umbraco-cms/backoffice/recycle-bin'; -import * as import69 from '@umbraco-cms/backoffice/relation-type'; -import * as import70 from '@umbraco-cms/backoffice/relations'; -import * as import71 from '@umbraco-cms/backoffice/repository'; -import * as import72 from '@umbraco-cms/backoffice/resources'; -import * as import73 from '@umbraco-cms/backoffice/router'; -import * as import74 from '@umbraco-cms/backoffice/rte'; -import * as import75 from '@umbraco-cms/backoffice/script'; -import * as import76 from '@umbraco-cms/backoffice/search'; -import * as import77 from '@umbraco-cms/backoffice/section'; -import * as import78 from '@umbraco-cms/backoffice/server-file-system'; -import * as import79 from '@umbraco-cms/backoffice/settings'; -import * as import80 from '@umbraco-cms/backoffice/sorter'; -import * as import81 from '@umbraco-cms/backoffice/static-file'; -import * as import82 from '@umbraco-cms/backoffice/store'; -import * as import83 from '@umbraco-cms/backoffice/style'; -import * as import84 from '@umbraco-cms/backoffice/stylesheet'; -import * as import85 from '@umbraco-cms/backoffice/sysinfo'; -import * as import86 from '@umbraco-cms/backoffice/tags'; -import * as import87 from '@umbraco-cms/backoffice/template'; -import * as import88 from '@umbraco-cms/backoffice/temporary-file'; -import * as import89 from '@umbraco-cms/backoffice/themes'; -import * as import90 from '@umbraco-cms/backoffice/tiny-mce'; -import * as import91 from '@umbraco-cms/backoffice/tiptap'; -import * as import92 from '@umbraco-cms/backoffice/translation'; -import * as import93 from '@umbraco-cms/backoffice/tree'; -import * as import94 from '@umbraco-cms/backoffice/ufm'; -import * as import95 from '@umbraco-cms/backoffice/user-change-password'; -import * as import96 from '@umbraco-cms/backoffice/user-group'; -import * as import97 from '@umbraco-cms/backoffice/user-permission'; -import * as import98 from '@umbraco-cms/backoffice/user'; -import * as import99 from '@umbraco-cms/backoffice/utils'; -import * as import100 from '@umbraco-cms/backoffice/validation'; -import * as import101 from '@umbraco-cms/backoffice/variant'; -import * as import102 from '@umbraco-cms/backoffice/webhook'; -import * as import103 from '@umbraco-cms/backoffice/workspace'; +import * as import39 from '@umbraco-cms/backoffice/entity-item'; +import * as import40 from '@umbraco-cms/backoffice/event'; +import * as import41 from '@umbraco-cms/backoffice/extension-registry'; +import * as import42 from '@umbraco-cms/backoffice/health-check'; +import * as import43 from '@umbraco-cms/backoffice/help'; +import * as import44 from '@umbraco-cms/backoffice/icon'; +import * as import45 from '@umbraco-cms/backoffice/id'; +import * as import46 from '@umbraco-cms/backoffice/imaging'; +import * as import47 from '@umbraco-cms/backoffice/language'; +import * as import48 from '@umbraco-cms/backoffice/lit-element'; +import * as import49 from '@umbraco-cms/backoffice/localization'; +import * as import50 from '@umbraco-cms/backoffice/log-viewer'; +import * as import51 from '@umbraco-cms/backoffice/media-type'; +import * as import52 from '@umbraco-cms/backoffice/media'; +import * as import53 from '@umbraco-cms/backoffice/member-group'; +import * as import54 from '@umbraco-cms/backoffice/member-type'; +import * as import55 from '@umbraco-cms/backoffice/member'; +import * as import56 from '@umbraco-cms/backoffice/menu'; +import * as import57 from '@umbraco-cms/backoffice/modal'; +import * as import58 from '@umbraco-cms/backoffice/multi-url-picker'; +import * as import59 from '@umbraco-cms/backoffice/notification'; +import * as import60 from '@umbraco-cms/backoffice/object-type'; +import * as import61 from '@umbraco-cms/backoffice/package'; +import * as import62 from '@umbraco-cms/backoffice/partial-view'; +import * as import63 from '@umbraco-cms/backoffice/picker-input'; +import * as import64 from '@umbraco-cms/backoffice/picker'; +import * as import65 from '@umbraco-cms/backoffice/property-action'; +import * as import66 from '@umbraco-cms/backoffice/property-editor'; +import * as import67 from '@umbraco-cms/backoffice/property-type'; +import * as import68 from '@umbraco-cms/backoffice/property'; +import * as import69 from '@umbraco-cms/backoffice/recycle-bin'; +import * as import70 from '@umbraco-cms/backoffice/relation-type'; +import * as import71 from '@umbraco-cms/backoffice/relations'; +import * as import72 from '@umbraco-cms/backoffice/repository'; +import * as import73 from '@umbraco-cms/backoffice/resources'; +import * as import74 from '@umbraco-cms/backoffice/router'; +import * as import75 from '@umbraco-cms/backoffice/rte'; +import * as import76 from '@umbraco-cms/backoffice/script'; +import * as import77 from '@umbraco-cms/backoffice/search'; +import * as import78 from '@umbraco-cms/backoffice/section'; +import * as import79 from '@umbraco-cms/backoffice/server-file-system'; +import * as import80 from '@umbraco-cms/backoffice/settings'; +import * as import81 from '@umbraco-cms/backoffice/sorter'; +import * as import82 from '@umbraco-cms/backoffice/static-file'; +import * as import83 from '@umbraco-cms/backoffice/store'; +import * as import84 from '@umbraco-cms/backoffice/style'; +import * as import85 from '@umbraco-cms/backoffice/stylesheet'; +import * as import86 from '@umbraco-cms/backoffice/sysinfo'; +import * as import87 from '@umbraco-cms/backoffice/tags'; +import * as import88 from '@umbraco-cms/backoffice/template'; +import * as import89 from '@umbraco-cms/backoffice/temporary-file'; +import * as import90 from '@umbraco-cms/backoffice/themes'; +import * as import91 from '@umbraco-cms/backoffice/tiny-mce'; +import * as import92 from '@umbraco-cms/backoffice/tiptap'; +import * as import93 from '@umbraco-cms/backoffice/translation'; +import * as import94 from '@umbraco-cms/backoffice/tree'; +import * as import95 from '@umbraco-cms/backoffice/ufm'; +import * as import96 from '@umbraco-cms/backoffice/user-change-password'; +import * as import97 from '@umbraco-cms/backoffice/user-group'; +import * as import98 from '@umbraco-cms/backoffice/user-permission'; +import * as import99 from '@umbraco-cms/backoffice/user'; +import * as import100 from '@umbraco-cms/backoffice/utils'; +import * as import101 from '@umbraco-cms/backoffice/validation'; +import * as import102 from '@umbraco-cms/backoffice/variant'; +import * as import103 from '@umbraco-cms/backoffice/webhook'; +import * as import104 from '@umbraco-cms/backoffice/workspace'; export const imports = [ { @@ -262,264 +263,268 @@ import * as import103 from '@umbraco-cms/backoffice/workspace'; package: import38 }, { - path: '@umbraco-cms/backoffice/event', + path: '@umbraco-cms/backoffice/entity-item', package: import39 }, { - path: '@umbraco-cms/backoffice/extension-registry', + path: '@umbraco-cms/backoffice/event', package: import40 }, { - path: '@umbraco-cms/backoffice/health-check', + path: '@umbraco-cms/backoffice/extension-registry', package: import41 }, { - path: '@umbraco-cms/backoffice/help', + path: '@umbraco-cms/backoffice/health-check', package: import42 }, { - path: '@umbraco-cms/backoffice/icon', + path: '@umbraco-cms/backoffice/help', package: import43 }, { - path: '@umbraco-cms/backoffice/id', + path: '@umbraco-cms/backoffice/icon', package: import44 }, { - path: '@umbraco-cms/backoffice/imaging', + path: '@umbraco-cms/backoffice/id', package: import45 }, { - path: '@umbraco-cms/backoffice/language', + path: '@umbraco-cms/backoffice/imaging', package: import46 }, { - path: '@umbraco-cms/backoffice/lit-element', + path: '@umbraco-cms/backoffice/language', package: import47 }, { - path: '@umbraco-cms/backoffice/localization', + path: '@umbraco-cms/backoffice/lit-element', package: import48 }, { - path: '@umbraco-cms/backoffice/log-viewer', + path: '@umbraco-cms/backoffice/localization', package: import49 }, { - path: '@umbraco-cms/backoffice/media-type', + path: '@umbraco-cms/backoffice/log-viewer', package: import50 }, { - path: '@umbraco-cms/backoffice/media', + path: '@umbraco-cms/backoffice/media-type', package: import51 }, { - path: '@umbraco-cms/backoffice/member-group', + path: '@umbraco-cms/backoffice/media', package: import52 }, { - path: '@umbraco-cms/backoffice/member-type', + path: '@umbraco-cms/backoffice/member-group', package: import53 }, { - path: '@umbraco-cms/backoffice/member', + path: '@umbraco-cms/backoffice/member-type', package: import54 }, { - path: '@umbraco-cms/backoffice/menu', + path: '@umbraco-cms/backoffice/member', package: import55 }, { - path: '@umbraco-cms/backoffice/modal', + path: '@umbraco-cms/backoffice/menu', package: import56 }, { - path: '@umbraco-cms/backoffice/multi-url-picker', + path: '@umbraco-cms/backoffice/modal', package: import57 }, { - path: '@umbraco-cms/backoffice/notification', + path: '@umbraco-cms/backoffice/multi-url-picker', package: import58 }, { - path: '@umbraco-cms/backoffice/object-type', + path: '@umbraco-cms/backoffice/notification', package: import59 }, { - path: '@umbraco-cms/backoffice/package', + path: '@umbraco-cms/backoffice/object-type', package: import60 }, { - path: '@umbraco-cms/backoffice/partial-view', + path: '@umbraco-cms/backoffice/package', package: import61 }, { - path: '@umbraco-cms/backoffice/picker-input', + path: '@umbraco-cms/backoffice/partial-view', package: import62 }, { - path: '@umbraco-cms/backoffice/picker', + path: '@umbraco-cms/backoffice/picker-input', package: import63 }, { - path: '@umbraco-cms/backoffice/property-action', + path: '@umbraco-cms/backoffice/picker', package: import64 }, { - path: '@umbraco-cms/backoffice/property-editor', + path: '@umbraco-cms/backoffice/property-action', package: import65 }, { - path: '@umbraco-cms/backoffice/property-type', + path: '@umbraco-cms/backoffice/property-editor', package: import66 }, { - path: '@umbraco-cms/backoffice/property', + path: '@umbraco-cms/backoffice/property-type', package: import67 }, { - path: '@umbraco-cms/backoffice/recycle-bin', + path: '@umbraco-cms/backoffice/property', package: import68 }, { - path: '@umbraco-cms/backoffice/relation-type', + path: '@umbraco-cms/backoffice/recycle-bin', package: import69 }, { - path: '@umbraco-cms/backoffice/relations', + path: '@umbraco-cms/backoffice/relation-type', package: import70 }, { - path: '@umbraco-cms/backoffice/repository', + path: '@umbraco-cms/backoffice/relations', package: import71 }, { - path: '@umbraco-cms/backoffice/resources', + path: '@umbraco-cms/backoffice/repository', package: import72 }, { - path: '@umbraco-cms/backoffice/router', + path: '@umbraco-cms/backoffice/resources', package: import73 }, { - path: '@umbraco-cms/backoffice/rte', + path: '@umbraco-cms/backoffice/router', package: import74 }, { - path: '@umbraco-cms/backoffice/script', + path: '@umbraco-cms/backoffice/rte', package: import75 }, { - path: '@umbraco-cms/backoffice/search', + path: '@umbraco-cms/backoffice/script', package: import76 }, { - path: '@umbraco-cms/backoffice/section', + path: '@umbraco-cms/backoffice/search', package: import77 }, { - path: '@umbraco-cms/backoffice/server-file-system', + path: '@umbraco-cms/backoffice/section', package: import78 }, { - path: '@umbraco-cms/backoffice/settings', + path: '@umbraco-cms/backoffice/server-file-system', package: import79 }, { - path: '@umbraco-cms/backoffice/sorter', + path: '@umbraco-cms/backoffice/settings', package: import80 }, { - path: '@umbraco-cms/backoffice/static-file', + path: '@umbraco-cms/backoffice/sorter', package: import81 }, { - path: '@umbraco-cms/backoffice/store', + path: '@umbraco-cms/backoffice/static-file', package: import82 }, { - path: '@umbraco-cms/backoffice/style', + path: '@umbraco-cms/backoffice/store', package: import83 }, { - path: '@umbraco-cms/backoffice/stylesheet', + path: '@umbraco-cms/backoffice/style', package: import84 }, { - path: '@umbraco-cms/backoffice/sysinfo', + path: '@umbraco-cms/backoffice/stylesheet', package: import85 }, { - path: '@umbraco-cms/backoffice/tags', + path: '@umbraco-cms/backoffice/sysinfo', package: import86 }, { - path: '@umbraco-cms/backoffice/template', + path: '@umbraco-cms/backoffice/tags', package: import87 }, { - path: '@umbraco-cms/backoffice/temporary-file', + path: '@umbraco-cms/backoffice/template', package: import88 }, { - path: '@umbraco-cms/backoffice/themes', + path: '@umbraco-cms/backoffice/temporary-file', package: import89 }, { - path: '@umbraco-cms/backoffice/tiny-mce', + path: '@umbraco-cms/backoffice/themes', package: import90 }, { - path: '@umbraco-cms/backoffice/tiptap', + path: '@umbraco-cms/backoffice/tiny-mce', package: import91 }, { - path: '@umbraco-cms/backoffice/translation', + path: '@umbraco-cms/backoffice/tiptap', package: import92 }, { - path: '@umbraco-cms/backoffice/tree', + path: '@umbraco-cms/backoffice/translation', package: import93 }, { - path: '@umbraco-cms/backoffice/ufm', + path: '@umbraco-cms/backoffice/tree', package: import94 }, { - path: '@umbraco-cms/backoffice/user-change-password', + path: '@umbraco-cms/backoffice/ufm', package: import95 }, { - path: '@umbraco-cms/backoffice/user-group', + path: '@umbraco-cms/backoffice/user-change-password', package: import96 }, { - path: '@umbraco-cms/backoffice/user-permission', + path: '@umbraco-cms/backoffice/user-group', package: import97 }, { - path: '@umbraco-cms/backoffice/user', + path: '@umbraco-cms/backoffice/user-permission', package: import98 }, { - path: '@umbraco-cms/backoffice/utils', + path: '@umbraco-cms/backoffice/user', package: import99 }, { - path: '@umbraco-cms/backoffice/validation', + path: '@umbraco-cms/backoffice/utils', package: import100 }, { - path: '@umbraco-cms/backoffice/variant', + path: '@umbraco-cms/backoffice/validation', package: import101 }, { - path: '@umbraco-cms/backoffice/webhook', + path: '@umbraco-cms/backoffice/variant', package: import102 }, { - path: '@umbraco-cms/backoffice/workspace', + path: '@umbraco-cms/backoffice/webhook', package: import103 + }, +{ + path: '@umbraco-cms/backoffice/workspace', + package: import104 } ]; \ No newline at end of file