diff --git a/src/packages/core/entity-bulk-action/common/index.ts b/src/packages/core/entity-bulk-action/common/index.ts index 4278200a36..c70f79f940 100644 --- a/src/packages/core/entity-bulk-action/common/index.ts +++ b/src/packages/core/entity-bulk-action/common/index.ts @@ -1,2 +1,3 @@ export * from './duplicate-to/index.js'; export * from './move-to/index.js'; +export * from './trash/index.js'; diff --git a/src/packages/core/entity-bulk-action/common/trash/index.ts b/src/packages/core/entity-bulk-action/common/trash/index.ts new file mode 100644 index 0000000000..8bed2d2b64 --- /dev/null +++ b/src/packages/core/entity-bulk-action/common/trash/index.ts @@ -0,0 +1,2 @@ +export type { UmbBulkTrashRepository } from './trash-repository.interface.js'; +export type { UmbBulkTrashRequestArgs } from './types.js'; diff --git a/src/packages/core/entity-bulk-action/common/trash/manifests.ts b/src/packages/core/entity-bulk-action/common/trash/manifests.ts new file mode 100644 index 0000000000..f48d4f6879 --- /dev/null +++ b/src/packages/core/entity-bulk-action/common/trash/manifests.ts @@ -0,0 +1,4 @@ +import { manifest as trashKindManifest } from './trash.action.kind.js'; +import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [trashKindManifest]; diff --git a/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts b/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts new file mode 100644 index 0000000000..195b2f7724 --- /dev/null +++ b/src/packages/core/entity-bulk-action/common/trash/trash-repository.interface.ts @@ -0,0 +1,7 @@ +import type { UmbRepositoryErrorResponse } from '../../../repository/types.js'; +import type { UmbBulkTrashRequestArgs } from './types.js'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface UmbBulkTrashRepository extends UmbApi { + requestBulkTrash(args: UmbBulkTrashRequestArgs): Promise; +} diff --git a/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts b/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts new file mode 100644 index 0000000000..5cec1325a1 --- /dev/null +++ b/src/packages/core/entity-bulk-action/common/trash/trash.action.kind.ts @@ -0,0 +1,22 @@ +import { UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST } from '../../default/default.action.kind.js'; +import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifest: UmbBackofficeManifestKind = { + type: 'kind', + alias: 'Umb.Kind.EntityBulkAction.Trash', + matchKind: 'trash', + matchType: 'entityBulkAction', + manifest: { + ...UMB_ENTITY_BULK_ACTION_DEFAULT_KIND_MANIFEST.manifest, + type: 'entityBulkAction', + kind: 'trash', + api: () => import('./trash.action.js'), + weight: 700, + forEntityTypes: [], + meta: { + icon: 'icon-trash', + label: '#actions_trash', + bulkTrashRepositoryAlias: '', + }, + }, +}; diff --git a/src/packages/core/entity-bulk-action/common/trash/trash.action.ts b/src/packages/core/entity-bulk-action/common/trash/trash.action.ts new file mode 100644 index 0000000000..70cbc7ffd5 --- /dev/null +++ b/src/packages/core/entity-bulk-action/common/trash/trash.action.ts @@ -0,0 +1,53 @@ +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 { + UmbRequestReloadChildrenOfEntityEvent, + UmbRequestReloadStructureForEntityEvent, +} from '@umbraco-cms/backoffice/entity-action'; +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'; + +export class UmbMediaTrashEntityBulkAction extends UmbEntityBulkActionBase { + async execute() { + if (this.selection?.length === 0) return; + + await umbConfirmModal(this._host, { + headline: `Trash`, + content: `Are you sure you want to move ${this.selection.length} ${this.selection.length === 1 ? 'item' : 'items'} to the recycle bin?`, + color: 'danger', + confirmLabel: 'Trash', + }); + + const bulkTrashRepository = await createExtensionApiByAlias( + this, + this.args.meta.bulkTrashRepositoryAlias, + ); + if (!bulkTrashRepository) throw new Error('Bulk Trash Repository is not available'); + + await bulkTrashRepository.requestBulkTrash({ uniques: this.selection }); + + const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + if (!entityContext) throw new Error('Entity Context is not available'); + + const entityType = entityContext.getEntityType(); + const unique = entityContext.getUnique(); + + if (entityType && unique !== undefined) { + const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event Context is not available'); + + const args = { entityType, unique }; + + const reloadChildren = new UmbRequestReloadChildrenOfEntityEvent(args); + eventContext.dispatchEvent(reloadChildren); + + const reloadStructure = new UmbRequestReloadStructureForEntityEvent(args); + eventContext.dispatchEvent(reloadStructure); + } + } +} + +export { UmbMediaTrashEntityBulkAction as api }; diff --git a/src/packages/core/entity-bulk-action/common/trash/types.ts b/src/packages/core/entity-bulk-action/common/trash/types.ts new file mode 100644 index 0000000000..d128c53b7a --- /dev/null +++ b/src/packages/core/entity-bulk-action/common/trash/types.ts @@ -0,0 +1,3 @@ +export interface UmbBulkTrashRequestArgs { + uniques: Array; +} diff --git a/src/packages/core/entity-bulk-action/manifests.ts b/src/packages/core/entity-bulk-action/manifests.ts index 9cf3d1e633..306185ef77 100644 --- a/src/packages/core/entity-bulk-action/manifests.ts +++ b/src/packages/core/entity-bulk-action/manifests.ts @@ -1,10 +1,12 @@ import { manifests as defaultEntityBulkActionManifests } from './default/manifests.js'; 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 type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ ...defaultEntityBulkActionManifests, ...duplicateEntityBulkActionManifests, ...moveToEntityBulkActionManifests, + ...trashEntityBulkActionManifests, ]; diff --git a/src/packages/core/extension-registry/models/entity-bulk-action.model.ts b/src/packages/core/extension-registry/models/entity-bulk-action.model.ts index 22aa35ec78..967a868949 100644 --- a/src/packages/core/extension-registry/models/entity-bulk-action.model.ts +++ b/src/packages/core/extension-registry/models/entity-bulk-action.model.ts @@ -70,3 +70,13 @@ export interface MetaEntityBulkActionMoveToKind extends MetaEntityBulkActionDefa foldersOnly?: boolean; treeAlias: string; } + +// TRASH +export interface ManifestEntityBulkActionTrashKind extends ManifestEntityBulkAction { + type: 'entityBulkAction'; + kind: 'trash'; +} + +export interface MetaEntityBulkActionTrashKind extends MetaEntityBulkActionDefaultKind { + bulkTrashRepositoryAlias: string; +} diff --git a/src/packages/media/media/entity-bulk-actions/delete/delete.action.ts b/src/packages/media/media/entity-bulk-actions/delete/delete.action.ts deleted file mode 100644 index 98d607aad4..0000000000 --- a/src/packages/media/media/entity-bulk-actions/delete/delete.action.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action'; - -export class UmbMediaDeleteEntityBulkAction extends UmbEntityBulkActionBase { - async execute() { - console.log(`execute delete for: ${this.selection}`); - - // TODO: show error - - // TODO: should we subscribe in cases like this? - /* - const { data } = await this.repository.requestItemsLegacy(this.selection); - - if (data) { - // TODO: use correct markup - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_CONFIRM_MODAL, { - headline: `Deleting ${this.selection.length} items`, - content: html` - This will delete the following files: -
    - ${data.map((item) => html`
  • ${item.name}
  • `)} -
- `, - color: 'danger', - confirmLabel: 'Delete', - }); - - await modalContext.onSubmit(); - await this.repository?.trash(this.selection); - } - */ - } -} - -export { UmbMediaDeleteEntityBulkAction as api }; diff --git a/src/packages/media/media/entity-bulk-actions/duplicate/duplicate.action.ts b/src/packages/media/media/entity-bulk-actions/duplicate/duplicate.action.ts deleted file mode 100644 index 28bfa324f3..0000000000 --- a/src/packages/media/media/entity-bulk-actions/duplicate/duplicate.action.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action'; - -export class UmbDuplicateMediaEntityBulkAction extends UmbEntityBulkActionBase { - async execute() { - console.log('execute bulk duplicate'); - } -} - -export { UmbDuplicateMediaEntityBulkAction as api }; diff --git a/src/packages/media/media/entity-bulk-actions/manifests.ts b/src/packages/media/media/entity-bulk-actions/manifests.ts index 28c8b20dff..805c692d8e 100644 --- a/src/packages/media/media/entity-bulk-actions/manifests.ts +++ b/src/packages/media/media/entity-bulk-actions/manifests.ts @@ -1,56 +1,5 @@ -import { UMB_MEDIA_COLLECTION_ALIAS } from '../collection/index.js'; -import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js'; import { manifests as moveToManifests } from './move-to/manifests.js'; -import { - UMB_COLLECTION_ALIAS_CONDITION, - UMB_COLLECTION_BULK_ACTION_PERMISSION_CONDITION, -} from '@umbraco-cms/backoffice/collection'; -import type { ManifestEntityBulkAction, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbCollectionBulkActionPermissions } from '@umbraco-cms/backoffice/collection'; +import { manifests as trashManifests } from './trash/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -const entityBulkActions: Array = [ - { - type: 'entityBulkAction', - alias: 'Umb.EntityBulkAction.Media.Duplicate', - name: 'Duplicate Media Entity Bulk Action', - weight: 30, - api: () => import('./duplicate/duplicate.action.js'), - forEntityTypes: [UMB_MEDIA_ENTITY_TYPE], - meta: { - label: 'Duplicate', - }, - conditions: [ - { - alias: UMB_COLLECTION_ALIAS_CONDITION, - match: UMB_MEDIA_COLLECTION_ALIAS, - }, - { - alias: UMB_COLLECTION_BULK_ACTION_PERMISSION_CONDITION, - match: (permissions: UmbCollectionBulkActionPermissions) => permissions.allowBulkCopy, - }, - ], - }, - { - type: 'entityBulkAction', - alias: 'Umb.EntityBulkAction.Media.Delete', - name: 'Delete Media Entity Bulk Action', - weight: 10, - api: () => import('./delete/delete.action.js'), - forEntityTypes: [UMB_MEDIA_ENTITY_TYPE], - meta: { - label: 'Delete', - }, - conditions: [ - { - alias: UMB_COLLECTION_ALIAS_CONDITION, - match: UMB_MEDIA_COLLECTION_ALIAS, - }, - { - alias: UMB_COLLECTION_BULK_ACTION_PERMISSION_CONDITION, - match: (permissions: UmbCollectionBulkActionPermissions) => permissions.allowBulkDelete, - }, - ], - }, -]; - -export const manifests: Array = [...entityBulkActions, ...moveToManifests]; +export const manifests: Array = [...moveToManifests, ...trashManifests]; diff --git a/src/packages/media/media/entity-bulk-actions/trash/index.ts b/src/packages/media/media/entity-bulk-actions/trash/index.ts new file mode 100644 index 0000000000..cdb28dce3b --- /dev/null +++ b/src/packages/media/media/entity-bulk-actions/trash/index.ts @@ -0,0 +1 @@ +export { UmbBulkTrashMediaRepository, UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './repository/index.js'; diff --git a/src/packages/media/media/entity-bulk-actions/trash/manifests.ts b/src/packages/media/media/entity-bulk-actions/trash/manifests.ts new file mode 100644 index 0000000000..6df781b6b4 --- /dev/null +++ b/src/packages/media/media/entity-bulk-actions/trash/manifests.ts @@ -0,0 +1,34 @@ +import { UMB_MEDIA_COLLECTION_ALIAS } from '../../collection/index.js'; +import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js'; +import { UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './repository/constants.js'; +import { manifests as repositoryManifests } from './repository/manifests.js'; +import { + UMB_COLLECTION_ALIAS_CONDITION, + UMB_COLLECTION_BULK_ACTION_PERMISSION_CONDITION, +} from '@umbraco-cms/backoffice/collection'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbCollectionBulkActionPermissions } from '@umbraco-cms/backoffice/collection'; + +const bulkTrashAction: ManifestTypes = { + 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, + }, + { + alias: UMB_COLLECTION_BULK_ACTION_PERMISSION_CONDITION, + match: (permissions: UmbCollectionBulkActionPermissions) => permissions.allowBulkDelete, + }, + ], +}; + +export const manifests: Array = [bulkTrashAction, ...repositoryManifests]; diff --git a/src/packages/media/media/entity-bulk-actions/trash/repository/constants.ts b/src/packages/media/media/entity-bulk-actions/trash/repository/constants.ts new file mode 100644 index 0000000000..6fe1f4fae1 --- /dev/null +++ b/src/packages/media/media/entity-bulk-actions/trash/repository/constants.ts @@ -0,0 +1 @@ +export const UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS = 'Umb.Repository.Media.BulkTrash'; diff --git a/src/packages/media/media/entity-bulk-actions/trash/repository/index.ts b/src/packages/media/media/entity-bulk-actions/trash/repository/index.ts new file mode 100644 index 0000000000..fe460293cf --- /dev/null +++ b/src/packages/media/media/entity-bulk-actions/trash/repository/index.ts @@ -0,0 +1,2 @@ +export { UmbBulkTrashMediaRepository } from './trash.repository.js'; +export { UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './constants.js'; diff --git a/src/packages/media/media/entity-bulk-actions/trash/repository/manifests.ts b/src/packages/media/media/entity-bulk-actions/trash/repository/manifests.ts new file mode 100644 index 0000000000..7f18a39a56 --- /dev/null +++ b/src/packages/media/media/entity-bulk-actions/trash/repository/manifests.ts @@ -0,0 +1,11 @@ +import { UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './constants.js'; +import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +const bulkTrashRepository: ManifestRepository = { + type: 'repository', + alias: UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS, + name: 'Bulk Trash Media Repository', + api: () => import('./trash.repository.js'), +}; + +export const manifests: Array = [bulkTrashRepository]; diff --git a/src/packages/media/media/entity-bulk-actions/trash/repository/trash.repository.ts b/src/packages/media/media/entity-bulk-actions/trash/repository/trash.repository.ts new file mode 100644 index 0000000000..d84cd3e956 --- /dev/null +++ b/src/packages/media/media/entity-bulk-actions/trash/repository/trash.repository.ts @@ -0,0 +1,43 @@ +import { UmbMediaRecycleBinServerDataSource } from '../../../recycle-bin/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'; + +export class UmbBulkTrashMediaRepository extends UmbRepositoryBase implements UmbBulkTrashRepository { + #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; + #recycleBinSource = new UmbMediaRecycleBinServerDataSource(this); + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_NOTIFICATION_CONTEXT, (notificationContext) => { + this.#notificationContext = notificationContext; + }); + } + + async requestBulkTrash(args: UmbBulkTrashRequestArgs): Promise { + let count = 0; + + for (const unique of args.uniques) { + const { error } = await this.#recycleBinSource.trash({ unique }); + + if (error) { + const notification = { data: { message: error.message } }; + this.#notificationContext?.peek('danger', notification); + } else { + count++; + } + } + + if (count > 0) { + const notification = { data: { message: `Trashed ${count} media ${count === 1 ? 'item' : 'items'}` } }; + this.#notificationContext?.peek('positive', notification); + } + + return {}; + } +} + +export { UmbBulkTrashMediaRepository as api }; diff --git a/src/packages/property-editors/collection/config/bulk-action-permissions/permissions.element.ts b/src/packages/property-editors/collection/config/bulk-action-permissions/permissions.element.ts index 329506d0bc..01ea17b4e2 100644 --- a/src/packages/property-editors/collection/config/bulk-action-permissions/permissions.element.ts +++ b/src/packages/property-editors/collection/config/bulk-action-permissions/permissions.element.ts @@ -84,7 +84,7 @@ export class UmbPropertyEditorUICollectionPermissionsElement this.#onChange(e, 'allowBulkDelete')} - label="Allow bulk delete">`; + label="Allow bulk trash">`; } static override styles = [