From a482ab239a82a1a10a0c61adfacc6c8629868f59 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 24 Feb 2025 14:03:26 +0100 Subject: [PATCH] Feature: Bulk Delete/Trash referenced by (#18393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add trash confirm modal * make referenceRepo optional + adjust styling * add referenceRepository to media trash action * wip entity-item-ref extension point * clean up * add ref list element * fix styling * Update document-item-ref.element.ts * move item repo * implement for input member * enable action slot * add null check * fix sorting again * fix sorting again * use member element * add draft styling back * move item repository * implement for user input * pass readonly and standalone props * make editPath a state * Update member-item-ref.element.ts * Fix user item ref * remove open button * remove unused * remove unused * check for section permission * add null check * change to use entity-item-ref element * register media item ref * add fallback element * show 3 references * wip data mapper concept * add unique to modal route registration * add unique to modal router * remove unused id * Update member-item-ref.element.ts * append unique * compare with old value * only recreate the controller if the entity type changes * fix console warning * implement for document item ref * Added $type to ReferenceResponseModels * move logic to item data resolver * render draft as a tag * Update document-item-ref.element.ts * generate server models * add more helpers to data resolver * export resolver * add observables * use observables in document item ref * add data resolver to tree item * add observable state * use const * align models * get icon from document type object * observe name and state * update observed value when a new item is set * update method name * update method names * pass model type * pass context type * use api prop instead of context * use api prop instead of context * fix types * use addUniquePaths for modal registration * add fallback * use ref list * use reference items for media * make mapper name more generic * make default ref item always readonly * export types * temp fake variants array * add variants array to model * Update media-references-workspace-info-app.element.ts * add variants to model * hardcode fake array * register media ref item * update mock data * dot not allow conditions for data mappers * add data mapper * prefix info routes * prefix all ref routes * return undefined if there is not edit path * add types for reference data source + repository * split trash with relation into its own action * render descendants with references * fix length check * set standalone attribute * implement for media * move trash * wip delete with relation * move to element * fix name collision * require data source identifier * use management api mapper * add management api mapper * fix type errors * Update index.ts * align naming * show references when deleting a document * Update delete-with-relation-modal.element.ts * use deleteWithRelation kind for media * clean up * localize trash * fix type * Update trash-with-relation.action.ts * override confirm methods in trash and delete actions * Update index.ts * export constants * Limit referenced-by document and media endpoints to references only. * Update document-reference-table.element.ts * add methods to get bulk references for documents * wip bulk trash * add todo comment * implement path pattern for media item * clean up * more clean up * sort imports * member edit path pattern * clean up * remove unused variant id * export extension types * wip bulk trash with relation * debounce incoming events * look up items + notify * add todo * temp solution to make it non breaking * add bulk delete * better description * implement methods for are referenced * change to use bulk trash with relation * implement delete with relation kind * deprecation warnings * move files * move files * export const * use correct kind * align naming * upper case Trash * correct uui-text impl * add comment about the v2 name * fix circular depdendencies * rename const * split to module * import global components * more explicit naming --------- Co-authored-by: Sven Geusens Co-authored-by: Andy Butland Co-authored-by: Niels Lyngsø --- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/assets/lang/en.ts | 4 + .../entity-action/entity-deleted.event.ts | 10 + .../src/packages/core/entity-action/index.ts | 1 + .../bulk-delete/bulk-delete.action.kind.ts | 24 ++ .../common/bulk-delete/bulk-delete.action.ts | 121 ++++++++ .../common/bulk-delete/constants.ts | 4 + .../common/bulk-delete/index.ts | 2 + .../common/bulk-delete/manifests.ts | 4 + .../common/bulk-delete/types.ts | 20 ++ .../common/trash/constants.ts | 1 + .../trash/trash-repository.interface.ts | 7 +- .../common/trash/trash.action.kind.ts | 5 +- .../common/trash/trash.action.ts | 20 +- .../common/{index.ts => types.ts} | 1 + .../packages/core/entity-bulk-action/index.ts | 10 +- .../core/entity-bulk-action/manifests.ts | 3 + .../packages/core/entity-bulk-action/types.ts | 4 + .../default-item-ref.element.ts | 0 .../entity-item-ref.element.ts | 4 +- .../entity-item-ref.extension.ts | 0 .../entity-item-ref/global-components.ts | 1 + .../entity-item-ref/index.ts | 2 - .../entity-item-ref/types.ts | 0 .../core/entity-item/global-components.ts | 1 + .../src/packages/core/entity-item/index.ts | 1 + .../src/packages/core/entity-item/types.ts | 5 + .../src/packages/core/entity/index.ts | 1 - .../src/packages/core/entity/types.ts | 4 - .../src/packages/core/entry-point.ts | 1 + .../entity-bulk-action.extension.ts | 4 +- .../packages/core/recycle-bin/constants.ts | 1 + .../bulk-trash/bulk-trash.action.kind.ts | 29 ++ .../bulk-trash/bulk-trash.action.ts | 122 ++++++++ .../bulk-trash/constants.ts | 4 + .../entity-bulk-action/bulk-trash/index.ts | 2 + .../bulk-trash/manifests.ts | 4 + .../entity-bulk-action/bulk-trash/types.ts | 20 ++ .../entity-bulk-action/constants.ts | 1 + .../recycle-bin/entity-bulk-action/index.ts | 1 + .../recycle-bin/entity-bulk-action/types.ts | 1 + .../src/packages/core/recycle-bin/index.ts | 3 +- .../packages/core/recycle-bin/manifests.ts | 2 + .../recycle-bin-repository-base.ts | 19 +- .../recycle-bin-tree-item.context.ts | 5 +- .../src/packages/core/recycle-bin/types.ts | 1 + .../repository/data-mapper/data-mapper.ts | 10 +- .../data-mapper/management-api/constants.ts | 2 +- .../management-api-data-mapper.ts | 10 +- .../mapping/data-mapping-resolver.ts | 12 +- .../mapping/data-mapping.extension.ts | 12 +- .../repository/data-mapper/mapping/types.ts | 2 +- .../detail/detail-repository-base.ts | 15 +- .../src/packages/core/vite.config.ts | 1 + .../entity-bulk-actions/constants.ts | 1 - .../entity-bulk-actions/manifests.ts | 3 +- .../entity-bulk-actions/trash/manifests.ts | 31 -- .../documents/recycle-bin/constants.ts | 1 + .../entity-action/bulk-trash}/constants.ts | 0 .../entity-action/bulk-trash}/index.ts | 0 .../entity-action/bulk-trash/manifests.ts | 36 +++ .../bulk-trash}/repository/constants.ts | 0 .../bulk-trash}/repository/index.ts | 0 .../bulk-trash}/repository/manifests.ts | 0 .../repository/trash.repository.ts | 12 +- .../recycle-bin/entity-action/constants.ts | 1 + .../recycle-bin/entity-action/manifests.ts | 2 + ...ference-response.management-api.mapping.ts | 4 +- .../document-reference.repository.ts | 5 + .../document-reference.server.data.ts | 34 ++- .../reference/repository/manifests.ts | 8 +- .../media/entity-bulk-actions/constants.ts | 1 - .../media/entity-bulk-actions/manifests.ts | 3 +- .../entity-bulk-actions/trash/manifests.ts | 25 -- .../media/media/recycle-bin/constants.ts | 1 + .../entity-action/bulk-trash}/constants.ts | 0 .../entity-action/bulk-trash}/index.ts | 0 .../entity-action/bulk-trash/manifests.ts | 30 ++ .../bulk-trash}/repository/constants.ts | 0 .../bulk-trash}/repository/index.ts | 0 .../bulk-trash}/repository/manifests.ts | 0 .../repository/trash.repository.ts | 12 +- .../recycle-bin/entity-action/constants.ts | 1 + .../recycle-bin/entity-action/manifests.ts | 2 + .../media/reference/repository/manifests.ts | 8 +- ...ference-response.management-api.mapping.ts | 4 +- .../repository/media-reference.repository.ts | 11 + .../repository/media-reference.server.data.ts | 34 ++- .../packages/relations/relations/constants.ts | 2 + .../bulk-delete-with-relation.action.kind.ts | 17 ++ .../bulk-delete-with-relation.action.ts | 22 ++ .../entity-actions/bulk-delete/constants.ts | 2 + .../entity-actions/bulk-delete/index.ts | 2 + .../entity-actions/bulk-delete/manifests.ts | 9 + ...bulk-delete-with-relation-modal.element.ts | 86 ++++++ .../bulk-delete-with-relation-modal.token.ts | 18 ++ .../bulk-delete/modal/constants.ts | 1 + .../bulk-delete/modal/manifests.ts | 8 + .../entity-actions/bulk-delete/types.ts | 18 ++ .../bulk-trash-with-relation.action.kind.ts | 17 ++ .../bulk-trash-with-relation.action.ts | 22 ++ .../entity-actions/bulk-trash/constants.ts | 2 + .../entity-actions/bulk-trash/index.ts | 2 + .../entity-actions/bulk-trash/manifests.ts | 6 + .../bulk-trash-with-relation-modal.element.ts | 86 ++++++ .../bulk-trash-with-relation-modal.token.ts | 18 ++ .../bulk-trash/modal/constants.ts | 1 + .../bulk-trash/modal/manifests.ts | 8 + .../entity-actions/bulk-trash/types.ts | 18 ++ ...m-bulk-action-entity-references.element.ts | 134 +++++++++ .../packages/relations/relations/manifests.ts | 4 + .../relations/relations/reference/types.ts | 14 + .../entity-bulk-actions/manifests.ts | 7 +- src/Umbraco.Web.UI.Client/tsconfig.json | 1 + .../utils/all-umb-consts/imports.ts | 265 +++++++++--------- 115 files changed, 1341 insertions(+), 264 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-deleted.event.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.kind.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/bulk-delete.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/bulk-delete/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/trash/constants.ts rename src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/{index.ts => types.ts} (73%) rename src/Umbraco.Web.UI.Client/src/packages/core/{entity => entity-item}/entity-item-ref/default-item-ref.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/{entity => entity-item}/entity-item-ref/entity-item-ref.element.ts (98%) rename src/Umbraco.Web.UI.Client/src/packages/core/{entity => entity-item}/entity-item-ref/entity-item-ref.extension.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-item/entity-item-ref/global-components.ts rename src/Umbraco.Web.UI.Client/src/packages/core/{entity => entity-item}/entity-item-ref/index.ts (53%) rename src/Umbraco.Web.UI.Client/src/packages/core/{entity => entity-item}/entity-item-ref/types.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-item/global-components.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-item/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/entity-item/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.kind.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/bulk-trash.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/bulk-trash/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-bulk-action/types.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/trash/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/documents/documents/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/constants.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/documents/documents/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/index.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/bulk-trash/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/documents/documents/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/constants.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/documents/documents/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/documents/documents/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/documents/documents/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/trash.repository.ts (78%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/entity-action/constants.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media/entity-bulk-actions/trash/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/media/media/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/constants.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/media/media/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/index.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/bulk-trash/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/media/media/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/constants.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/media/media/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/media/media/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/media/media/{entity-bulk-actions/trash => recycle-bin/entity-action/bulk-trash}/repository/trash.repository.ts (75%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/entity-action/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.kind.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/bulk-delete-with-relation-modal.token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/modal/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.kind.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/bulk-trash-with-relation-modal.token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/constants.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/modal/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/local-components/confirm-bulk-action-entity-references.element.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index f012dd2ac1..369297a9bb 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 9add9072f8..031f436480 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts @@ -513,6 +513,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 0000000000..9e2bfa0548 --- /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 37d12fa9f0..886f167a42 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 0000000000..c0a727b3e0 --- /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 0000000000..8da90bffc5 --- /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 0000000000..b88413e675 --- /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 0000000000..07e2f108e4 --- /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 0000000000..558f12b16c --- /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 0000000000..17ff0c6f48 --- /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 0000000000..f919f4aea6 --- /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 195b2f7724..8570bfc8ad 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 2c36d6dc35..dd6d351192 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 70cbc7ffd5..84d27501cc 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 e3330f3081..7dd2feb9d1 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 bcc30c388e..91126a4727 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 57fab22a3d..2d3fde2e92 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 49faf0bca5..6e72415531 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 de07c6a3d5..f239f3ebe4 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 0000000000..b04a6b66a0 --- /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 338849e069..0dfe5acf10 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 0000000000..a518da7233 --- /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 0000000000..5ada9eda75 --- /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 0000000000..31240f7f67 --- /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 7fbfc9d7f6..737d7bf482 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 09ec5529e2..f2fcb7dde9 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 df77e8a18b..cf71700dce 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 969a1e28a6..4d5b2adcb5 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 27f0cb6150..19138819b5 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 0000000000..1e659cc7e8 --- /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 0000000000..384e111ed5 --- /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 0000000000..65c101c9fb --- /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 0000000000..a281a02351 --- /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 0000000000..066b290beb --- /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 0000000000..8a8492c24c --- /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 0000000000..e96d7ef38d --- /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 0000000000..b88b7cd9ad --- /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 0000000000..83d23b4b0c --- /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 e27c2c13ac..83cdb218b8 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 f5fc0ea7da..b9eabac4a7 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 b7c101717f..e246dac49d 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 50c6a06dfb..ff1156ed0b 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 587bcc35d1..52db4d7f18 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/data-mapper.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/data-mapper.ts index 24de99cdd1..88f0fbd274 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/data-mapper.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/data-mapper.ts @@ -1,17 +1,17 @@ -import { UmbDataMappingResolver } from './mapping/data-mapping-resolver.js'; +import { UmbDataSourceDataMappingResolver } from './mapping/data-mapping-resolver.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -export interface UmbDataMapperMapArgs { +export interface UmbDataSourceDataMapperMapArgs { forDataModel: string; forDataSource: string; data: fromModelType; fallback?: (data: fromModelType) => Promise; } -export class UmbDataMapper extends UmbControllerBase { - #dataMappingResolver = new UmbDataMappingResolver(this); +export class UmbDataSourceDataMapper extends UmbControllerBase { + #dataMappingResolver = new UmbDataSourceDataMappingResolver(this); - async map(args: UmbDataMapperMapArgs) { + async map(args: UmbDataSourceDataMapperMapArgs) { if (!args.forDataSource) { throw new Error('data source identifier is required'); } 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 25d3b5812f..52e1d472b8 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 d220ee390d..2bd4c3481a 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,19 +1,19 @@ -import { UmbDataMapper, type UmbDataMapperMapArgs } from '../data-mapper.js'; -import { UMB_MANAGEMENT_API_DATA_SOURCE_IDENTIFIER } from './constants.js'; +import { UmbDataSourceDataMapper, type UmbDataSourceDataMapperMapArgs } from '../data-mapper.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'; export class UmbManagementApiDataMapper extends UmbControllerBase { - #dataMapper = new UmbDataMapper(this); + #dataMapper = new UmbDataSourceDataMapper(this); constructor(host: UmbControllerHost) { super(host); } - map(args: Omit) { + 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/data-mapper/mapping/data-mapping-resolver.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts index 0f7b1c46e1..c0816ee9b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts @@ -1,12 +1,12 @@ -import type { UmbDataMapping } from './types.js'; +import type { UmbDataSourceDataMapping } from './types.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { createExtensionApi, type ManifestBase } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -export class UmbDataMappingResolver extends UmbControllerBase { - #apiCache = new Map(); +export class UmbDataSourceDataMappingResolver extends UmbControllerBase { + #apiCache = new Map(); - async resolve(forDataSource: string, forDataModel: string): Promise { + async resolve(forDataSource: string, forDataModel: string): Promise { if (!forDataSource) { throw new Error('data source identifier is required'); } @@ -26,7 +26,7 @@ export class UmbDataMappingResolver extends UmbControllerBase { return this.#apiCache.get(manifest.alias)!; } - const dataMapping = await createExtensionApi(this, manifest); + const dataMapping = await createExtensionApi(this, manifest); if (!dataMapping) { return undefined; @@ -55,7 +55,7 @@ export class UmbDataMappingResolver extends UmbControllerBase { } #getSupportedManifests(forDataSource: string, forDataModel: string) { - const supportedManifests = umbExtensionsRegistry.getByTypeAndFilter('dataMapping', (manifest) => { + const supportedManifests = umbExtensionsRegistry.getByTypeAndFilter('dataSourceDataMapping', (manifest) => { return manifest.forDataSource === forDataSource && manifest.forDataModel === forDataModel; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts index 64e85b2722..a64ea7fd02 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping.extension.ts @@ -1,19 +1,19 @@ -import type { UmbDataMapping } from './types.js'; +import type { UmbDataSourceDataMapping } from './types.js'; import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestDataMapping - extends ManifestApi { - type: 'dataMapping'; +export interface ManifestDataSourceDataMapping + extends ManifestApi { + type: 'dataSourceDataMapping'; forDataSource: string; forDataModel: string; meta: MetaType; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -export interface MetaDataMapping {} +export interface MetaDataSourceDataMapping {} declare global { interface UmbExtensionManifestMap { - umbManifestDataMapping: ManifestDataMapping; + umbManifestDataSourceDataMapping: ManifestDataSourceDataMapping; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts index 4dc1a4883e..3c398c0658 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/types.ts @@ -1,6 +1,6 @@ import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; export type * from './data-mapping.extension.js'; -export interface UmbDataMapping extends UmbApi { +export interface UmbDataSourceDataMapping extends UmbApi { map: (data: fromModelType) => Promise; } 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 12a77e5437..831cdab778 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 893def16fc..691e31b0b3 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 8d305a6458..8d2371e6f3 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 65ee8658fc..b67ea4c1a0 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 d1ff3f6058..0000000000 --- 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 0002782c2b..9af32d7bb7 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 0000000000..2ac56f94f5 --- /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 edd03d8400..43c2ed8c1d 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 0000000000..e96d7ef38d --- /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 db98e6e0bb..6c4cf7b4b2 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-response.management-api.mapping.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference-response.management-api.mapping.ts index 97baecf652..5dc5b08754 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference-response.management-api.mapping.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference-response.management-api.mapping.ts @@ -5,11 +5,11 @@ import { type DocumentReferenceResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbDataMapping } from '@umbraco-cms/backoffice/repository'; +import type { UmbDataSourceDataMapping } from '@umbraco-cms/backoffice/repository'; export class UmbDocumentReferenceResponseManagementApiDataMapping extends UmbControllerBase - implements UmbDataMapping + implements UmbDataSourceDataMapping { async map(data: DocumentReferenceResponseModel): Promise { return { 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 fa3b1f6dbe..0f1ae193f2 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 dd19c40ddf..3b2f7d3de7 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 8e2dc6740b..d7d09175b2 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 = [ { @@ -9,11 +9,11 @@ export const manifests: Array = [ api: () => import('./document-reference.repository.js'), }, { - type: 'dataMapping', - alias: 'Umb.DataMapping.ManagementApi.DocumentReferenceResponse', + type: 'dataSourceDataMapping', + alias: 'Umb.DataSourceDataMapping.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 35432ce6cd..5e9a915209 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 4930ca0530..397a313678 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 48bc80af00..0000000000 --- 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 e05617eb5a..a77985d164 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 0000000000..f1fccda364 --- /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 d84cd3e956..f75ccf7989 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 0000000000..e96d7ef38d --- /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 cf87461c89..65b3693975 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 c885ccb85c..9b64e888df 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 = [ { @@ -9,11 +9,11 @@ export const manifests: Array = [ api: () => import('./media-reference.repository.js'), }, { - type: 'dataMapping', - alias: 'Umb.DataMapping.ManagementApi.MediaReferenceResponse', + type: 'dataSourceDataMapping', + alias: 'Umb.DataSourceDataMapping.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-response.management-api.mapping.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference-response.management-api.mapping.ts index f00cf503c2..e9c29762e5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference-response.management-api.mapping.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference-response.management-api.mapping.ts @@ -2,11 +2,11 @@ import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js'; import type { UmbMediaReferenceModel } from './types.js'; import type { MediaReferenceResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbDataMapping } from '@umbraco-cms/backoffice/repository'; +import type { UmbDataSourceDataMapping } from '@umbraco-cms/backoffice/repository'; export class UmbMediaReferenceResponseManagementApiDataMapping extends UmbControllerBase - implements UmbDataMapping + implements UmbDataSourceDataMapping { async map(data: MediaReferenceResponseModel): Promise { return { 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 ab5ac83a5f..53ed8b45e1 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 c3ea920753..6f169f00c4 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 1ad52da7ec..6d7855c151 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 0000000000..177f452f17 --- /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 0000000000..71b26bf653 --- /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 0000000000..255321445a --- /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 0000000000..64a7503c0f --- /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 0000000000..9d922686b0 --- /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 0000000000..c7d2a22c79 --- /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 0000000000..b66ee86cb1 --- /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 0000000000..8fcf44dcda --- /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 0000000000..13a881cd09 --- /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 0000000000..b838827362 --- /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 0000000000..88b74d93c8 --- /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 0000000000..40383894fd --- /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 0000000000..98307596b7 --- /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 0000000000..5f67a28b79 --- /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 0000000000..d51d0e1981 --- /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 0000000000..8785fe42e1 --- /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 0000000000..2726358d21 --- /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 0000000000..6d6d2883f9 --- /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 0000000000..8252cb163d --- /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 0000000000..e173e1bf97 --- /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 0000000000..4d07370a05 --- /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 574ed691cf..fceac92145 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 a84034a642..599ee7559b 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 f39f855037..3bae91ba41 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 da094f92b1..512ce70e9a 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 4d0720208b..0cd1f38783 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