Skip to content

Commit 7c5c3fd

Browse files
authored
Merge pull request #18154 from umbraco/v15/feature/enable-document-rollback-as-entity-action
Rollback as entity action + Picker data updates
2 parents 0480a32 + 9896119 commit 7c5c3fd

File tree

8 files changed

+393
-166
lines changed

8 files changed

+393
-166
lines changed

src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-detail-workspace-base.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import type { UmbModalToken } from '@umbraco-cms/backoffice/modal';
4242
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
4343
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
4444
import {
45+
UmbEntityUpdatedEvent,
4546
UmbRequestReloadChildrenOfEntityEvent,
4647
UmbRequestReloadStructureForEntityEvent,
4748
} from '@umbraco-cms/backoffice/entity-action';
@@ -709,13 +710,21 @@ export abstract class UmbContentDetailWorkspaceContextBase<
709710
);
710711
this._data.setCurrent(newCurrentData);
711712

713+
const unique = this.getUnique()!;
714+
const entityType = this.getEntityType();
715+
712716
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
713-
const event = new UmbRequestReloadStructureForEntityEvent({
714-
entityType: this.getEntityType(),
715-
unique: this.getUnique()!,
717+
const structureEvent = new UmbRequestReloadStructureForEntityEvent({ unique, entityType });
718+
eventContext.dispatchEvent(structureEvent);
719+
720+
const updatedEvent = new UmbEntityUpdatedEvent({
721+
unique,
722+
entityType,
723+
eventUnique: this._workspaceEventUnique,
716724
});
717725

718-
eventContext.dispatchEvent(event);
726+
eventContext.dispatchEvent(updatedEvent);
727+
719728
this._closeModal();
720729
}
721730

src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-action.event.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { UmbControllerEvent } from '@umbraco-cms/backoffice/controller-api';
22
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
33

44
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
5-
export interface UmbEntityActionEventArgs extends UmbEntityModel {}
5+
export interface UmbEntityActionEventArgs extends UmbEntityModel {
6+
eventUnique?: string;
7+
}
68

79
export class UmbEntityActionEvent<
810
ArgsType extends UmbEntityActionEventArgs = UmbEntityActionEventArgs,
@@ -21,4 +23,8 @@ export class UmbEntityActionEvent<
2123
getUnique(): string | null {
2224
return this._args.unique;
2325
}
26+
27+
getEventUnique(): string | undefined {
28+
return this._args.eventUnique;
29+
}
2430
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { UmbEntityActionEventArgs } from './entity-action.event.js';
2+
import { UmbEntityActionEvent } from './entity-action.event.js';
3+
4+
export class UmbEntityUpdatedEvent extends UmbEntityActionEvent {
5+
static readonly TYPE = 'entity-updated';
6+
7+
constructor(args: UmbEntityActionEventArgs) {
8+
super(UmbEntityUpdatedEvent.TYPE, args);
9+
}
10+
}

src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './constants.js';
55
export * from './entity-action-base.js';
66
export * from './entity-action-list.element.js';
77
export * from './entity-action.event.js';
8+
export * from './entity-updated.event.js';
89
export type * from './types.js';
910

1011
export { UmbRequestReloadStructureForEntityEvent } from './request-reload-structure-for-entity.event.js';

src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
44
import { type ManifestRepository, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
55
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
66
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
7+
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
8+
import { UmbEntityUpdatedEvent } from '@umbraco-cms/backoffice/entity-action';
79

810
const ObserveRepositoryAlias = Symbol();
911

@@ -14,6 +16,7 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
1416

1517
#init: Promise<unknown>;
1618
#currentRequest?: Promise<unknown>;
19+
#eventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE;
1720

1821
// the init promise is used externally for recognizing when the manager is ready.
1922
public get init() {
@@ -70,6 +73,20 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
7073
},
7174
null,
7275
);
76+
77+
this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (context) => {
78+
this.#eventContext = context;
79+
80+
this.#eventContext.removeEventListener(
81+
UmbEntityUpdatedEvent.TYPE,
82+
this.#onEntityUpdatedEvent as unknown as EventListener,
83+
);
84+
85+
this.#eventContext.addEventListener(
86+
UmbEntityUpdatedEvent.TYPE,
87+
this.#onEntityUpdatedEvent as unknown as EventListener,
88+
);
89+
});
7390
}
7491

7592
getUniques(): Array<string> {
@@ -122,6 +139,25 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
122139
}
123140
}
124141

142+
async #reloadItem(unique: string): Promise<void> {
143+
await this.#init;
144+
if (!this.repository) throw new Error('Repository is not initialized');
145+
146+
const { data } = await this.repository.requestItems([unique]);
147+
148+
if (data) {
149+
const items = this.getItems();
150+
const item = items.find((item) => this.#getUnique(item) === unique);
151+
152+
if (item) {
153+
const index = items.indexOf(item);
154+
const newItems = [...items];
155+
newItems[index] = data[0];
156+
this.#items.setValue(this.#sortByUniques(newItems));
157+
}
158+
}
159+
}
160+
125161
#sortByUniques(data: Array<ItemType>): Array<ItemType> {
126162
const uniques = this.getUniques();
127163
return [...data].sort((a, b) => {
@@ -130,4 +166,25 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
130166
return aIndex - bIndex;
131167
});
132168
}
169+
170+
#onEntityUpdatedEvent = (event: UmbEntityUpdatedEvent) => {
171+
const eventUnique = event.getUnique();
172+
173+
const items = this.getItems();
174+
if (items.length === 0) return;
175+
176+
// Ignore events if the entity is not in the list of items.
177+
const item = items.find((item) => this.#getUnique(item) === eventUnique);
178+
if (!item) return;
179+
180+
this.#reloadItem(item.unique);
181+
};
182+
183+
override destroy(): void {
184+
this.#eventContext?.removeEventListener(
185+
UmbEntityUpdatedEvent.TYPE,
186+
this.#onEntityUpdatedEvent as unknown as EventListener,
187+
);
188+
super.destroy();
189+
}
133190
}

src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { UmbEntityContext, type UmbEntityModel, type UmbEntityUnique } from '@um
77
import { UMB_DISCARD_CHANGES_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
88
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
99
import {
10+
UmbEntityUpdatedEvent,
1011
UmbRequestReloadChildrenOfEntityEvent,
1112
UmbRequestReloadStructureForEntityEvent,
1213
} from '@umbraco-cms/backoffice/entity-action';
@@ -15,6 +16,7 @@ import { umbExtensionsRegistry, type ManifestRepository } from '@umbraco-cms/bac
1516
import type { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
1617
import { UmbStateManager } from '@umbraco-cms/backoffice/utils';
1718
import { UmbValidationContext } from '@umbraco-cms/backoffice/validation';
19+
import { UmbId } from '@umbraco-cms/backoffice/id';
1820

1921
const LOADING_STATE_UNIQUE = 'umbLoadingEntityDetail';
2022

@@ -45,6 +47,8 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
4547
protected _getDataPromise?: Promise<any>;
4648
protected _detailRepository?: DetailRepositoryType;
4749

50+
#eventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE;
51+
4852
#parent = new UmbObjectState<{ entityType: string; unique: UmbEntityUnique } | undefined>(undefined);
4953
public readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
5054
public readonly parentEntityType = this.#parent.asObservablePart((parent) =>
@@ -85,6 +89,19 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
8589
window.addEventListener('willchangestate', this.#onWillNavigate);
8690
this.#observeRepository(args.detailRepositoryAlias);
8791
this.addValidationContext(this.validationContext);
92+
93+
this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (context) => {
94+
this.#eventContext = context;
95+
96+
this.#eventContext.removeEventListener(
97+
UmbEntityUpdatedEvent.TYPE,
98+
this.#onEntityUpdatedEvent as unknown as EventListener,
99+
);
100+
this.#eventContext.addEventListener(
101+
UmbEntityUpdatedEvent.TYPE,
102+
this.#onEntityUpdatedEvent as unknown as EventListener,
103+
);
104+
});
88105
}
89106

90107
/**
@@ -307,13 +324,21 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
307324
this._data.setPersisted(data);
308325
this._data.setCurrent(data);
309326

310-
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
311-
const event = new UmbRequestReloadStructureForEntityEvent({
312-
unique: this.getUnique()!,
313-
entityType: this.getEntityType(),
327+
const unique = this.getUnique()!;
328+
const entityType = this.getEntityType();
329+
330+
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
331+
const event = new UmbRequestReloadStructureForEntityEvent({ unique, entityType });
332+
333+
eventContext.dispatchEvent(event);
334+
335+
const updatedEvent = new UmbEntityUpdatedEvent({
336+
unique,
337+
entityType,
338+
eventUnique: this._workspaceEventUnique,
314339
});
315340

316-
actionEventContext.dispatchEvent(event);
341+
eventContext.dispatchEvent(updatedEvent);
317342
}
318343

319344
#allowNavigateAway = false;
@@ -396,8 +421,30 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
396421
}
397422
}
398423

424+
// Discriminator to identify events from this workspace context
425+
protected readonly _workspaceEventUnique = UmbId.new();
426+
427+
#onEntityUpdatedEvent = (event: UmbEntityUpdatedEvent) => {
428+
const eventEntityUnique = event.getUnique();
429+
const eventEntityType = event.getEntityType();
430+
const eventDiscriminator = event.getEventUnique();
431+
432+
// Ignore events for other entities
433+
if (eventEntityType !== this.getEntityType()) return;
434+
if (eventEntityUnique !== this.getUnique()) return;
435+
436+
// Ignore events from this workspace so we don't reload the data twice. Ex saving this workspace
437+
if (eventDiscriminator === this._workspaceEventUnique) return;
438+
439+
this.reload();
440+
};
441+
399442
public override destroy(): void {
400443
window.removeEventListener('willchangestate', this.#onWillNavigate);
444+
this.#eventContext?.removeEventListener(
445+
UmbEntityUpdatedEvent.TYPE,
446+
this.#onEntityUpdatedEvent as unknown as EventListener,
447+
);
401448
this._detailRepository?.destroy();
402449
this.#entityContext.destroy();
403450
super.destroy();
Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import {
2-
UMB_USER_PERMISSION_DOCUMENT_ROLLBACK,
3-
UMB_DOCUMENT_ENTITY_TYPE,
4-
UMB_DOCUMENT_WORKSPACE_ALIAS,
5-
} from '../../constants.js';
6-
import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace';
1+
import { UMB_USER_PERMISSION_DOCUMENT_ROLLBACK, UMB_DOCUMENT_ENTITY_TYPE } from '../../constants.js';
72
import { UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS } from '@umbraco-cms/backoffice/recycle-bin';
83

94
export const manifests: Array<UmbExtensionManifest> = [
@@ -12,7 +7,7 @@ export const manifests: Array<UmbExtensionManifest> = [
127
kind: 'default',
138
alias: 'Umb.EntityAction.Document.Rollback',
149
name: 'Rollback Document Entity Action',
15-
weight: 500,
10+
weight: 450,
1611
api: () => import('./rollback.action.js'),
1712
forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE],
1813
meta: {
@@ -27,12 +22,6 @@ export const manifests: Array<UmbExtensionManifest> = [
2722
{
2823
alias: UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS,
2924
},
30-
/* Currently the rollback is tightly coupled to the workspace contexts so we only allow it to show up
31-
In the document workspace. */
32-
{
33-
alias: UMB_WORKSPACE_CONDITION_ALIAS,
34-
match: UMB_DOCUMENT_WORKSPACE_ALIAS,
35-
},
3625
],
3726
},
3827
];

0 commit comments

Comments
 (0)