Skip to content

Commit 76d3ba2

Browse files
committed
Permissions UI: Merge strategy #8805
1 parent 6f49d64 commit 76d3ba2

File tree

12 files changed

+286
-212
lines changed

12 files changed

+286
-212
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {Principal} from '@enonic/lib-admin-ui/security/Principal';
2+
import {Permission} from '../../access/Permission';
3+
4+
export interface AccessControlChangedPermissions {
5+
persisted?: Permission[];
6+
updated?: Permission[];
7+
}
8+
9+
export class AccessControlChangedItem {
10+
11+
private readonly principal: Principal;
12+
13+
private readonly permissions: AccessControlChangedPermissions;
14+
15+
constructor(principal: Principal, permissions: AccessControlChangedPermissions) {
16+
this.principal = principal;
17+
this.permissions = permissions;
18+
}
19+
20+
getPrincipal(): Principal {
21+
return this.principal;
22+
}
23+
24+
getPermissions(): AccessControlChangedPermissions {
25+
return this.permissions;
26+
}
27+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import {DivEl} from '@enonic/lib-admin-ui/dom/DivEl';
2+
import {AccessControlChangedItem} from './AccessControlChangedItem';
3+
import {ApplyPermissionsScope} from './PermissionsData';
4+
import {i18n} from '@enonic/lib-admin-ui/util/Messages';
5+
import {Permission} from '../../access/Permission';
6+
import * as Q from 'q';
7+
import {PrincipalViewer} from '@enonic/lib-admin-ui/ui/security/PrincipalViewer';
8+
import {SpanEl} from '@enonic/lib-admin-ui/dom/SpanEl';
9+
import {PermissionsHelper} from '../../access/PermissionsHelper';
10+
import {PermissionStateView} from './PermissionStateView';
11+
import {EditPermissionState} from './EditPermissionState';
12+
13+
export class AccessControlChangedItemView
14+
extends DivEl {
15+
16+
private readonly item: AccessControlChangedItem;
17+
18+
private readonly applyTo: ApplyPermissionsScope;
19+
20+
private readonly resetChildPermissions: boolean;
21+
22+
constructor(item: AccessControlChangedItem, applyTo: ApplyPermissionsScope, resetChildPermissions: boolean) {
23+
super('access-control-changed-item-view');
24+
25+
this.item = item;
26+
this.applyTo = applyTo;
27+
this.resetChildPermissions = resetChildPermissions;
28+
}
29+
30+
private getAccessValue(): string {
31+
if (this.isEntryRemoved()) {
32+
return i18n('dialog.permissions.step.strategy.item.removed');
33+
}
34+
35+
if (this.isEntryAdded()) {
36+
return i18n('dialog.permissions.step.strategy.item.added');
37+
}
38+
39+
return i18n('dialog.permissions.step.strategy.item.modified');
40+
}
41+
42+
private isEntryRemoved(): boolean {
43+
return !this.item.getPermissions().updated || this.item.getPermissions().updated?.length === 0;
44+
}
45+
46+
private isEntryAdded(): boolean {
47+
return !this.item.getPermissions().persisted && !!this.item.getPermissions().updated;
48+
}
49+
50+
private getPermissionState(permission: Permission): EditPermissionState {
51+
const isPermInPersisted = this.item.getPermissions().persisted && this.item.getPermissions().persisted?.indexOf(permission) !== -1;
52+
const isPermInUpdated = this.item.getPermissions().updated && this.item.getPermissions().updated?.indexOf(permission) !== -1;
53+
54+
if (!isPermInPersisted) { // permission was not present before
55+
return isPermInUpdated ? EditPermissionState.ADDED : EditPermissionState.UNSET;
56+
}
57+
58+
// permission was present before
59+
60+
if (isPermInUpdated) {
61+
return EditPermissionState.UNCHANGED;
62+
}
63+
64+
return EditPermissionState.REMOVED;
65+
}
66+
67+
doRender(): Q.Promise<boolean> {
68+
return super.doRender().then((rendered: boolean) => {
69+
this.addClass(this.applyTo);
70+
this.addClass(this.resetChildPermissions ? 'reset' : 'merge');
71+
this.toggleClass('removed', this.isEntryRemoved());
72+
const principalViewer = new PrincipalViewer();
73+
principalViewer.setObject(this.item.getPrincipal());
74+
75+
const accessEl = new SpanEl('access-control-changed-item-access');
76+
accessEl.setHtml(this.getAccessValue());
77+
78+
const principalAndStatusEl = new DivEl('access-control-changed-item-principal-with-status');
79+
principalAndStatusEl.appendChildren(principalViewer, accessEl);
80+
this.appendChild(principalAndStatusEl);
81+
82+
if (!this.isEntryRemoved()) {
83+
const permissionsEl = new DivEl('access-control-changed-item-permissions');
84+
85+
PermissionsHelper.getAllPermissions().forEach(permission => {
86+
permissionsEl.appendChild(new PermissionStateView(permission, this.getPermissionState(permission)));
87+
});
88+
89+
this.appendChild(permissionsEl);
90+
}
91+
92+
return rendered;
93+
});
94+
}
95+
}
Lines changed: 45 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,79 @@
11
import {ListBox} from '@enonic/lib-admin-ui/ui/selector/list/ListBox';
2-
import {DivEl} from '@enonic/lib-admin-ui/dom/DivEl';
3-
import * as Q from 'q';
4-
import {PrincipalViewer} from '@enonic/lib-admin-ui/ui/security/PrincipalViewer';
5-
import {SpanEl} from '@enonic/lib-admin-ui/dom/SpanEl';
6-
import {Principal} from '@enonic/lib-admin-ui/security/Principal';
7-
import {Permission} from '../../access/Permission';
8-
import {i18n} from '@enonic/lib-admin-ui/util/Messages';
9-
import {AccessControlEntryView} from '../../view/AccessControlEntryView';
10-
import {Access} from '../../security/Access';
11-
import {PermissionsHelper} from '../../access/PermissionsHelper';
12-
13-
export interface AccessControlChangedPermissions {
14-
persisted?: Permission[];
15-
updated?: Permission[];
16-
}
17-
18-
export class AccessControlChangedItem {
2+
import {ApplyPermissionsScope} from './PermissionsData';
3+
import {RoleKeys} from '@enonic/lib-admin-ui/security/RoleKeys';
4+
import {AccessControlEntry} from '../../access/AccessControlEntry';
5+
import {AccessControlChangedItem} from './AccessControlChangedItem';
6+
import {AccessControlChangedItemView} from './AccessControlChangedItemView';
197

20-
private readonly principal: Principal;
8+
export class AccessControlChangedItemsList
9+
extends ListBox<AccessControlChangedItem> {
2110

22-
private readonly permissions: AccessControlChangedPermissions;
11+
private applyTo: ApplyPermissionsScope;
2312

24-
constructor(principal: Principal, permissions: AccessControlChangedPermissions) {
25-
this.principal = principal;
26-
this.permissions = permissions;
27-
}
13+
private resetChildPermissions: boolean;
2814

29-
getPrincipal(): Principal {
30-
return this.principal;
31-
}
15+
private originalValues: AccessControlEntry[] = [];
3216

33-
getPermissions(): AccessControlChangedPermissions {
34-
return this.permissions;
35-
}
36-
}
37-
38-
export class AccessControlChangedItemsList
39-
extends ListBox<AccessControlChangedItem> {
17+
private currentValues: AccessControlEntry[] = [];
4018

4119
constructor() {
4220
super('access-control-changed-items-list');
4321
}
4422

4523
protected createItemView(item: AccessControlChangedItem, readOnly: boolean): AccessControlChangedItemView {
46-
return new AccessControlChangedItemView(item);
24+
return new AccessControlChangedItemView(item, this.applyTo, this.resetChildPermissions);
4725
}
4826

4927
protected getItemId(item: AccessControlChangedItem): string {
5028
return item.getPrincipal().getKey().toString();
5129
}
5230

53-
}
54-
55-
export class AccessControlChangedItemView
56-
extends DivEl {
57-
58-
private readonly item: AccessControlChangedItem;
59-
60-
constructor(item: AccessControlChangedItem) {
61-
super('access-control-changed-item-view');
62-
63-
this.item = item;
31+
setApplyTo(applyTo: ApplyPermissionsScope): void {
32+
this.applyTo = applyTo;
6433
}
6534

66-
private getAccessValue(): string {
67-
if (!this.item.getPermissions().updated || this.item.getPermissions().updated?.length === 0) {
68-
return i18n('dialog.permissions.step.strategy.item.removed');
69-
}
70-
71-
if (!this.item.getPermissions().persisted && this.item.getPermissions().updated) {
72-
return i18n('dialog.permissions.step.strategy.item.added');
73-
}
74-
75-
const access = AccessControlEntryView.getAccessValueFromPermissions(this.item.getPermissions().updated);
76-
return i18n(`security.access.${Access[access].toLowerCase()}`);
35+
setResetChildPermissions(resetChildPermissions: boolean): void {
36+
this.resetChildPermissions = resetChildPermissions;
7737
}
7838

79-
private isRemoved(): boolean {
80-
return !this.item.getPermissions().updated || this.item.getPermissions().updated?.length === 0;
39+
setOriginalValues(originalValues: AccessControlEntry[]): void {
40+
this.originalValues = originalValues;
8141
}
8242

83-
private getPermissionState(permission: Permission): PermissionState {
84-
const isPermInPersisted = this.item.getPermissions().persisted && this.item.getPermissions().persisted?.indexOf(permission) !== -1;
85-
const isPermInUpdated = this.item.getPermissions().updated && this.item.getPermissions().updated?.indexOf(permission) !== -1;
86-
87-
if (!isPermInPersisted) { // permission was not present before
88-
return isPermInUpdated ? PermissionState.ADDED : PermissionState.UNSET;
89-
}
90-
91-
// permission was present before
92-
93-
if (isPermInUpdated) {
94-
return PermissionState.UNCHANGED;
95-
}
96-
97-
return PermissionState.REMOVED;
43+
setCurrentValues(currentValues: AccessControlEntry[]): void {
44+
this.currentValues = currentValues;
45+
this.setItems(this.getChangedItems());
9846
}
9947

100-
doRender(): Q.Promise<boolean> {
101-
return super.doRender().then((rendered: boolean) => {
102-
this.toggleClass('removed', this.isRemoved());
103-
const principalViewer = new PrincipalViewer();
104-
principalViewer.setObject(this.item.getPrincipal());
105-
106-
const accessEl = new SpanEl('access-control-changed-item-access');
107-
accessEl.setHtml(this.getAccessValue());
108-
109-
const principalAndStatusEl = new DivEl('access-control-changed-item-principal-with-status');
110-
principalAndStatusEl.appendChildren(principalViewer, accessEl);
111-
this.appendChild(principalAndStatusEl);
112-
113-
if (!this.isRemoved()) {
114-
const permissionsEl = new DivEl('access-control-changed-item-permissions');
115-
116-
PermissionsHelper.getAllPermissions().forEach(permission => {
117-
permissionsEl.appendChild(new PermissionStateView(permission, this.getPermissionState(permission)));
118-
});
119-
120-
this.appendChild(permissionsEl);
48+
private getChangedItems(): AccessControlChangedItem[] {
49+
const result: AccessControlChangedItem[] = [];
50+
51+
this.originalValues.forEach((originalVal) => {
52+
const found = this.currentValues.find((currentVal) => originalVal.getPrincipalKey().equals(currentVal.getPrincipalKey()));
53+
if (found) { // item was present before and remains
54+
if (!originalVal.equals(found) || this.isOverwriteForChildren()) { // item's permissions were changed or children to be reset
55+
result.push(new AccessControlChangedItem(originalVal.getPrincipal(),
56+
{persisted: originalVal.getAllowedPermissions(), updated: found.getAllowedPermissions()}));
57+
}
58+
} else { // item was removed
59+
if (!RoleKeys.isEveryone(originalVal.getPrincipalKey())) {
60+
result.push(new AccessControlChangedItem(originalVal.getPrincipal(), {persisted: originalVal.getAllowedPermissions()}));
61+
}
12162
}
122-
123-
return rendered;
12463
});
125-
}
126-
127-
}
128-
129-
enum PermissionState {
130-
ADDED = 'added',
131-
REMOVED = 'removed',
132-
UNCHANGED = 'unchanged',
133-
UNSET = 'unset'
134-
}
13564

136-
class PermissionStateView extends SpanEl {
137-
138-
private readonly permission: Permission;
139-
140-
private readonly state: PermissionState;
141-
142-
constructor(permission: Permission, state: PermissionState) {
143-
super('permission-state-view');
144-
145-
this.permission = permission;
146-
this.state = state;
147-
}
65+
// check for newly added items
66+
this.currentValues.forEach((currentValue) => {
67+
const found = this.originalValues.find((originalVal) => originalVal.getPrincipalKey().equals(currentValue.getPrincipalKey()));
68+
if (!found && !RoleKeys.isEveryone(currentValue.getPrincipalKey())) { // item was added
69+
result.push(new AccessControlChangedItem(currentValue.getPrincipal(), {updated: currentValue.getAllowedPermissions()}));
70+
}
71+
});
14872

149-
private permissionToString(permission: Permission): string {
150-
return Permission[permission]?.toLowerCase().replace('_', '') ?? '';
73+
return result;
15174
}
15275

153-
doRender(): Q.Promise<boolean> {
154-
return super.doRender().then((rendered: boolean) => {
155-
this.addClass(this.state);
156-
this.setHtml(i18n(`security.permission.${this.permissionToString(this.permission)}`));
157-
158-
return rendered;
159-
});
76+
private isOverwriteForChildren(): boolean {
77+
return this.applyTo !== 'single' && this.resetChildPermissions;
16078
}
161-
16279
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export enum EditPermissionState {
2+
ADDED = 'added',
3+
REMOVED = 'removed',
4+
UNCHANGED = 'unchanged',
5+
UNSET = 'unset'
6+
}

modules/lib/src/main/resources/assets/js/app/dialog/permissions/EditPermissionsDialog.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,12 @@ export class EditPermissionsDialog
122122

123123
const req = new ApplyContentPermissionsRequest().setId(this.contentId).setScope(data.applyTo);
124124

125-
if (data.strategy === 'merge') {
125+
if (data.reset) {
126+
req.setPermissions(permissions);
127+
} else {
126128
const {added, removed} = AccessControlHelper.calcMergePermissions(this.originalValues, data.permissions);
127129
req.setAddPermissions(added);
128130
req.setRemovePermissions(removed);
129-
} else {
130-
req.setPermissions(permissions);
131131
}
132132

133133
req.sendAndParse().then((taskId) => {
@@ -141,7 +141,7 @@ export class EditPermissionsDialog
141141
return {
142142
permissions: this.mainStep.getData(),
143143
applyTo: strategyData.applyTo,
144-
strategy: strategyData.strategy,
144+
reset: strategyData.reset,
145145
}
146146
}
147147

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {SpanEl} from '@enonic/lib-admin-ui/dom/SpanEl';
2+
import {Permission} from '../../access/Permission';
3+
import * as Q from 'q';
4+
import {i18n} from '@enonic/lib-admin-ui/util/Messages';
5+
import {EditPermissionState} from './EditPermissionState';
6+
7+
export class PermissionStateView extends SpanEl {
8+
9+
private readonly permission: Permission;
10+
11+
private readonly state: EditPermissionState;
12+
13+
constructor(permission: Permission, state: EditPermissionState) {
14+
super('permission-state-view');
15+
16+
this.permission = permission;
17+
this.state = state;
18+
}
19+
20+
private permissionToString(permission: Permission): string {
21+
return Permission[permission]?.toLowerCase().replace('_', '') ?? '';
22+
}
23+
24+
doRender(): Q.Promise<boolean> {
25+
return super.doRender().then((rendered: boolean) => {
26+
this.addClass(this.state);
27+
this.setHtml(i18n(`security.permission.${this.permissionToString(this.permission)}`));
28+
29+
return rendered;
30+
});
31+
}
32+
33+
}

0 commit comments

Comments
 (0)