Skip to content

Commit 6f38a57

Browse files
madsrasmussenAndyButlandnielslyngsoe
authored
Document permission inheritance in UI (#18935)
* check the full path for permissions * fix race condition * wip update permission when variants change * Populate ancestor keys on document tree response items. * Populate ancestor keys on document collection response items. * Update OpenApi.json * generate server models * update types * map data * add ancestor context * set ancestors in context * use ancestor context in tree * clean up * provide ancestor context from a collection item * provide ancestor context from structure context * Use array of objects rather than Ids for the ancestor collection. * Update OpenApi.json. * add ancestor data to mocks * set ancestors ids in mocks * omit ancestors for recycle bin item * use correct models for document blueprint mock data * remove constructor * mock documents for testing * add user group permission test data * wip document user permission condition tests * generate new server models * update data efter server models update * clean up * Update entity-actions-table-column-view.element.ts * longer time for not found to appear * use arg * observe alias * set new the right place * remove const --------- Co-authored-by: Andy Butland <[email protected]> Co-authored-by: Niels Lyngsø <[email protected]> Co-authored-by: Niels Lyngsø <[email protected]>
1 parent a0e3ca6 commit 6f38a57

28 files changed

+515
-90
lines changed

src/Umbraco.Web.UI.Client/src/mocks/data/document-blueprint/document-blueprint.data.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
1-
import type { UmbMockDocumentModel } from '../document/document.data.js';
2-
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
1+
import {
2+
DocumentVariantStateModel,
3+
type DocumentBlueprintItemResponseModel,
4+
type DocumentBlueprintResponseModel,
5+
type DocumentBlueprintTreeItemResponseModel,
6+
} from '@umbraco-cms/backoffice/external/backend-api';
37

48
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
5-
export interface UmbMockDocumentBlueprintModel extends UmbMockDocumentModel {}
9+
export type UmbMockDocumentBlueprintModel = DocumentBlueprintResponseModel &
10+
DocumentBlueprintItemResponseModel &
11+
DocumentBlueprintTreeItemResponseModel;
612

713
export const data: Array<UmbMockDocumentBlueprintModel> = [
814
{
9-
ancestors: [],
10-
urls: [
11-
{
12-
culture: 'en-US',
13-
url: '/',
14-
},
15-
],
16-
template: null,
1715
id: 'the-simplest-document-id',
18-
createDate: '2023-02-06T15:32:05.350038',
19-
parent: null,
2016
documentType: {
2117
id: 'the-simplest-document-type-id',
2218
icon: 'icon-document',
2319
},
2420
hasChildren: false,
25-
noAccess: false,
26-
isProtected: false,
27-
isTrashed: false,
21+
isFolder: false,
22+
name: 'The Simplest Document Blueprint',
2823
variants: [
2924
{
3025
state: DocumentVariantStateModel.DRAFT,

src/Umbraco.Web.UI.Client/src/mocks/data/document-blueprint/document-blueprint.db.ts

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import type { UmbMockDocumentBlueprintModel } from './document-blueprint.data.js
88
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
99
import { UmbId } from '@umbraco-cms/backoffice/id';
1010
import type {
11-
CreateDocumentRequestModel,
12-
DocumentItemResponseModel,
13-
DocumentResponseModel,
14-
DocumentTreeItemResponseModel,
11+
CreateDocumentBlueprintRequestModel,
12+
DocumentBlueprintItemResponseModel,
13+
DocumentBlueprintResponseModel,
14+
DocumentBlueprintTreeItemResponseModel,
1515
DocumentValueResponseModel,
1616
} from '@umbraco-cms/backoffice/external/backend-api';
1717

@@ -23,52 +23,43 @@ export class UmbDocumentBlueprintMockDB extends UmbEntityMockDbBase<UmbMockDocum
2323
createMockDocumentBlueprintMapper,
2424
detailResponseMapper,
2525
);
26-
27-
constructor(data: Array<UmbMockDocumentBlueprintModel>) {
28-
super(data);
29-
}
3026
}
3127

32-
const treeItemMapper = (model: UmbMockDocumentBlueprintModel): Omit<DocumentTreeItemResponseModel, 'type'> => {
28+
const treeItemMapper = (model: UmbMockDocumentBlueprintModel): DocumentBlueprintTreeItemResponseModel => {
3329
const documentType = umbDocumentTypeMockDb.read(model.documentType.id);
3430
if (!documentType) throw new Error(`Document type with id ${model.documentType.id} not found`);
3531

3632
return {
37-
ancestors: model.ancestors,
3833
documentType: {
3934
icon: documentType.icon,
4035
id: documentType.id,
4136
},
4237
hasChildren: model.hasChildren,
4338
id: model.id,
44-
isProtected: model.isProtected,
45-
isTrashed: model.isTrashed,
46-
noAccess: model.noAccess,
39+
isFolder: model.isFolder,
40+
name: model.name,
4741
parent: model.parent,
48-
variants: model.variants,
49-
createDate: model.createDate,
5042
};
5143
};
5244

53-
const createMockDocumentBlueprintMapper = (request: CreateDocumentRequestModel): UmbMockDocumentBlueprintModel => {
45+
const createMockDocumentBlueprintMapper = (
46+
request: CreateDocumentBlueprintRequestModel,
47+
): UmbMockDocumentBlueprintModel => {
5448
const documentType = umbDocumentTypeMockDb.read(request.documentType.id);
5549
if (!documentType) throw new Error(`Document type with id ${request.documentType.id} not found`);
5650

5751
const now = new Date().toString();
5852

5953
return {
60-
ancestors: [],
6154
documentType: {
6255
id: documentType.id,
6356
icon: documentType.icon,
6457
collection: undefined, // TODO: get list from doc type when ready
6558
},
6659
hasChildren: false,
6760
id: request.id ? request.id : UmbId.new(),
68-
createDate: now,
69-
isProtected: false,
70-
isTrashed: false,
71-
noAccess: false,
61+
isFolder: false,
62+
name: request.variants[0].name,
7263
parent: request.parent,
7364
values: request.values as DocumentValueResponseModel[],
7465
variants: request.variants.map((variantRequest) => {
@@ -82,35 +73,27 @@ const createMockDocumentBlueprintMapper = (request: CreateDocumentRequestModel):
8273
publishDate: null,
8374
};
8475
}),
85-
urls: [],
8676
};
8777
};
8878

89-
const detailResponseMapper = (model: UmbMockDocumentBlueprintModel): DocumentResponseModel => {
79+
const detailResponseMapper = (model: UmbMockDocumentBlueprintModel): DocumentBlueprintResponseModel => {
9080
return {
9181
documentType: model.documentType,
9282
id: model.id,
93-
isTrashed: model.isTrashed,
94-
template: model.template,
95-
urls: model.urls,
9683
values: model.values,
9784
variants: model.variants,
9885
};
9986
};
10087

101-
const itemMapper = (model: UmbMockDocumentBlueprintModel): DocumentItemResponseModel => {
88+
const itemMapper = (model: UmbMockDocumentBlueprintModel): DocumentBlueprintItemResponseModel => {
10289
return {
10390
documentType: {
10491
collection: model.documentType.collection,
10592
icon: model.documentType.icon,
10693
id: model.documentType.id,
10794
},
108-
hasChildren: model.hasChildren,
10995
id: model.id,
110-
isProtected: model.isProtected,
111-
isTrashed: model.isTrashed,
112-
parent: model.parent,
113-
variants: model.variants,
96+
name: model.name,
11497
};
11598
};
11699

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import type { UmbMockDocumentModel } from '../document.data.js';
2+
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
3+
4+
const permissionsTestDocument = {
5+
ancestors: [],
6+
urls: [
7+
{
8+
culture: null,
9+
url: '/',
10+
},
11+
],
12+
template: null,
13+
id: 'permissions-document-id',
14+
createDate: '2023-02-06T15:32:05.350038',
15+
parent: null,
16+
documentType: {
17+
id: 'the-simplest-document-type-id',
18+
icon: 'icon-document',
19+
},
20+
hasChildren: false,
21+
noAccess: false,
22+
isProtected: false,
23+
isTrashed: false,
24+
values: [],
25+
variants: [
26+
{
27+
state: DocumentVariantStateModel.PUBLISHED,
28+
publishDate: '2023-02-06T15:32:24.957009',
29+
culture: null,
30+
segment: null,
31+
name: 'Permissions',
32+
createDate: '2023-02-06T15:32:05.350038',
33+
updateDate: '2023-02-06T15:32:24.957009',
34+
},
35+
],
36+
};
37+
38+
export const data: Array<UmbMockDocumentModel> = [
39+
permissionsTestDocument,
40+
{
41+
...permissionsTestDocument,
42+
ancestors: [{ id: 'permissions-document-id' }],
43+
hasChildren: false,
44+
id: 'permissions-1-document-id',
45+
parent: { id: 'permissions-document-id' },
46+
urls: [
47+
{
48+
culture: null,
49+
url: '/permission-1',
50+
},
51+
],
52+
variants: permissionsTestDocument.variants.map((variant) => ({
53+
...variant,
54+
name: 'Permissions 1',
55+
})),
56+
},
57+
{
58+
...permissionsTestDocument,
59+
ancestors: [{ id: 'permissions-document-id' }],
60+
hasChildren: true,
61+
id: 'permissions-2-document-id',
62+
parent: { id: 'permissions-document-id' },
63+
urls: [
64+
{
65+
culture: null,
66+
url: '/permissions-2',
67+
},
68+
],
69+
variants: permissionsTestDocument.variants.map((variant) => ({
70+
...variant,
71+
name: 'Permissions 2',
72+
})),
73+
},
74+
{
75+
...permissionsTestDocument,
76+
ancestors: [{ id: 'permissions-document-id' }, { id: 'permissions-2-document-id' }],
77+
hasChildren: true,
78+
id: 'permission-2-1-document-id',
79+
parent: { id: 'permissions-2-document-id' },
80+
urls: [
81+
{
82+
culture: null,
83+
url: '/permissions-1/permissions-2-1',
84+
},
85+
],
86+
variants: permissionsTestDocument.variants.map((variant) => ({
87+
...variant,
88+
name: 'Permissions 2.1',
89+
})),
90+
},
91+
{
92+
...permissionsTestDocument,
93+
ancestors: [{ id: 'permissions-document-id' }, { id: 'permissions-2-document-id' }],
94+
hasChildren: false,
95+
id: 'permissions-2-2-document-id',
96+
parent: { id: 'permissions-2-document-id' },
97+
urls: [
98+
{
99+
culture: null,
100+
url: '/permissions-1/permissions-2-2',
101+
},
102+
],
103+
variants: permissionsTestDocument.variants.map((variant) => ({
104+
...variant,
105+
name: 'Permissions 2.2',
106+
})),
107+
},
108+
{
109+
...permissionsTestDocument,
110+
ancestors: [
111+
{ id: 'permissions-document-id' },
112+
{ id: 'permissions-2-document-id' },
113+
{ id: 'permissions-2-2-document-id' },
114+
],
115+
hasChildren: false,
116+
id: 'permission-2-2-1-document-id',
117+
parent: { id: 'permissions-2-2-document-id' },
118+
urls: [
119+
{
120+
culture: null,
121+
url: '/permissions-1/permissions-2-2/permissions-2-2-1',
122+
},
123+
],
124+
variants: permissionsTestDocument.variants.map((variant) => ({
125+
...variant,
126+
name: 'Permissions 2.2.1',
127+
})),
128+
},
129+
];

src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { data as permissionsTestData } from './data/permissions-test.data.js';
12
import type {
23
DocumentItemResponseModel,
34
DocumentResponseModel,
@@ -1240,4 +1241,5 @@ export const data: Array<UmbMockDocumentModel> = [
12401241
},
12411242
],
12421243
},
1244+
...permissionsTestData,
12431245
];

src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,24 @@ const createMockDocumentMapper = (request: CreateDocumentRequestModel): UmbMockD
7878
const documentType = umbDocumentTypeMockDb.read(request.documentType.id);
7979
if (!documentType) throw new Error(`Document type with id ${request.documentType.id} not found`);
8080

81+
const isRoot = request.parent === null || request.parent === undefined;
82+
let ancestors: Array<{ id: string }> = [];
83+
84+
if (!isRoot) {
85+
const parentId = request.parent!.id;
86+
87+
const parentAncestors = umbDocumentMockDb.tree.getAncestorsOf({ descendantId: parentId }).map((ancestor) => {
88+
return {
89+
id: ancestor.id,
90+
};
91+
});
92+
ancestors = [...parentAncestors, { id: parentId }];
93+
}
94+
8195
const now = new Date().toString();
8296

8397
return {
84-
ancestors: [],
98+
ancestors,
8599
documentType: {
86100
id: documentType.id,
87101
icon: documentType.icon,

src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.data.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,29 @@ export const data: Array<UmbMockUserGroupModel> = [
2626
'Umb.Document.PublicAccess',
2727
'Umb.Document.Rollback',
2828
],
29-
permissions: [],
29+
permissions: [
30+
{
31+
$type: 'DocumentPermissionPresentationModel',
32+
document: {
33+
id: 'permissions-document-id',
34+
},
35+
verbs: ['Umb.Document.Read'],
36+
},
37+
{
38+
$type: 'DocumentPermissionPresentationModel',
39+
document: {
40+
id: 'permissions-2-document-id',
41+
},
42+
verbs: ['Umb.Document.Create', 'Umb.Document.Read'],
43+
},
44+
{
45+
$type: 'DocumentPermissionPresentationModel',
46+
document: {
47+
id: 'permissions-2-2-document-id',
48+
},
49+
verbs: ['Umb.Document.Delete', 'Umb.Document.Read'],
50+
},
51+
],
3052
sections: [
3153
UMB_CONTENT_SECTION_ALIAS,
3254
'Umb.Section.Media',
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
2-
import { html, nothing, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
2+
import { html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit';
33
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
44

5-
const elementName = 'umb-entity-actions-table-column-view';
6-
@customElement(elementName)
5+
@customElement('umb-entity-actions-table-column-view')
76
export class UmbEntityActionsTableColumnViewElement extends UmbLitElement {
87
@property({ attribute: false })
98
value?: UmbEntityModel;
109

11-
@state()
12-
_isOpen = false;
13-
1410
override render() {
1511
if (!this.value) return nothing;
1612

@@ -23,6 +19,6 @@ export class UmbEntityActionsTableColumnViewElement extends UmbLitElement {
2319

2420
declare global {
2521
interface HTMLElementTagNameMap {
26-
[elementName]: UmbEntityActionsTableColumnViewElement;
22+
'umb-entity-actions-table-column-view': UmbEntityActionsTableColumnViewElement;
2723
}
2824
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './contexts/ancestors/constants.js';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { UmbAncestorsEntityContext } from './ancestors.entity-context.js';
2+
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
3+
4+
export const UMB_ANCESTORS_ENTITY_CONTEXT = new UmbContextToken<UmbAncestorsEntityContext>('UmbAncestorsEntityContext');

0 commit comments

Comments
 (0)