Skip to content

Commit 5f1c65e

Browse files
nielslyngsoemadsrasmusseniOvergaard
authored
User group: permissions grouping (#20584)
* clean up * localizations * group user permission by entity type * adjustments * fix lint errors * Support granular permissions without entity type Updated granular permission handling to allow permissions that are not tied to a specific entity type. Adjusted rendering logic and manifest interface to support undefined or empty forEntityTypes, and added UI for displaying ungrouped granular permissions. * revert for now --------- Co-authored-by: Mads Rasmussen <[email protected]> Co-authored-by: Jacob Overgaard <[email protected]>
1 parent 20eedda commit 5f1c65e

File tree

14 files changed

+243
-166
lines changed

14 files changed

+243
-166
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export default {
22
user: {
33
// eslint-disable-next-line @typescript-eslint/naming-convention
4-
permissionsEntityGroup_dictionary: 'Dictionary',
4+
permissionsEntityGroup_dictionary: 'Dictionary permissions',
55
},
66
};

src/Umbraco.Web.UI.Client/src/assets/lang/da.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export default {
8080
content: 'Indhold',
8181
administration: 'Administration',
8282
structure: 'Struktur',
83+
general: 'Generelt',
8384
other: 'Andet',
8485
},
8586
actionDescriptions: {
@@ -2082,10 +2083,10 @@ export default {
20822083
permissionsGranularHelp: 'Sæt rettigheder for specifikke noder',
20832084
granularRightsLabel: 'Dokumenter',
20842085
granularRightsDescription: 'Tillad adgang til specifikke dokumenter',
2085-
permissionsEntityGroup_document: 'Indhold',
2086-
permissionsEntityGroup_media: 'Medie',
2087-
permissionsEntityGroup_member: 'Medlemmer',
2088-
'permissionsEntityGroup_document-property-value': 'Dokumentegenskabsværdi',
2086+
permissionsEntityGroup_document: 'Indholdsrettigheder',
2087+
permissionsEntityGroup_media: 'Medierettigheder',
2088+
permissionsEntityGroup_member: 'Medlemsrettigheder',
2089+
'permissionsEntityGroup_document-property-value': 'Feltrettigheder',
20892090
permissionNoVerbs: 'Ingen tilladte rettigheder',
20902091
profile: 'Profil',
20912092
searchAllChildren: "Søg alle 'børn'",

src/Umbraco.Web.UI.Client/src/assets/lang/en.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export default {
8080
content: 'Content',
8181
administration: 'Administration',
8282
structure: 'Structure',
83+
general: 'General',
8384
other: 'Other',
8485
},
8586
actionDescriptions: {
@@ -2104,10 +2105,10 @@ export default {
21042105
permissionsGranularHelp: 'Set permissions for specific nodes',
21052106
granularRightsLabel: 'Documents',
21062107
granularRightsDescription: 'Assign permissions to specific documents',
2107-
permissionsEntityGroup_document: 'Document',
2108-
permissionsEntityGroup_media: 'Media',
2109-
permissionsEntityGroup_member: 'Member',
2110-
'permissionsEntityGroup_document-property-value': 'Document Property Value',
2108+
permissionsEntityGroup_document: 'Document permissions',
2109+
permissionsEntityGroup_media: 'Media permissions',
2110+
permissionsEntityGroup_member: 'Member permissions',
2111+
'permissionsEntityGroup_document-property-value': 'Document Property Value permissions',
21112112
permissionNoVerbs: 'No allowed permissions',
21122113
profile: 'Profile',
21132114
searchAllChildren: 'Search all children',

src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-property-value/manifests.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> =
3939
alias: 'Umb.UserGranularPermission.Document.PropertyValue',
4040
name: 'Document Property Values Granular User Permission',
4141
weight: 950,
42+
forEntityTypes: [UMB_DOCUMENT_PROPERTY_VALUE_ENTITY_TYPE],
4243
element: () =>
4344
import(
4445
'./input-document-property-value-user-permission/input-document-property-value-user-permission.element.js'
4546
),
4647
meta: {
4748
schemaType: 'DocumentPropertyValuePermissionPresentationModel',
48-
label: 'Document Property Values',
49+
label: '#user_permissionsGranular',
4950
description: 'Assign permissions to Document property values',
5051
},
5152
},

src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document/input-document-granular-user-permission/input-document-granular-user-permission.element.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class UmbInputDocumentGranularUserPermissionElement extends UUIFormContro
2828
}
2929

3030
@property({ type: Array, attribute: false })
31-
fallbackPermissions: Array<string> = [];
31+
public fallbackPermissions: Array<string> = [];
3232

3333
@state()
3434
private _items?: Array<UmbDocumentItemModel>;
@@ -50,7 +50,13 @@ export class UmbInputDocumentGranularUserPermissionElement extends UUIFormContro
5050

5151
async #observePickedDocuments(uniques: Array<string>) {
5252
const { asObservable } = await this.#documentItemRepository.requestItems(uniques);
53-
this.observe(asObservable?.(), (items) => (this._items = items), 'observeItems');
53+
this.observe(
54+
asObservable?.(),
55+
(items) => {
56+
this._items = items;
57+
},
58+
'observeItems',
59+
);
5460
}
5561

5662
async #editGranularPermission(item: UmbDocumentItemModel) {

src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document/manifests.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
UMB_USER_PERMISSION_DOCUMENT_CREATE,
77
UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS,
88
UMB_USER_PERMISSION_DOCUMENT_PUBLISH,
9-
UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS,
109
UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH,
1110
UMB_USER_PERMISSION_DOCUMENT_UPDATE,
1211
UMB_USER_PERMISSION_DOCUMENT_DUPLICATE,
@@ -91,7 +90,7 @@ const permissions: Array<ManifestEntityUserPermission> = [
9190
description: '#actionDescriptions_publish',
9291
},
9392
},
94-
{
93+
/*{
9594
type: 'entityUserPermission',
9695
alias: UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS,
9796
name: 'Document Permissions User Permission',
@@ -101,7 +100,7 @@ const permissions: Array<ManifestEntityUserPermission> = [
101100
label: '#actions_setPermissions',
102101
description: '#actionDescriptions_rights',
103102
},
104-
},
103+
},*/
105104
{
106105
type: 'entityUserPermission',
107106
alias: UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH,
@@ -204,11 +203,12 @@ export const granularPermissions: Array<ManifestGranularUserPermission> = [
204203
alias: 'Umb.UserGranularPermission.Document',
205204
name: 'Document Granular User Permission',
206205
weight: 1000,
206+
forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE],
207207
element: () =>
208208
import('./input-document-granular-user-permission/input-document-granular-user-permission.element.js'),
209209
meta: {
210210
schemaType: 'DocumentPermissionPresentationModel',
211-
label: '#user_granularRightsLabel',
211+
label: '#user_permissionsGranular',
212212
description: '{#user_granularRightsDescription}',
213213
},
214214
},
Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { UMB_USER_GROUP_WORKSPACE_CONTEXT } from '../user-group-workspace.contex
22
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
33
import type { UmbExtensionElementInitializer } from '@umbraco-cms/backoffice/extension-api';
44
import type { ManifestGranularUserPermission } from '@umbraco-cms/backoffice/user-permission';
5-
import { html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
5+
import { html, customElement, state, nothing, property } from '@umbraco-cms/backoffice/external/lit';
66
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
77
import { filterFrozenArray } from '@umbraco-cms/backoffice/observable-api';
88

9-
@customElement('umb-user-group-granular-permission-list')
9+
@customElement('umb-user-group-entity-type-granular-permissions')
1010
export class UmbUserGroupGranularPermissionListElement extends UmbLitElement {
11+
@property()
12+
public entityType?: string;
13+
1114
@state()
1215
private _userGroupPermissions?: Array<any>;
1316

@@ -57,9 +60,21 @@ export class UmbUserGroupGranularPermissionListElement extends UmbLitElement {
5760

5861
override render() {
5962
if (!this._userGroupPermissions) return;
63+
64+
if (!this.entityType) {
65+
return html`
66+
<umb-extension-slot
67+
type="userGranularPermission"
68+
.filter=${(manifest: ManifestGranularUserPermission) =>
69+
manifest.forEntityTypes === undefined || manifest.forEntityTypes?.length === 0}
70+
.renderMethod=${this.#renderProperty}></umb-extension-slot>
71+
`;
72+
}
73+
6074
return html`<umb-extension-slot
6175
type="userGranularPermission"
62-
.props=${{ fallbackPermissions: this._userGroupFallbackPermissions }}
76+
.filter=${(manifest: ManifestGranularUserPermission) =>
77+
manifest.forEntityTypes?.includes(this.entityType!) || manifest.forEntityTypes?.length === 0}
6378
.renderMethod=${this.#renderProperty}></umb-extension-slot>`;
6479
}
6580

@@ -95,6 +110,6 @@ export default UmbUserGroupGranularPermissionListElement;
95110

96111
declare global {
97112
interface HTMLElementTagNameMap {
98-
'umb-user-group-granular-permission-list': UmbUserGroupGranularPermissionListElement;
113+
'umb-user-group-entity-type-granular-permissions': UmbUserGroupGranularPermissionListElement;
99114
}
100115
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
2+
import { html, customElement, state, repeat, css, nothing } from '@umbraco-cms/backoffice/external/lit';
3+
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
4+
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
5+
6+
import './user-group-entity-type-permissions.element.js';
7+
import './user-group-entity-type-granular-permissions.element.js';
8+
9+
@customElement('umb-user-group-entity-type-permission-groups')
10+
export class UmbUserGroupEntityTypePermissionGroupsElement extends UmbLitElement {
11+
@state()
12+
private _groups: Array<{ entityType: string; headline: string }> = [];
13+
14+
@state()
15+
private _hasGranularPermissionsWithNoEntityType = false;
16+
17+
constructor() {
18+
super();
19+
20+
this.observe(
21+
umbExtensionsRegistry.byType('entityUserPermission'),
22+
(manifests) => {
23+
const entityTypes = [...new Set(manifests.flatMap((manifest) => manifest.forEntityTypes))];
24+
this._groups = entityTypes
25+
.map((entityType) => {
26+
return {
27+
entityType,
28+
headline: this.localize.term(`user_permissionsEntityGroup_${entityType}`),
29+
};
30+
})
31+
.sort((a, b) => a.headline.localeCompare(b.headline));
32+
},
33+
'umbUserPermissionsObserver',
34+
);
35+
36+
this.#observeGranularPermissionsWithNoEntityType();
37+
}
38+
39+
#observeGranularPermissionsWithNoEntityType() {
40+
this.observe(
41+
umbExtensionsRegistry.byTypeAndFilter(
42+
'userGranularPermission',
43+
(manifest) => manifest.forEntityTypes === undefined || manifest.forEntityTypes.length === 0,
44+
),
45+
(manifests) => {
46+
this._hasGranularPermissionsWithNoEntityType = manifests.length > 0;
47+
},
48+
);
49+
}
50+
51+
override render() {
52+
return html`${repeat(
53+
this._groups,
54+
(group) => group.entityType,
55+
(group) =>
56+
html`<uui-box>
57+
<div slot="headline">${group.headline}</div>
58+
59+
<umb-user-group-entity-type-permissions
60+
.entityType=${group.entityType}></umb-user-group-entity-type-permissions>
61+
62+
<umb-user-group-entity-type-granular-permissions
63+
.entityType=${group.entityType}></umb-user-group-entity-type-granular-permissions>
64+
</uui-box>`,
65+
)}
66+
${this.#renderUngroupedGranularPermissions()}`;
67+
}
68+
69+
#renderUngroupedGranularPermissions() {
70+
if (!this._hasGranularPermissionsWithNoEntityType) return nothing;
71+
return html`<uui-box headline=${this.localize.term('general_rights')}>
72+
<umb-user-group-entity-type-granular-permissions></umb-user-group-entity-type-granular-permissions
73+
></uui-box>`;
74+
}
75+
76+
static override styles = [
77+
UmbTextStyles,
78+
css`
79+
uui-box {
80+
margin-top: var(--uui-size-space-6);
81+
}
82+
`,
83+
];
84+
}
85+
86+
export default UmbUserGroupEntityTypePermissionGroupsElement;
87+
88+
declare global {
89+
interface HTMLElementTagNameMap {
90+
'umb-user-group-entity-type-permission-groups': UmbUserGroupEntityTypePermissionGroupsElement;
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { UMB_USER_GROUP_WORKSPACE_CONTEXT } from '../user-group-workspace.context-token.js';
2+
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
3+
import { html, customElement, state, nothing, property } from '@umbraco-cms/backoffice/external/lit';
4+
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
5+
import type { UmbSelectionChangeEvent } from '@umbraco-cms/backoffice/event';
6+
7+
@customElement('umb-user-group-entity-type-permissions')
8+
export class UmbUserGroupEntityTypePermissionsElement extends UmbLitElement {
9+
@property()
10+
public entityType?: string;
11+
12+
@state()
13+
private _fallBackPermissions?: Array<string>;
14+
15+
#userGroupWorkspaceContext?: typeof UMB_USER_GROUP_WORKSPACE_CONTEXT.TYPE;
16+
17+
constructor() {
18+
super();
19+
20+
this.consumeContext(UMB_USER_GROUP_WORKSPACE_CONTEXT, (instance) => {
21+
this.#userGroupWorkspaceContext = instance;
22+
this.observe(
23+
this.#userGroupWorkspaceContext?.fallbackPermissions,
24+
(fallbackPermissions) => {
25+
this._fallBackPermissions = fallbackPermissions;
26+
},
27+
'umbUserGroupEntityUserPermissionsObserver',
28+
);
29+
});
30+
}
31+
32+
#onPermissionChange(event: UmbSelectionChangeEvent) {
33+
event.stopPropagation();
34+
const target = event.target as any;
35+
const verbs = target.allowedVerbs;
36+
if (verbs === undefined || verbs === null) throw new Error('The verbs are not defined');
37+
this.#userGroupWorkspaceContext?.setFallbackPermissions(verbs);
38+
}
39+
40+
override render() {
41+
return this.entityType
42+
? html`
43+
<umb-input-entity-user-permission
44+
.entityType=${this.entityType}
45+
.allowedVerbs=${this._fallBackPermissions || []}
46+
@change=${this.#onPermissionChange}></umb-input-entity-user-permission>
47+
`
48+
: nothing;
49+
}
50+
51+
static override styles = [UmbTextStyles];
52+
}
53+
54+
export default UmbUserGroupEntityTypePermissionsElement;
55+
56+
declare global {
57+
interface HTMLElementTagNameMap {
58+
'umb-user-group-entity-type-permissions': UmbUserGroupEntityTypePermissionsElement;
59+
}
60+
}

0 commit comments

Comments
 (0)