Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Delete/Trash referenced by #18351

Merged
merged 152 commits into from
Feb 20, 2025
Merged
Changes from all commits
Commits
Show all changes
152 commits
Select commit Hold shift + click to select a range
01516bb
add trash confirm modal
madsrasmussen Feb 6, 2025
9ccb58a
make referenceRepo optional + adjust styling
madsrasmussen Feb 6, 2025
f99562a
add referenceRepository to media trash action
madsrasmussen Feb 6, 2025
e7348f5
Merge branch 'v15/dev' into v15/feature/delete-referenced-by
madsrasmussen Feb 6, 2025
edb6a7c
wip entity-item-ref extension point
madsrasmussen Feb 6, 2025
62f1bd7
clean up
madsrasmussen Feb 6, 2025
fd06887
add ref list element
madsrasmussen Feb 6, 2025
977b047
fix styling
madsrasmussen Feb 6, 2025
0731f66
Update document-item-ref.element.ts
madsrasmussen Feb 6, 2025
5901d4a
move item repo
madsrasmussen Feb 6, 2025
5b0444f
implement for input member
madsrasmussen Feb 6, 2025
d073ab5
enable action slot
madsrasmussen Feb 7, 2025
964e86c
add null check
madsrasmussen Feb 7, 2025
e1cbf73
fix sorting again
madsrasmussen Feb 7, 2025
b986ebd
fix sorting again
madsrasmussen Feb 7, 2025
a98e3db
use member element
madsrasmussen Feb 7, 2025
3aba568
add draft styling back
madsrasmussen Feb 7, 2025
f4547b5
move item repository
madsrasmussen Feb 7, 2025
1adf436
implement for user input
madsrasmussen Feb 7, 2025
db9b9d3
pass readonly and standalone props
madsrasmussen Feb 7, 2025
1088b04
make editPath a state
madsrasmussen Feb 7, 2025
d70fb95
Update member-item-ref.element.ts
madsrasmussen Feb 7, 2025
914953b
Fix user item ref
madsrasmussen Feb 7, 2025
726d07d
remove open button
madsrasmussen Feb 7, 2025
efe75be
remove unused
madsrasmussen Feb 7, 2025
33f3759
remove unused
madsrasmussen Feb 8, 2025
95c12ec
check for section permission
madsrasmussen Feb 8, 2025
dea4393
Merge branch 'v15/feature/entity-item-ref-extension' into v15/feature…
madsrasmussen Feb 8, 2025
c1276e4
add null check
madsrasmussen Feb 9, 2025
93aa0ea
change to use entity-item-ref element
madsrasmussen Feb 9, 2025
dbd27cc
register media item ref
madsrasmussen Feb 9, 2025
e940f6e
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 10, 2025
0afe46c
add fallback element
madsrasmussen Feb 10, 2025
e446c42
Merge branch 'v15/feature/entity-item-ref-extension' into v15/feature…
madsrasmussen Feb 10, 2025
4394db3
show 3 references
madsrasmussen Feb 10, 2025
4ffa3d6
wip data mapper concept
madsrasmussen Feb 10, 2025
fa49110
add unique to modal route registration
madsrasmussen Feb 11, 2025
a76b30a
add unique to modal router
madsrasmussen Feb 11, 2025
ed0deb6
remove unused id
madsrasmussen Feb 11, 2025
0f72728
Update member-item-ref.element.ts
madsrasmussen Feb 11, 2025
13d5fea
append unique
madsrasmussen Feb 11, 2025
494fcbe
compare with old value
madsrasmussen Feb 11, 2025
3c67f00
only recreate the controller if the entity type changes
madsrasmussen Feb 11, 2025
b521750
fix console warning
madsrasmussen Feb 11, 2025
4988c80
Merge branch 'v15/feature/entity-item-ref-extension' into v15/feature…
madsrasmussen Feb 11, 2025
80b144e
implement for document item ref
madsrasmussen Feb 11, 2025
08d3add
Added $type to ReferenceResponseModels
Migaroez Feb 11, 2025
2cb20c8
move logic to item data resolver
madsrasmussen Feb 11, 2025
0c15b41
render draft as a tag
madsrasmussen Feb 11, 2025
7fe30d7
Update document-item-ref.element.ts
madsrasmussen Feb 11, 2025
e8ad351
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 11, 2025
cb4df75
Merge branch 'v15/feature/entity-item-ref-extension' into v15/feature…
madsrasmussen Feb 11, 2025
212ae20
Merge branch 'v15/feature/reference-types' into v15/feature/$type-dat…
madsrasmussen Feb 11, 2025
9be6d81
generate server models
madsrasmussen Feb 11, 2025
e47217e
add more helpers to data resolver
madsrasmussen Feb 11, 2025
f45ae29
export resolver
madsrasmussen Feb 11, 2025
2b0ae99
add observables
madsrasmussen Feb 11, 2025
0ae2420
use observables in document item ref
madsrasmussen Feb 11, 2025
6f7cc8e
add data resolver to tree item
madsrasmussen Feb 11, 2025
ef1638b
add observable state
madsrasmussen Feb 11, 2025
029cc3c
use const
madsrasmussen Feb 11, 2025
cdcbf31
align models
madsrasmussen Feb 11, 2025
f6ac0e4
get icon from document type object
madsrasmussen Feb 11, 2025
ff164c7
observe name and state
madsrasmussen Feb 11, 2025
fe1fbea
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 12, 2025
9b47824
update observed value when a new item is set
madsrasmussen Feb 12, 2025
d43c6cb
Merge branch 'v15/dev' into v15/feature/entity-item-ref-extension
madsrasmussen Feb 12, 2025
dbb744d
Merge branch 'v15/feature/entity-item-ref-extension' into v15/feature…
madsrasmussen Feb 12, 2025
f218486
update method name
madsrasmussen Feb 12, 2025
dc1b5ce
update method names
madsrasmussen Feb 12, 2025
a1444b5
pass model type
madsrasmussen Feb 12, 2025
41a0c11
pass context type
madsrasmussen Feb 12, 2025
9b3f45d
use api prop instead of context
madsrasmussen Feb 12, 2025
c075deb
use api prop instead of context
madsrasmussen Feb 12, 2025
cf34d19
fix types
madsrasmussen Feb 12, 2025
1b04ec7
use addUniquePaths for modal registration
madsrasmussen Feb 13, 2025
6dea5f0
Merge branch 'v15/feature/entity-item-ref-extension' into v15/feature…
madsrasmussen Feb 13, 2025
f1dc763
Merge branch 'v15/feature/display-current-variant-item-name' into v15…
madsrasmussen Feb 13, 2025
1c8432a
add fallback
madsrasmussen Feb 13, 2025
bafd96b
use ref list
madsrasmussen Feb 13, 2025
ec95d71
Merge branch 'v15/dev' into v15/feature/display-current-variant-item-…
madsrasmussen Feb 13, 2025
9f62268
Merge branch 'v15/feature/display-current-variant-item-name' into v15…
madsrasmussen Feb 13, 2025
870cd86
use reference items for media
madsrasmussen Feb 13, 2025
f43e32e
make mapper name more generic
madsrasmussen Feb 13, 2025
bb03968
make default ref item always readonly
madsrasmussen Feb 13, 2025
fbf9f2f
export types
madsrasmussen Feb 13, 2025
75face7
temp fake variants array
madsrasmussen Feb 13, 2025
63e03ac
add variants array to model
madsrasmussen Feb 13, 2025
575f3b2
Update media-references-workspace-info-app.element.ts
madsrasmussen Feb 13, 2025
81a0805
add variants to model
madsrasmussen Feb 13, 2025
934e03b
hardcode fake array
madsrasmussen Feb 13, 2025
54814c1
register media ref item
madsrasmussen Feb 13, 2025
bbc5fee
update mock data
madsrasmussen Feb 13, 2025
193d93a
dot not allow conditions for data mappers
madsrasmussen Feb 13, 2025
57b1145
add data mapper
madsrasmussen Feb 13, 2025
5f9d9d4
prefix info routes
madsrasmussen Feb 13, 2025
a731160
prefix all ref routes
madsrasmussen Feb 13, 2025
ddbec2a
return undefined if there is not edit path
madsrasmussen Feb 13, 2025
c401187
Merge branch 'v15/feature/$type-data-mapper-extension' into v15/featu…
madsrasmussen Feb 14, 2025
83cf3c2
Merge branch 'v15/dev' into v15/feature/display-current-variant-item-…
madsrasmussen Feb 14, 2025
23fd63d
Merge branch 'v15/feature/display-current-variant-item-name' into v15…
madsrasmussen Feb 14, 2025
6b1891e
Merge branch 'v15/feature/$type-data-mapper-extension' into v15/featu…
madsrasmussen Feb 14, 2025
b518e78
add types for reference data source + repository
madsrasmussen Feb 14, 2025
161c9d5
split trash with relation into its own action
madsrasmussen Feb 14, 2025
a8324b6
render descendants with references
madsrasmussen Feb 14, 2025
541753c
fix length check
madsrasmussen Feb 14, 2025
dcc9b3d
set standalone attribute
madsrasmussen Feb 14, 2025
d3e2db5
implement for media
madsrasmussen Feb 14, 2025
e311867
move trash
madsrasmussen Feb 16, 2025
1e084b4
wip delete with relation
madsrasmussen Feb 16, 2025
9e273f9
move to element
madsrasmussen Feb 16, 2025
c18e8e7
Merge branch 'v15/dev' into v15/feature/display-current-variant-item-…
madsrasmussen Feb 17, 2025
ece64d9
Merge branch 'v15/feature/display-current-variant-item-name' into v15…
madsrasmussen Feb 17, 2025
46b050a
fix name collision
madsrasmussen Feb 17, 2025
11b3e04
require data source identifier
madsrasmussen Feb 17, 2025
2d3ca29
use management api mapper
madsrasmussen Feb 17, 2025
1c5aedf
add management api mapper
madsrasmussen Feb 17, 2025
edefa42
Merge branch 'v15/dev' into v15/feature/display-current-variant-item-…
madsrasmussen Feb 17, 2025
ac8955e
fix type errors
madsrasmussen Feb 17, 2025
2cbe1ef
Update index.ts
madsrasmussen Feb 17, 2025
0a62426
align naming
madsrasmussen Feb 17, 2025
8dd474d
Merge branch 'v15/feature/display-current-variant-item-name' into v15…
madsrasmussen Feb 17, 2025
5a38296
Merge branch 'v15/feature/$type-data-mapper-extension' into v15/featu…
madsrasmussen Feb 17, 2025
f83ee80
show references when deleting a document
madsrasmussen Feb 17, 2025
1443b9c
Update delete-with-relation-modal.element.ts
madsrasmussen Feb 17, 2025
6815160
use deleteWithRelation kind for media
madsrasmussen Feb 17, 2025
0e206b6
clean up
madsrasmussen Feb 17, 2025
2823164
localize trash
madsrasmussen Feb 17, 2025
e0eca32
fix type
madsrasmussen Feb 17, 2025
1f264f6
Update trash-with-relation.action.ts
madsrasmussen Feb 17, 2025
c37302e
override confirm methods in trash and delete actions
madsrasmussen Feb 17, 2025
cbd7ea4
Update index.ts
madsrasmussen Feb 17, 2025
7655089
export constants
madsrasmussen Feb 17, 2025
a178424
Limit referenced-by document and media endpoints to references only.
AndyButland Feb 17, 2025
aafc107
Merge branch 'v15/task/limit-referenced-by-endpoints-to-dependencies-…
madsrasmussen Feb 18, 2025
a63bdcd
Merge branch 'v15/dev' into v15/feature/delete-referenced-by
madsrasmussen Feb 18, 2025
3d6be7a
Update document-reference-table.element.ts
madsrasmussen Feb 18, 2025
7559785
Merge branch 'v15/dev' into v15/feature/$type-data-mapper-extension
nielslyngsoe Feb 18, 2025
c17d198
add todo comment
nielslyngsoe Feb 18, 2025
6dcc897
implement path pattern for media item
nielslyngsoe Feb 18, 2025
8dd8293
clean up
nielslyngsoe Feb 18, 2025
608b103
more clean up
nielslyngsoe Feb 18, 2025
3e06749
sort imports
nielslyngsoe Feb 18, 2025
a92ea32
member edit path pattern
nielslyngsoe Feb 18, 2025
9b1534f
clean up
nielslyngsoe Feb 18, 2025
9b070c0
remove unused variant id
madsrasmussen Feb 19, 2025
32c3d17
export extension types
madsrasmussen Feb 19, 2025
cc94607
Merge branch 'v15/feature/$type-data-mapper-extension' into v15/featu…
madsrasmussen Feb 19, 2025
2b0ceab
align naming
madsrasmussen Feb 20, 2025
aa8bbea
Merge branch 'v15/feature/$type-data-mapper-extension' into v15/featu…
madsrasmussen Feb 20, 2025
6463334
Merge branch 'v15/dev' into v15/feature/delete-referenced-by
nielslyngsoe Feb 20, 2025
d7410a0
upper case Trash
nielslyngsoe Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Umbraco.Web.UI.Client/src/assets/lang/en.ts
Original file line number Diff line number Diff line change
@@ -510,6 +510,7 @@ export default {
confirmremoveusageof: 'Are you sure you want to remove the usage of <strong>%0%</strong>',
confirmlogout: 'Are you sure?',
confirmSure: 'Are you sure?',
confirmTrash: (name: string) => `Are you sure you want to move <strong>${name}</strong> to the Recycle Bin?`,
cut: 'Cut',
editDictionary: 'Edit dictionary item',
editLanguage: 'Edit language',
Original file line number Diff line number Diff line change
@@ -5,17 +5,19 @@ import { UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui';
@customElement('umb-ref-item')
export class UmbRefItemElement extends UmbElementMixin(UUIRefNodeElement) {
@property({ type: String })
icon = '';
icon? = '';

#iconElement = document.createElement('umb-icon');

protected override firstUpdated(_changedProperties: PropertyValues): void {
super.firstUpdated(_changedProperties);

// Temporary fix for the icon appending, this could in the future be changed to override a renderIcon method, or other ways to make this happen without appending children.
this.#iconElement.setAttribute('slot', 'icon');
this.#iconElement.setAttribute('name', this.icon);
this.appendChild(this.#iconElement);
if (this.icon) {
// Temporary fix for the icon appending, this could in the future be changed to override a renderIcon method, or other ways to make this happen without appending children.
this.#iconElement.setAttribute('slot', 'icon');
this.#iconElement.setAttribute('name', this.icon);
this.appendChild(this.#iconElement);
}
}

static override styles = [
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './create/constants.js';
export * from './delete/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UMB_ENTITY_ACTION_DELETE_KIND_MANIFEST } from './delete.action.kind.js';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UMB_ENTITY_ACTION_DEFAULT_KIND_MANIFEST } from '../../default/default.action.kind.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifest: UmbExtensionManifestKind = {
export const UMB_ENTITY_ACTION_DELETE_KIND_MANIFEST: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.EntityAction.Delete',
matchKind: 'delete',
@@ -22,3 +22,5 @@ export const manifest: UmbExtensionManifestKind = {
},
},
};

export const manifest = UMB_ENTITY_ACTION_DELETE_KIND_MANIFEST;
Original file line number Diff line number Diff line change
@@ -7,22 +7,30 @@ import type { UmbDetailRepository, UmbItemRepository } from '@umbraco-cms/backof
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';

export class UmbDeleteEntityAction extends UmbEntityActionBase<MetaEntityActionDeleteKind> {
export class UmbDeleteEntityAction<
MetaKind extends MetaEntityActionDeleteKind = MetaEntityActionDeleteKind,
> extends UmbEntityActionBase<MetaKind> {
// TODO: make base type for item and detail models
#localize = new UmbLocalizationController(this);

override async execute() {
if (!this.args.unique) throw new Error('Cannot delete an item without a unique identifier.');

const itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
const item = await this.#requestItem();

await this._confirmDelete(item);

const detailRepository = await createExtensionApiByAlias<UmbDetailRepository<any>>(
this,
this.args.meta.itemRepositoryAlias,
this.args.meta.detailRepositoryAlias,
);

const { data } = await itemRepository.requestItems([this.args.unique]);
const item = data?.[0];
if (!item) throw new Error('Item not found.');
await detailRepository.delete(this.args.unique);

await this.#notify();
}

async _confirmDelete(item: any) {
const headline = this.args.meta.confirm?.headline ?? '#actions_delete';
const message = this.args.meta.confirm?.message ?? '#defaultdialogs_confirmdelete';

@@ -33,14 +41,26 @@ export class UmbDeleteEntityAction extends UmbEntityActionBase<MetaEntityActionD
color: 'danger',
confirmLabel: '#general_delete',
});
}

const detailRepository = await createExtensionApiByAlias<UmbDetailRepository<any>>(
async #requestItem() {
if (!this.args.unique) throw new Error('Cannot delete an item without a unique identifier.');

const itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
this,
this.args.meta.detailRepositoryAlias,
this.args.meta.itemRepositoryAlias,
);
await detailRepository.delete(this.args.unique);

const { data } = await itemRepository.requestItems([this.args.unique]);
const item = data?.[0];
if (!item) throw new Error('Item not found.');

return item;
}

async #notify() {
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);

const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.args.unique,
entityType: this.args.entityType,
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type * from './delete/types.js';
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';

export type * from './common/types.js';
export type * from './default/types.js';
export type * from './entity-action-element.interface.js';
export type * from './entity-action.extension.js';
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './restore-from-recycle-bin/constants.js';
export * from './trash/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UMB_ENTITY_ACTION_TRASH_KIND_MANIFEST } from './trash.action.kind.js';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UMB_ENTITY_ACTION_DEFAULT_KIND_MANIFEST } from '../../../entity-action/default/default.action.kind.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifest: UmbExtensionManifestKind = {
export const UMB_ENTITY_ACTION_TRASH_KIND_MANIFEST: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.EntityAction.Trash',
matchKind: 'trash',
@@ -12,13 +12,12 @@ export const manifest: UmbExtensionManifestKind = {
kind: 'trash',
api: () => import('./trash.action.js'),
weight: 1150,
forEntityTypes: [],
meta: {
icon: 'icon-trash',
label: '#actions_trash',
itemRepositoryAlias: '',
recycleBinRepositoryAlias: '',
additionalOptions: true,
},
},
};

export const manifest = UMB_ENTITY_ACTION_TRASH_KIND_MANIFEST;
Original file line number Diff line number Diff line change
@@ -1,50 +1,69 @@
import { UmbEntityActionBase } from '../../../entity-action/entity-action-base.js';
import { UmbRequestReloadStructureForEntityEvent } from '../../../entity-action/request-reload-structure-for-entity.event.js';
import type { UmbRecycleBinRepository } from '../../recycle-bin-repository.interface.js';
import type { MetaEntityActionTrashKind } from './types.js';
import { UmbEntityTrashedEvent } from './trash.event.js';
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';

/**
* Entity action for trashing an item.
* @class UmbTrashEntityAction
* @augments {UmbEntityActionBase<MetaEntityActionTrashKind>}
*/
export class UmbTrashEntityAction extends UmbEntityActionBase<MetaEntityActionTrashKind> {
export class UmbTrashEntityAction<
MetaKindType extends MetaEntityActionTrashKind = MetaEntityActionTrashKind,
> extends UmbEntityActionBase<MetaKindType> {
#localize = new UmbLocalizationController(this);

/**
* Executes the action.
* @memberof UmbTrashEntityAction
*/
override async execute() {
if (!this.args.unique) throw new Error('Cannot trash an item without a unique identifier.');

const itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
const item = await this.#requestItem();

await this._confirmTrash(item);

const recycleBinRepository = await createExtensionApiByAlias<UmbRecycleBinRepository>(
this,
this.args.meta.itemRepositoryAlias,
this.args.meta.recycleBinRepositoryAlias,
);

const { data } = await itemRepository.requestItems([this.args.unique]);
const item = data?.[0];
if (!item) throw new Error('Item not found.');
await recycleBinRepository.requestTrash({ unique: this.args.unique });

this.#notify();
}

protected async _confirmTrash(item: any) {
const headline = '#actions_trash';
const message = '#defaultdialogs_confirmTrash';

// TODO: handle items with variants
await umbConfirmModal(this._host, {
headline: `Trash`,
content: `Are you sure you want to move ${item.name} to the recycle bin?`,
headline,
content: this.#localize.string(message, item.name),
color: 'danger',
confirmLabel: 'Trash',
confirmLabel: '#actions_trash',
});
}

const recycleBinRepository = await createExtensionApiByAlias<UmbRecycleBinRepository>(
async #requestItem() {
if (!this.args.unique) throw new Error('Cannot trash an item without a unique identifier.');

const itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
this,
this.args.meta.recycleBinRepositoryAlias,
this.args.meta.itemRepositoryAlias,
);
await recycleBinRepository.requestTrash({ unique: this.args.unique });

this.#notify();
const { data } = await itemRepository.requestItems([this.args.unique]);
const item = data?.[0];
if (!item) throw new Error('Item not found.');
return item;
}

async #notify() {
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS } from '../repository/index.js';
import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../item/constants.js';
import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js';
import { UMB_USER_PERMISSION_DOCUMENT_DELETE } from '../user-permissions/constants.js';
import { UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS } from '../reference/constants.js';
import { manifests as createBlueprintManifests } from './create-blueprint/manifests.js';
import { manifests as createManifests } from './create/manifests.js';
import { manifests as cultureAndHostnamesManifests } from './culture-and-hostnames/manifests.js';
@@ -15,13 +16,14 @@ import { UMB_ENTITY_IS_TRASHED_CONDITION_ALIAS } from '@umbraco-cms/backoffice/r
const entityActions: Array<UmbExtensionManifest> = [
{
type: 'entityAction',
kind: 'delete',
kind: 'deleteWithRelation',
alias: 'Umb.EntityAction.Document.Delete',
name: 'Delete Document Entity Action',
forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE],
meta: {
itemRepositoryAlias: UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS,
detailRepositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS,
referenceRepositoryAlias: UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS,
},
conditions: [
{
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import {
UMB_USER_PERMISSION_DOCUMENT_DELETE,
UMB_USER_PERMISSION_DOCUMENT_MOVE,
} from '../../constants.js';
import { UMB_DOCUMENT_REFERENCE_REPOSITORY_ALIAS } from '../../reference/constants.js';
import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../item/constants.js';
import {
UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS,
@@ -14,13 +15,14 @@ import {
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'entityAction',
kind: 'trash',
kind: 'trashWithRelation',
alias: 'Umb.EntityAction.Document.RecycleBin.Trash',
name: 'Trash Document Entity Action',
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: [
{
Original file line number Diff line number Diff line change
@@ -44,7 +44,8 @@ export class UmbDocumentReferenceTableElement extends UmbLitElement {
}

if (!data) return;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
this._items = data.items;
this._hasMoreReferences = data.total > this.#pageSize ? data.total - this.#pageSize : 0;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { UmbDocumentReferenceServerDataSource } from './document-reference.server.data.js';
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';

export class UmbDocumentReferenceRepository extends UmbControllerBase {
export class UmbDocumentReferenceRepository extends UmbControllerBase implements UmbEntityReferenceRepository {
#referenceSource: UmbDocumentReferenceServerDataSource;

constructor(host: UmbControllerHost) {
@@ -14,6 +15,11 @@ export class UmbDocumentReferenceRepository extends UmbControllerBase {
if (!unique) throw new Error(`unique is required`);
return this.#referenceSource.getReferencedBy(unique, 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);
}
}

export default UmbDocumentReferenceRepository;
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js';
import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import type { UmbEntityReferenceDataSource, UmbReferenceItemModel } from '@umbraco-cms/backoffice/relations';
import type { UmbPagedModel, UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository';
import { UmbManagementApiDataMapper } from '@umbraco-cms/backoffice/repository';

/**
* @class UmbDocumentReferenceServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDocumentReferenceServerDataSource extends UmbControllerBase {
export class UmbDocumentReferenceServerDataSource extends UmbControllerBase implements UmbEntityReferenceDataSource {
#dataMapper = new UmbManagementApiDataMapper(this);

/**
* Fetches the item for the given unique from the server
* @param {string} unique - The unique identifier of the item to fetch
* @returns {*}
* @param {number} skip - The number of items to skip
* @param {number} take - The number of items to take
* @returns {Promise<UmbDataSourceResponse<UmbPagedModel<UmbReferenceItemModel>>>} - Items that are referenced by the given unique
* @memberof UmbDocumentReferenceServerDataSource
*/
async getReferencedBy(unique: string, skip = 0, take = 20) {
async getReferencedBy(
unique: string,
skip = 0,
take = 20,
): Promise<UmbDataSourceResponse<UmbPagedModel<UmbReferenceItemModel>>> {
const { data, error } = await tryExecuteAndNotify(
this,
DocumentService.getDocumentByIdReferencedBy({ id: unique, skip, take }),
@@ -44,4 +54,36 @@ export class UmbDocumentReferenceServerDataSource extends UmbControllerBase {

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
* @param {number} skip - The number of items to skip
* @param {number} take - The number of items to take
* @returns {Promise<UmbDataSourceResponse<UmbPagedModel<UmbEntityModel>>>} - Any descendants of the given unique that is referenced by other items
* @memberof UmbDocumentReferenceServerDataSource
*/
async getReferencedDescendants(
unique: string,
skip: number = 0,
take: number = 20,
): Promise<UmbDataSourceResponse<UmbPagedModel<UmbEntityModel>>> {
const { data, error } = await tryExecuteAndNotify(
this,
DocumentService.getDocumentByIdReferencedDescendants({ id: unique, skip, take }),
);

if (data) {
const items: Array<UmbEntityModel> = data.items.map((item) => {
return {
unique: item.id,
entityType: UMB_DOCUMENT_ENTITY_TYPE,
};
});

return { data: { items, total: data.total } };
}

return { data, error };
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { UMB_MEDIA_DETAIL_REPOSITORY_ALIAS, UMB_MEDIA_ITEM_REPOSITORY_ALIAS } from '../constants.js';
import {
UMB_MEDIA_DETAIL_REPOSITORY_ALIAS,
UMB_MEDIA_ITEM_REPOSITORY_ALIAS,
UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS,
} from '../constants.js';
import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js';
import { manifests as createManifests } from './create/manifests.js';
import { manifests as moveManifests } from './move-to/manifests.js';
@@ -11,11 +15,12 @@ export const manifests: Array<UmbExtensionManifest> = [
type: 'entityAction',
alias: 'Umb.EntityAction.Media.Delete',
name: 'Delete Media Entity Action ',
kind: 'delete',
kind: 'deleteWithRelation',
forEntityTypes: [UMB_MEDIA_ENTITY_TYPE],
meta: {
itemRepositoryAlias: UMB_MEDIA_ITEM_REPOSITORY_ALIAS,
detailRepositoryAlias: UMB_MEDIA_DETAIL_REPOSITORY_ALIAS,
referenceRepositoryAlias: UMB_MEDIA_REFERENCE_REPOSITORY_ALIAS,
},
conditions: [
{
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ export const manifests: Array<UmbExtensionManifest> = [
{
type: 'entityItemRef',
alias: 'Umb.EntityItemRef.Media',
name: 'Member Entity Item Reference',
name: 'Media Entity Item Reference',
element: () => import('./media-item-ref.element.js'),
forEntityTypes: [UMB_MEDIA_ENTITY_TYPE],
},
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import {
UMB_MEDIA_ENTITY_TYPE,
} 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 {
UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS,
UMB_ENTITY_IS_TRASHED_CONDITION_ALIAS,
@@ -12,13 +13,14 @@ import {
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'entityAction',
kind: 'trash',
kind: 'trashWithRelation',
alias: 'Umb.EntityAction.Media.RecycleBin.Trash',
name: 'Trash Media Entity Action',
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: [
{
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { UmbMediaReferenceServerDataSource } from './media-reference.server.data.js';
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';

export class UmbMediaReferenceRepository extends UmbControllerBase {
export class UmbMediaReferenceRepository extends UmbControllerBase implements UmbEntityReferenceRepository {
#referenceSource: UmbMediaReferenceServerDataSource;

constructor(host: UmbControllerHost) {
@@ -14,6 +15,11 @@ export class UmbMediaReferenceRepository extends UmbControllerBase {
if (!unique) throw new Error(`unique is required`);
return this.#referenceSource.getReferencedBy(unique, 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);
}
}

export default UmbMediaReferenceRepository;
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js';
import { MediaService } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbManagementApiDataMapper } from '@umbraco-cms/backoffice/repository';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import type { UmbEntityReferenceDataSource, UmbReferenceItemModel } from '@umbraco-cms/backoffice/relations';
import type { UmbPagedModel, UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository';

/**
* @class UmbMediaReferenceServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbMediaReferenceServerDataSource extends UmbControllerBase {
export class UmbMediaReferenceServerDataSource extends UmbControllerBase implements UmbEntityReferenceDataSource {
#dataMapper = new UmbManagementApiDataMapper(this);

/**
* Fetches the item for the given id from the server
* Fetches the item for the given unique from the server
* @param {string} unique - The unique identifier of the item to fetch
* @returns {*}
* @param {number} skip - The number of items to skip
* @param {number} take - The number of items to take
* @returns {Promise<UmbDataSourceResponse<UmbPagedModel<UmbReferenceItemModel>>>} - Items that are referenced by the given unique
* @memberof UmbMediaReferenceServerDataSource
*/
async getReferencedBy(unique: string, skip = 0, take = 20) {
async getReferencedBy(
unique: string,
skip: number = 0,
take: number = 20,
): Promise<UmbDataSourceResponse<UmbPagedModel<UmbReferenceItemModel>>> {
const { data, error } = await tryExecuteAndNotify(
this,
MediaService.getMediaByIdReferencedBy({ id: unique, skip, take }),
@@ -44,4 +54,36 @@ export class UmbMediaReferenceServerDataSource extends UmbControllerBase {

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
* @param {number} skip - The number of items to skip
* @param {number} take - The number of items to take
* @returns {Promise<UmbDataSourceResponse<UmbPagedModel<UmbEntityModel>>>} - Any descendants of the given unique that is referenced by other items
* @memberof UmbMediaReferenceServerDataSource
*/
async getReferencedDescendants(
unique: string,
skip: number = 0,
take: number = 20,
): Promise<UmbDataSourceResponse<UmbPagedModel<UmbEntityModel>>> {
const { data, error } = await tryExecuteAndNotify(
this,
MediaService.getMediaByIdReferencedDescendants({ id: unique, skip, take }),
);

if (data) {
const items: Array<UmbEntityModel> = data.items.map((item) => {
return {
unique: item.id,
entityType: UMB_MEDIA_ENTITY_TYPE,
};
});

return { data: { items, total: data.total } };
}

return { data, error };
}
}
Original file line number Diff line number Diff line change
@@ -2,8 +2,9 @@ import { manifests as menuManifests } from './menu/manifests.js';
import { manifests as relationManifests } from './relations/manifests.js';
import { manifests as relationTypeManifests } from './relation-types/manifests.js';
import { manifests as workspaceManifests } from './workspace/manifests.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<UmbExtensionManifest> = [
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
...menuManifests,
...relationManifests,
...relationTypeManifests,
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './collection/constants.js';
export { UMB_RELATION_ENTITY_TYPE } from './entity.js';
export * from './collection/constants.js';
export * from './entity-actions/delete/constants.js';
export * from './entity-actions/trash/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './modal/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
import { UMB_ENTITY_ACTION_DELETE_KIND_MANIFEST } from '@umbraco-cms/backoffice/entity-action';

export const manifest: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.EntityAction.DeleteWithRelation',
matchKind: 'deleteWithRelation',
matchType: 'entityAction',
manifest: {
...UMB_ENTITY_ACTION_DELETE_KIND_MANIFEST.manifest,
type: 'entityAction',
kind: 'deleteWithRelation',
api: () => import('./delete-with-relation.action.js'),
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { MetaEntityActionDeleteWithRelationKind } from './types.js';
import { UMB_DELETE_WITH_RELATION_CONFIRM_MODAL } from './modal/constants.js';
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { UmbDeleteEntityAction } from '@umbraco-cms/backoffice/entity-action';

/**
* Entity action for deleting an item with relations.
* @class UmbDeleteWithRelationEntityAction
* @augments {UmbEntityActionBase<MetaEntityActionDeleteWithRelationKind>}
*/
export class UmbDeleteWithRelationEntityAction extends UmbDeleteEntityAction<MetaEntityActionDeleteWithRelationKind> {
override async _confirmDelete() {
if (!this.args.unique) throw new Error('Cannot delete an item without a unique identifier.');

const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);

const modal = modalManager.open(this, UMB_DELETE_WITH_RELATION_CONFIRM_MODAL, {
data: {
unique: this.args.unique,
entityType: this.args.entityType,
itemRepositoryAlias: this.args.meta.itemRepositoryAlias,
referenceRepositoryAlias: this.args.meta.referenceRepositoryAlias,
},
});

await modal.onSubmit();
}
}

export { UmbDeleteWithRelationEntityAction as api };
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './delete-with-relation.action.js';
export type * from './types.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { manifest as deleteKindManifest } from './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<UmbExtensionManifest | UmbExtensionManifestKind> = [
deleteKindManifest,
...modalManifests,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './delete-with-relation-modal.token.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type {
UmbDeleteWithRelationConfirmModalData,
UmbDeleteWithRelationConfirmModalValue,
} from './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 type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';

import '../../local-components/confirm-action-entity-references.element.js';

@customElement('umb-delete-with-relation-confirm-modal')
export class UmbDeleteWithRelationConfirmModalElement extends UmbModalBaseElement<
UmbDeleteWithRelationConfirmModalData,
UmbDeleteWithRelationConfirmModalValue
> {
@state()
_name?: string;

@state()
_referencesConfig?: any;

#itemRepository?: UmbItemRepository<any>;

protected override firstUpdated(_changedProperties: PropertyValues): void {
super.firstUpdated(_changedProperties);
this.#initData();
}

async #initData() {
if (!this.data) {
this.#itemRepository?.destroy();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just already destroy the itemRepository if it exists at this point.

Aka. removing the condition around it. :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah well, I mean move it above the condition

return;
}

this.#itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(this, this.data.itemRepositoryAlias);

const { data } = await this.#itemRepository.requestItems([this.data.unique]);
const item = data?.[0];
if (!item) throw new Error('Item not found.');

this._name = item.name;

this._referencesConfig = {
unique: this.data.unique,
itemRepositoryAlias: this.data.itemRepositoryAlias,
referenceRepositoryAlias: this.data.referenceRepositoryAlias,
};
}

override render() {
const headline = this.localize.string('#actions_delete');
const content = this.localize.string('#defaultdialogs_confirmdelete', this._name);

return html`
<uui-dialog-layout class="uui-text" headline=${headline}>
<p>${unsafeHTML(content)}</p>
${this._referencesConfig
? html`<umb-confirm-action-modal-entity-references
.config=${this._referencesConfig}></umb-confirm-action-modal-entity-references>`
: nothing}
<uui-button
slot="actions"
id="cancel"
label=${this.localize.term('general_cancel')}
@click=${this._rejectModal}></uui-button>
<uui-button
slot="actions"
id="confirm"
color="danger"
look="primary"
label=${this.localize.term('general_delete')}
@click=${this._submitModal}
${umbFocus()}></uui-button>
</uui-dialog-layout>
`;
}

static override styles = [
UmbTextStyles,
css`
uui-dialog-layout {
max-inline-size: 60ch;
}
`,
];
}

export { UmbDeleteWithRelationConfirmModalElement as element };

declare global {
interface HTMLElementTagNameMap {
'umb-delete-with-relation-confirm-modal': UmbDeleteWithRelationConfirmModalElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';

export interface UmbDeleteWithRelationConfirmModalData {
unique: string;
entityType: string;
itemRepositoryAlias: string;
referenceRepositoryAlias: string;
}

export type UmbDeleteWithRelationConfirmModalValue = undefined;

export const UMB_DELETE_WITH_RELATION_CONFIRM_MODAL = new UmbModalToken<
UmbDeleteWithRelationConfirmModalData,
UmbDeleteWithRelationConfirmModalValue
>('Umb.Modal.DeleteWithRelation', {
modal: {
type: 'dialog',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'modal',
alias: 'Umb.Modal.DeleteWithRelation',
name: 'Delete With Relation Modal',
element: () => import('./delete-with-relation-modal.element.js'),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ManifestEntityAction, MetaEntityActionDeleteKind } from '@umbraco-cms/backoffice/entity-action';

export interface ManifestEntityActionDeleteWithRelationKind
extends ManifestEntityAction<MetaEntityActionDeleteWithRelationKind> {
type: 'entityAction';
kind: 'deleteWithRelation';
}

export interface MetaEntityActionDeleteWithRelationKind extends MetaEntityActionDeleteKind {
referenceRepositoryAlias: string;
}

declare global {
interface UmbExtensionManifestMap {
umbManifestEntityActionDeleteWithRelationKind: ManifestEntityActionDeleteWithRelationKind;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import type { UmbEntityReferenceRepository, UmbReferenceItemModel } 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-action-modal-entity-references')
export class UmbConfirmActionModalEntityReferencesElement extends UmbLitElement {
@property({ type: Object, attribute: false })
config?: {
itemRepositoryAlias: string;
referenceRepositoryAlias: string;
entityType: string;
unique: string;
};

@state()
_referencedByItems: Array<UmbReferenceItemModel> = [];

@state()
_totalReferencedByItems: number = 0;

@state()
_totalDescendantsWithReferences: number = 0;

@state()
_descendantsWithReferences: Array<any> = [];

#itemRepository?: UmbItemRepository<any>;
#referenceRepository?: UmbEntityReferenceRepository;

#limitItems = 3;

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<UmbEntityReferenceRepository>(
this,
this.config?.referenceRepositoryAlias,
);

if (!this.config?.itemRepositoryAlias) {
throw new Error('Missing itemRepositoryAlias in config.');
}

this.#itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
this,
this.config.itemRepositoryAlias,
);

this.#loadReferencedBy();
this.#loadDescendantsWithReferences();
}

Check warning on line 75 in src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/local-components/confirm-action-entity-references.element.ts

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Complex Method

UmbConfirmActionModalEntityReferencesElement.initData has a cyclomatic complexity of 11, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

async #loadReferencedBy() {
if (!this.#referenceRepository) {
throw new Error('Failed to create reference repository.');
}

if (!this.config?.unique) {
throw new Error('Missing unique in data.');
}

const { data } = await this.#referenceRepository.requestReferencedBy(this.config.unique, 0, this.#limitItems);

if (data) {
this._referencedByItems = [...data.items];
this._totalReferencedByItems = data.total;
}
}

async #loadDescendantsWithReferences() {
if (!this.#referenceRepository) {
throw new Error('Failed to create reference repository.');
}

if (!this.#itemRepository) {
throw new Error('Failed to create item repository.');
}

// If the repository does not have the method, we don't need to load the referenced descendants.
if (!this.#referenceRepository.requestDescendantsWithReferences) return;

if (!this.config?.unique) {
throw new Error('Missing unique in data.');
}

const { data } = await this.#referenceRepository.requestDescendantsWithReferences(
this.config.unique,
0,
this.#limitItems,
);

if (data) {
this._totalDescendantsWithReferences = data.total;
const uniques = data.items.map((item) => item.unique).filter((unique) => unique) as Array<string>;
const { data: items } = await this.#itemRepository.requestItems(uniques);
this._descendantsWithReferences = items ?? [];
}
}

Check warning on line 122 in src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/local-components/confirm-action-entity-references.element.ts

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Complex Method

UmbConfirmActionModalEntityReferencesElement.loadDescendantsWithReferences has a cyclomatic complexity of 10, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

override render() {
return html`
${this.#renderItems('references_labelDependsOnThis', this._referencedByItems, this._totalReferencedByItems)}
${this.#renderItems(
'references_labelDependentDescendants',
this._descendantsWithReferences,
this._totalDescendantsWithReferences,
)}
`;
}

#renderItems(headline: string, items: Array<UmbReferenceItemModel>, total: number) {
if (total === 0) return nothing;

return html`
<h5 id="reference-headline">${this.localize.term(headline)}</h5>
<uui-ref-list>
${items.map(
(item) =>
html`<umb-entity-item-ref .item=${item} readonly ?standalone=${total === 1}></umb-entity-item-ref> `,
)}
</uui-ref-list>
${total > this.#limitItems
? html`<span>${this.localize.term('references_labelMoreReferences', total - this.#limitItems)}</span>`
: nothing}
`;
}

static override styles = [
UmbTextStyles,
css`
#reference-headline {
margin-bottom: var(--uui-size-3);
}
uui-ref-list {
margin-bottom: var(--uui-size-2);
}
`,
];
}

export { UmbConfirmActionModalEntityReferencesElement as element };

declare global {
interface HTMLElementTagNameMap {
'umb-confirm-action-modal-entity-references': UmbConfirmActionModalEntityReferencesElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './modal/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './trash-with-relation.action.js';
export type * from './types.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { manifest as trashKindManifest } from './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<UmbExtensionManifest | UmbExtensionManifestKind> = [trashKindManifest, ...modalManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './trash-with-relation-modal.token.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'modal',
alias: 'Umb.Modal.TrashWithRelation',
name: 'Trash With Relation Modal',
element: () => import('./trash-with-relation-modal.element.js'),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type {
UmbTrashWithRelationConfirmModalData,
UmbTrashWithRelationConfirmModalValue,
} from './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 type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';

// import of local component
import '../../local-components/confirm-action-entity-references.element.js';

@customElement('umb-trash-with-relation-confirm-modal')
export class UmbTrashWithRelationConfirmModalElement extends UmbModalBaseElement<
UmbTrashWithRelationConfirmModalData,
UmbTrashWithRelationConfirmModalValue
> {
@state()
_name?: string;

@state()
_referencesConfig?: any;

#itemRepository?: UmbItemRepository<any>;

protected override firstUpdated(_changedProperties: PropertyValues): void {
super.firstUpdated(_changedProperties);
this.#initData();
}

async #initData() {
if (!this.data) {
this.#itemRepository?.destroy();
return;
}

this.#itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(this, this.data.itemRepositoryAlias);

const { data } = await this.#itemRepository.requestItems([this.data.unique]);
const item = data?.[0];
if (!item) throw new Error('Item not found.');

this._name = item.name;

this._referencesConfig = {
unique: this.data.unique,
itemRepositoryAlias: this.data.itemRepositoryAlias,
referenceRepositoryAlias: this.data.referenceRepositoryAlias,
};
}

override render() {
const headline = this.localize.string('#actions_trash');
const content = this.localize.string('#defaultdialogs_confirmTrash', this._name);

return html`
<uui-dialog-layout class="uui-text" headline=${headline}>
<p>${unsafeHTML(content)}</p>
${this._referencesConfig
? html`<umb-confirm-action-modal-entity-references
.config=${this._referencesConfig}></umb-confirm-action-modal-entity-references>`
: nothing}
<uui-button slot="actions" id="cancel" label="Cancel" @click=${this._rejectModal}></uui-button>
<uui-button
slot="actions"
id="confirm"
color="danger"
look="primary"
label=${this.localize.term('actions_trash')}
@click=${this._submitModal}
${umbFocus()}></uui-button>
</uui-dialog-layout>
`;
}

static override styles = [
UmbTextStyles,
css`
uui-dialog-layout {
max-inline-size: 60ch;
}
`,
];
}

export { UmbTrashWithRelationConfirmModalElement as element };

declare global {
interface HTMLElementTagNameMap {
'umb-trash-with-relation-confirm-modal': UmbTrashWithRelationConfirmModalElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';

export interface UmbTrashWithRelationConfirmModalData {
unique: string;
entityType: string;
itemRepositoryAlias: string;
referenceRepositoryAlias: string;
}

export type UmbTrashWithRelationConfirmModalValue = undefined;

export const UMB_TRASH_WITH_RELATION_CONFIRM_MODAL = new UmbModalToken<
UmbTrashWithRelationConfirmModalData,
UmbTrashWithRelationConfirmModalValue
>('Umb.Modal.TrashWithRelation', {
modal: {
type: 'dialog',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
import { UMB_ENTITY_ACTION_TRASH_KIND_MANIFEST } from '@umbraco-cms/backoffice/recycle-bin';

export const manifest: UmbExtensionManifestKind = {
type: 'kind',
alias: 'Umb.Kind.EntityAction.TrashWithRelation',
matchKind: 'trashWithRelation',
matchType: 'entityAction',
manifest: {
...UMB_ENTITY_ACTION_TRASH_KIND_MANIFEST.manifest,
type: 'entityAction',
kind: 'trashWithRelation',
api: () => import('./trash-with-relation.action.js'),
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { MetaEntityActionTrashWithRelationKind } from './types.js';
import { UMB_TRASH_WITH_RELATION_CONFIRM_MODAL } from './modal/constants.js';
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { UmbTrashEntityAction } from '@umbraco-cms/backoffice/recycle-bin';

/**
* Entity action for trashing an item with relations.
* @class UmbTrashWithRelationEntityAction
* @augments {UmbEntityActionBase<MetaEntityActionTrashWithRelationKind>}
*/
export class UmbTrashWithRelationEntityAction extends UmbTrashEntityAction<MetaEntityActionTrashWithRelationKind> {
override async _confirmTrash(item: any) {
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);

const modal = modalManager.open(this, UMB_TRASH_WITH_RELATION_CONFIRM_MODAL, {
data: {
unique: item.unique,
entityType: item.entityType,
itemRepositoryAlias: this.args.meta.itemRepositoryAlias,
referenceRepositoryAlias: this.args.meta.referenceRepositoryAlias,
},
});

await modal.onSubmit();
}
}

export { UmbTrashWithRelationEntityAction as api };
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ManifestEntityAction } from '@umbraco-cms/backoffice/entity-action';
import type { MetaEntityActionTrashKind } from '@umbraco-cms/backoffice/recycle-bin';

export interface ManifestEntityActionTrashWithRelationKind
extends ManifestEntityAction<MetaEntityActionTrashWithRelationKind> {
type: 'entityAction';
kind: 'trashWithRelation';
}

export interface MetaEntityActionTrashWithRelationKind extends MetaEntityActionTrashKind {
referenceRepositoryAlias: string;
}

declare global {
interface UmbExtensionManifestMap {
umbManifestEntityActionTrashWithRelationKind: ManifestEntityActionTrashWithRelationKind;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
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<UmbExtensionManifest> = [...collectionManifests];
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
...collectionManifests,
...deleteManifests,
...trashManifests,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
import type {
DefaultReferenceResponseModel,
DocumentReferenceResponseModel,
MediaReferenceResponseModel,
} from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbDataSourceResponse, UmbPagedModel, UmbRepositoryResponse } from '@umbraco-cms/backoffice/repository';

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbReferenceItemModel extends UmbEntityModel {}

export type UmbReferenceModel =
| DefaultReferenceResponseModel
| DocumentReferenceResponseModel
| MediaReferenceResponseModel;

export interface UmbEntityReferenceRepository extends UmbApi {
requestReferencedBy(
unique: string,
skip?: number,
take?: number,
): Promise<UmbRepositoryResponse<UmbPagedModel<UmbReferenceItemModel>>>;
requestDescendantsWithReferences?(
unique: string,
skip?: number,
take?: number,
): Promise<UmbRepositoryResponse<UmbPagedModel<UmbEntityModel>>>;
}

export interface UmbEntityReferenceDataSource {
getReferencedBy(
unique: string,
skip?: number,
take?: number,
): Promise<UmbDataSourceResponse<UmbPagedModel<UmbReferenceItemModel>>>;
getReferencedDescendants?(
unique: string,
skip?: number,
take?: number,
): Promise<UmbDataSourceResponse<UmbPagedModel<UmbEntityModel>>>;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import type { UmbRelationEntityType } from './entity.js';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import type {
DefaultReferenceResponseModel,
DocumentReferenceResponseModel,
MediaReferenceResponseModel,
} from '@umbraco-cms/backoffice/external/backend-api';
export type * from './reference/types.js';

export interface UmbRelationDetailModel {
unique: string;
@@ -23,11 +18,3 @@ export interface UmbRelationDetailModel {
createDate: string;
comment: string | null;
}

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbReferenceItemModel extends UmbEntityModel {}

export type UmbReferenceModel =
| DefaultReferenceResponseModel
| DocumentReferenceResponseModel
| MediaReferenceResponseModel;

Unchanged files with check annotations Beta

export class UmbDataMappingResolver extends UmbControllerBase {
#apiCache = new Map<string, UmbDataMapping>();
async resolve(forDataSource: string, forDataModel: string): Promise<UmbDataMapping | undefined> {
if (!forDataSource) {
throw new Error('data source identifier is required');
}
if (!forDataModel) {
throw new Error('data identifier is required');
}
const manifest = this.#getManifestWithBestFit(forDataSource, forDataModel);
if (!manifest) {
return undefined;
}
// Check the cache before creating a new instance
if (this.#apiCache.has(manifest.alias)) {
return this.#apiCache.get(manifest.alias)!;
}
const dataMapping = await createExtensionApi<UmbDataMapping>(this, manifest);
if (!dataMapping) {
return undefined;
}
if (!dataMapping.map) {
throw new Error('Data Mapping does not have a map method.');
}
// Cache the api instance for future use
this.#apiCache.set(manifest.alias, dataMapping);
return dataMapping;
}

Check warning on line 43 in src/Umbraco.Web.UI.Client/src/packages/core/repository/data-mapper/mapping/data-mapping-resolver.ts

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v15/dev)

❌ New issue: Complex Method

UmbDataMappingResolver.resolve has a cyclomatic complexity of 10, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
#getManifestWithBestFit(forDataSource: string, forDataModel: string) {
const supportedManifests = this.#getSupportedManifests(forDataSource, forDataModel);