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: Has Children Condition #18161

Draft
wants to merge 31 commits into
base: v15/dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f01f00f
add children to reload translation
madsrasmussen Jan 29, 2025
c623fa7
add has children condition + context
madsrasmussen Jan 29, 2025
86f034b
export
madsrasmussen Jan 29, 2025
331f445
register manifests
madsrasmussen Jan 29, 2025
91f57bf
set hasChildren value for tree items
madsrasmussen Jan 29, 2025
61cdfd5
add condition to document tree item
madsrasmussen Jan 29, 2025
ec19732
add has children condition to sort children of document
madsrasmussen Jan 29, 2025
72b8747
add conditions for media
madsrasmussen Jan 29, 2025
8ee1b74
Merge branch 'v15/dev' into v15/bugfix/has-children-condition
madsrasmussen Feb 4, 2025
bf13d7a
add entity trashed event
madsrasmussen Feb 4, 2025
55be805
dispatch event when entity is trashed
madsrasmussen Feb 4, 2025
5c02fc5
remove double event listeners
madsrasmussen Feb 4, 2025
af752b5
export action class
madsrasmussen Feb 4, 2025
c4676b5
Update default-tree.context.ts
madsrasmussen Feb 4, 2025
9d55d52
wip reload tree when entity is trashed
madsrasmussen Feb 4, 2025
d704329
move into data folder
madsrasmussen Feb 4, 2025
db9af62
clean up listeners
madsrasmussen Feb 4, 2025
860cdab
move manifest
madsrasmussen Feb 4, 2025
e726ef8
wip reload root
madsrasmussen Feb 4, 2025
d5f9d42
clean up
madsrasmussen Feb 4, 2025
d8c7e0c
add recycle bin tree item
madsrasmussen Feb 4, 2025
d71010d
use for media
madsrasmussen Feb 4, 2025
0818c72
pass entity types through manifest + add recycle bin tree item kind
madsrasmussen Feb 4, 2025
529cc7a
remove custom implementation for document recycle bin
madsrasmussen Feb 4, 2025
44470dc
use kind
madsrasmussen Feb 4, 2025
b5f2604
rename to supportedEntityTypes
madsrasmussen Feb 4, 2025
08aa5a6
Update recycle-bin-tree-item.context.ts
madsrasmussen Feb 4, 2025
2553e82
clean up
madsrasmussen Feb 4, 2025
5368766
Merge branch 'v15/bugfix/reload-recycle-bin-on-trashed' into v15/bugf…
madsrasmussen Feb 4, 2025
c82cb2c
remove condition
madsrasmussen Feb 4, 2025
8ec3a23
only show empty recycle bin if it has children
madsrasmussen Feb 4, 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
2 changes: 1 addition & 1 deletion src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default {
notify: 'Notifications',
protect: 'Public Access',
publish: 'Publish',
refreshNode: 'Reload',
refreshNode: 'Reload children',
remove: 'Remove',
rename: 'Rename',
republish: 'Republish entire site',
Expand Down
2 changes: 1 addition & 1 deletion src/Umbraco.Web.UI.Client/src/assets/lang/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default {
protect: 'Public Access',
publish: 'Publish',
readOnly: 'Read-only',
refreshNode: 'Reload',
refreshNode: 'Reload children',
remove: 'Remove',
rename: 'Rename',
republish: 'Republish entire site',
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './common/constants.js';
export * from './has-children/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const UMB_ENTITY_HAS_CHILDREN_CONDITION_ALIAS = 'Umb.Condition.EntityHasChildren';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { UMB_ENTITY_HAS_CHILDREN_CONDITION_ALIAS } from './constants.js';
import type { UmbConditionConfigBase } from '@umbraco-cms/backoffice/extension-api';

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbEntityHasChildrenConditionConfig
extends UmbConditionConfigBase<typeof UMB_ENTITY_HAS_CHILDREN_CONDITION_ALIAS> {}

declare global {
interface UmbExtensionConditionConfigMap {
UmbEntityHasChildrenConditionConfig: UmbEntityHasChildrenConditionConfig;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { UMB_ENTITY_HAS_CHILDREN_CONDITION_ALIAS } from './constants.js';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';

export const manifest: ManifestCondition = {
type: 'condition',
name: 'Entity Has Children Condition',
alias: UMB_ENTITY_HAS_CHILDREN_CONDITION_ALIAS,
api: () => import('./entity-has-children.condition.js'),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { UMB_HAS_CHILDREN_ENTITY_CONTEXT } from '../context/has-children.context-token.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type {
UmbConditionConfigBase,
UmbConditionControllerArguments,
UmbExtensionCondition,
} from '@umbraco-cms/backoffice/extension-api';
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';

export class UmbEntityHasChildrenCondition
extends UmbConditionBase<UmbConditionConfigBase>
implements UmbExtensionCondition
{
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(host, args);

this.consumeContext(UMB_HAS_CHILDREN_ENTITY_CONTEXT, (context) => {
this.observe(context.hasChildren, (hasChildren) => {
this.permitted = hasChildren === true;
});
});
}
}

export { UmbEntityHasChildrenCondition as api };
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './condition/constants.js';
export * from './context/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './has-children.context-token.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { UmbHasChildrenEntityContext } from './has-children.entity-context.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';

export const UMB_HAS_CHILDREN_ENTITY_CONTEXT = new UmbContextToken<UmbHasChildrenEntityContext>(
'UmbHasChildrenEntityContext',
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { UMB_HAS_CHILDREN_ENTITY_CONTEXT } from './has-children.context-token.js';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';

export class UmbHasChildrenEntityContext extends UmbContextBase<UmbHasChildrenEntityContext> {
#hasChildren = new UmbBooleanState(undefined);
public readonly hasChildren = this.#hasChildren.asObservable();

constructor(host: UmbControllerHost) {
super(host, UMB_HAS_CHILDREN_ENTITY_CONTEXT);
}

/**
* Gets the hasChildren state
* @returns {boolean} - The hasChildren state
* @memberof UmbHasChildrenEntityContext
*/
public getHasChildren(): boolean | undefined {
return this.#hasChildren.getValue();
}

/**
* Sets the hasChildren state
* @param {boolean} hasChildren - The hasChildren state
* @memberof UmbHasChildrenEntityContext
*/
public setHasChildren(hasChildren: boolean) {
this.#hasChildren.setValue(hasChildren);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './has-children.entity-context.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './context/index.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { manifest as conditionManifest } from './condition/entity-has-children.condition.manifest.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [conditionManifest];
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export * from './constants.js';
export * from './entity-action-base.js';
export * from './entity-action-list.element.js';
export * from './entity-action.event.js';
export * from './has-children/index.js';
export * from './entity-updated.event.js';

export type * from './types.js';

export { UmbRequestReloadStructureForEntityEvent } from './request-reload-structure-for-entity.event.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { manifests as createEntityActionManifests } from './common/create/manife
import { manifests as defaultEntityActionManifests } from './default/manifests.js';
import { manifests as deleteEntityActionManifests } from './common/delete/manifests.js';
import { manifests as duplicateEntityActionManifests } from './common/duplicate/manifests.js';
import { manifests as hasChildrenManifests } from './has-children/manifests.js';

import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

Expand All @@ -10,4 +11,5 @@ export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> =
...defaultEntityActionManifests,
...deleteEntityActionManifests,
...duplicateEntityActionManifests,
...hasChildrenManifests,
];
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { UmbTrashEntityAction } from './trash/index.js';
export { UmbTrashEntityAction, UmbEntityTrashedEvent } from './trash/index.js';
export { UmbRestoreFromRecycleBinEntityAction } from './restore-from-recycle-bin/restore-from-recycle-bin.action.js';
export { UmbEmptyRecycleBinEntityAction } from './empty-recycle-bin/empty-recycle-bin.action.js';
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type * from './types.js';
export * from './trash.action.js';
export * from './trash.event.js';
export type * from './types.js';
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { UmbEntityActionBase } from '../../../entity-action/entity-action-base.j
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';
Expand Down Expand Up @@ -43,15 +44,25 @@ export class UmbTrashEntityAction extends UmbEntityActionBase<MetaEntityActionTr
);
await recycleBinRepository.requestTrash({ unique: this.args.unique });

this.#notify();
}

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

const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.args.unique,
entityType: this.args.entityType,
});

actionEventContext.dispatchEvent(event);

// TODO: reload destination
const trashedEvent = new UmbEntityTrashedEvent({
unique: this.args.unique,
entityType: this.args.entityType,
});

actionEventContext.dispatchEvent(trashedEvent);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { UmbEntityActionEvent, type UmbEntityActionEventArgs } from '@umbraco-cms/backoffice/entity-action';

export class UmbEntityTrashedEvent extends UmbEntityActionEvent {
static readonly TYPE = 'entity-trashed';

constructor(args: UmbEntityActionEventArgs) {
super(UmbEntityTrashedEvent.TYPE, args);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { manifests as trashEntityActionManifests } from './entity-action/trash/manifests.js';
import { manifests as restoreFromRecycleBinEntityActionManifests } from './entity-action/restore-from-recycle-bin/manifests.js';
import { manifests as emptyRecycleBinEntityActionManifests } from './entity-action/empty-recycle-bin/manifests.js';
import { manifests as conditionManifests } from './conditions/manifests.js';
import { manifests as emptyRecycleBinEntityActionManifests } from './entity-action/empty-recycle-bin/manifests.js';
import { manifests as restoreFromRecycleBinEntityActionManifests } from './entity-action/restore-from-recycle-bin/manifests.js';
import { manifests as trashEntityActionManifests } from './entity-action/trash/manifests.js';
import { manifests as treeManifests } from './tree/manifests.js';

import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
...conditionManifests,
...emptyRecycleBinEntityActionManifests,
...restoreFromRecycleBinEntityActionManifests,
...trashEntityActionManifests,
...treeManifests,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { manifests as treeItemManifests } from './tree-item/manifests.js';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [...treeItemManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
import { UMB_TREE_ITEM_DEFAULT_KIND_MANIFEST } from '@umbraco-cms/backoffice/tree';

export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
{
type: 'kind',
alias: 'Umb.Kind.TreeItem.RecycleBin',
matchType: 'treeItem',
matchKind: 'recycleBin',
manifest: {
...UMB_TREE_ITEM_DEFAULT_KIND_MANIFEST.manifest,
type: 'treeItem',
kind: 'recycleBin',
api: () => import('./recycle-bin-tree-item.context.js'),
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { ManifestTreeItemRecycleBinKind } from './types.js';
import { UmbDefaultTreeItemContext, type UmbTreeItemModel, type UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbActionEventContext } from '@umbraco-cms/backoffice/action';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbEntityTrashedEvent } from '@umbraco-cms/backoffice/recycle-bin';

export class UmbRecycleBinTreeItemContext<
RecycleBinTreeItemModelType extends UmbTreeItemModel,
RecycleBinTreeRootModelType extends UmbTreeRootModel,
> extends UmbDefaultTreeItemContext<
RecycleBinTreeItemModelType,
RecycleBinTreeRootModelType,
ManifestTreeItemRecycleBinKind
> {
#actionEventContext?: UmbActionEventContext;

constructor(host: UmbControllerHost) {
super(host);

this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (instance) => {
this.#removeEventListener();
this.#actionEventContext = instance;
this.#actionEventContext?.addEventListener(UmbEntityTrashedEvent.TYPE, this.#onEntityTrashed as EventListener);
});
}

#onEntityTrashed = (event: UmbEntityTrashedEvent) => {
const entityType = event.getEntityType();
if (!entityType) throw new Error('Entity type is required');

const supportedEntityTypes = this.getManifest()?.meta.supportedEntityTypes;

if (!supportedEntityTypes) {
throw new Error('Entity types are missing from the manifest.');
}

if (supportedEntityTypes.includes(entityType)) {
this.loadChildren();
}
};

#removeEventListener = () => {
this.#actionEventContext?.removeEventListener(UmbEntityTrashedEvent.TYPE, this.#onEntityTrashed as EventListener);
};

override destroy(): void {
this.#removeEventListener();
super.destroy();
}
}

export { UmbRecycleBinTreeItemContext as api };
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ManifestTreeItem } from '@umbraco-cms/backoffice/tree';

export interface ManifestTreeItemRecycleBinKind extends ManifestTreeItem {
type: 'treeItem';
kind: 'recycleBin';
meta: MetaTreeItemRecycleBinKind;
}

export interface MetaTreeItemRecycleBinKind {
supportedEntityTypes: Array<string>;
}

declare global {
interface UmbExtensionManifestMap {
umbManifestTreeItemRecycleBinKind: ManifestTreeItemRecycleBinKind;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ export class UmbDefaultTreeContext<
this.#loadRootItems(reload);
return;
}

this.#loadTreeRoot();
}

async #loadTreeRoot() {
Expand Down Expand Up @@ -300,24 +302,10 @@ export class UmbDefaultTreeContext<

#consumeContexts() {
this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (instance) => {
this.#removeEventListeners();
this.#actionEventContext = instance;

this.#actionEventContext.removeEventListener(
UmbRequestReloadChildrenOfEntityEvent.TYPE,
this.#onReloadRequest as EventListener,
);

this.#actionEventContext.removeEventListener(
UmbRequestReloadChildrenOfEntityEvent.TYPE,
this.#onReloadRequest as EventListener,
);

this.#actionEventContext.addEventListener(
UmbRequestReloadChildrenOfEntityEvent.TYPE,
this.#onReloadRequest as EventListener,
);

this.#actionEventContext.addEventListener(
this.#actionEventContext?.addEventListener(
UmbRequestReloadChildrenOfEntityEvent.TYPE,
this.#onReloadRequest as EventListener,
);
Expand Down Expand Up @@ -354,17 +342,15 @@ export class UmbDefaultTreeContext<
this.loadTree();
};

override destroy(): void {
this.#actionEventContext?.removeEventListener(
UmbRequestReloadChildrenOfEntityEvent.TYPE,
this.#onReloadRequest as EventListener,
);

#removeEventListeners() {
this.#actionEventContext?.removeEventListener(
UmbRequestReloadChildrenOfEntityEvent.TYPE,
this.#onReloadRequest as EventListener,
);
}

override destroy(): void {
this.#removeEventListeners();
super.destroy();
}
}
Expand Down
Loading
Loading