Skip to content

Commit

Permalink
Merge pull request #2127 from umbraco/v14/feature/entity-bulk-action-…
Browse files Browse the repository at this point in the history
…trash

Feature: Entity Bulk Action `trash` kind
  • Loading branch information
leekelleher authored Jul 18, 2024
2 parents 777c693 + b9140d2 commit 761c129
Show file tree
Hide file tree
Showing 19 changed files with 200 additions and 99 deletions.
1 change: 1 addition & 0 deletions src/packages/core/entity-bulk-action/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './duplicate-to/index.js';
export * from './move-to/index.js';
export * from './trash/index.js';
2 changes: 2 additions & 0 deletions src/packages/core/entity-bulk-action/common/trash/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { UmbBulkTrashRepository } from './trash-repository.interface.js';
export type { UmbBulkTrashRequestArgs } from './types.js';
Original file line number Diff line number Diff line change
@@ -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<ManifestTypes | UmbBackofficeManifestKind> = [trashKindManifest];
Original file line number Diff line number Diff line change
@@ -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<UmbRepositoryErrorResponse>;
}
Original file line number Diff line number Diff line change
@@ -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: '',
},
},
};
53 changes: 53 additions & 0 deletions src/packages/core/entity-bulk-action/common/trash/trash.action.ts
Original file line number Diff line number Diff line change
@@ -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<MetaEntityBulkActionTrashKind> {
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<UmbBulkTrashRepository>(
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 };
3 changes: 3 additions & 0 deletions src/packages/core/entity-bulk-action/common/trash/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface UmbBulkTrashRequestArgs {
uniques: Array<string>;
}
2 changes: 2 additions & 0 deletions src/packages/core/entity-bulk-action/manifests.ts
Original file line number Diff line number Diff line change
@@ -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<ManifestTypes | UmbBackofficeManifestKind> = [
...defaultEntityBulkActionManifests,
...duplicateEntityBulkActionManifests,
...moveToEntityBulkActionManifests,
...trashEntityBulkActionManifests,
];
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,13 @@ export interface MetaEntityBulkActionMoveToKind extends MetaEntityBulkActionDefa
foldersOnly?: boolean;
treeAlias: string;
}

// TRASH
export interface ManifestEntityBulkActionTrashKind extends ManifestEntityBulkAction<MetaEntityBulkActionTrashKind> {
type: 'entityBulkAction';
kind: 'trash';
}

export interface MetaEntityBulkActionTrashKind extends MetaEntityBulkActionDefaultKind {
bulkTrashRepositoryAlias: string;
}

This file was deleted.

This file was deleted.

57 changes: 3 additions & 54 deletions src/packages/media/media/entity-bulk-actions/manifests.ts
Original file line number Diff line number Diff line change
@@ -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<ManifestEntityBulkAction> = [
{
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<ManifestTypes> = [...entityBulkActions, ...moveToManifests];
export const manifests: Array<ManifestTypes> = [...moveToManifests, ...trashManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UmbBulkTrashMediaRepository, UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './repository/index.js';
34 changes: 34 additions & 0 deletions src/packages/media/media/entity-bulk-actions/trash/manifests.ts
Original file line number Diff line number Diff line change
@@ -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<ManifestTypes> = [bulkTrashAction, ...repositoryManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS = 'Umb.Repository.Media.BulkTrash';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { UmbBulkTrashMediaRepository } from './trash.repository.js';
export { UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './constants.js';
Original file line number Diff line number Diff line change
@@ -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<ManifestTypes> = [bulkTrashRepository];
Original file line number Diff line number Diff line change
@@ -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<UmbRepositoryErrorResponse> {
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 };
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class UmbPropertyEditorUICollectionPermissionsElement
<uui-toggle
?checked=${this.value.allowBulkDelete}
@change=${(e: UUIBooleanInputEvent) => this.#onChange(e, 'allowBulkDelete')}
label="Allow bulk delete"></uui-toggle>`;
label="Allow bulk trash"></uui-toggle>`;
}

static override styles = [
Expand Down

0 comments on commit 761c129

Please sign in to comment.