Merge pull request #2127 from umbraco/v14/feature/entity-bulk-action-trash
Feature: Entity Bulk Action `trash` kind
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export * from './duplicate-to/index.js';
|
||||
export * from './move-to/index.js';
|
||||
export * from './trash/index.js';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export type { UmbBulkTrashRepository } from './trash-repository.interface.js';
|
||||
export type { UmbBulkTrashRequestArgs } from './types.js';
|
||||
@@ -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];
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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 };
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface UmbBulkTrashRequestArgs {
|
||||
uniques: Array<string>;
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action';
|
||||
|
||||
export class UmbMediaDeleteEntityBulkAction extends UmbEntityBulkActionBase<object> {
|
||||
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:
|
||||
<ul style="list-style-type: none; padding: 0; margin: 0; margin-top: var(--uui-size-space-2);">
|
||||
${data.map((item) => html`<li>${item.name}</li>`)}
|
||||
</ul>
|
||||
`,
|
||||
color: 'danger',
|
||||
confirmLabel: 'Delete',
|
||||
});
|
||||
|
||||
await modalContext.onSubmit();
|
||||
await this.repository?.trash(this.selection);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbMediaDeleteEntityBulkAction as api };
|
||||
@@ -1,9 +0,0 @@
|
||||
import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action';
|
||||
|
||||
export class UmbDuplicateMediaEntityBulkAction extends UmbEntityBulkActionBase<object> {
|
||||
async execute() {
|
||||
console.log('execute bulk duplicate');
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbDuplicateMediaEntityBulkAction as api };
|
||||
@@ -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];
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { UmbBulkTrashMediaRepository, UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './repository/index.js';
|
||||
@@ -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];
|
||||
@@ -0,0 +1 @@
|
||||
export const UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS = 'Umb.Repository.Media.BulkTrash';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { UmbBulkTrashMediaRepository } from './trash.repository.js';
|
||||
export { UMB_BULK_TRASH_MEDIA_REPOSITORY_ALIAS } from './constants.js';
|
||||
@@ -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];
|
||||
@@ -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 };
|
||||
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user