Skip to content

Commit e5663b8

Browse files
authored
feat: Enhance Layout Permissions Input - MEED-9311 - Meeds-io/meeds#3424 (#444)
This change will allow roles selection in addition to a group or space when editing a site or page permissions (access or edit).
1 parent c16ee1d commit e5663b8

File tree

7 files changed

+225
-49
lines changed

7 files changed

+225
-49
lines changed

layout-webapp/src/main/resources/locale/portlet/SiteNavigation_en.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ sites.permission.administrators=Administrators
128128
sites.permission.users=Users
129129
sites.permission.guests=Guests
130130
sites.permission.groupMembers=Group Members
131+
sites.permission.in=In
132+
sites.permission.redactors=Redactors
133+
sites.permission.publishers=Publishers
134+
sites.permission.managers=Managers
131135
sites.permission.everyone=Any
132136
sites.saveAsSiteTemplate=Save as template
133137
spaceTemplate.label.restoreDefaultPages=Restore Default Pages
138+
siteNavigation.label.deleteCustomGroup=Delete custom group
Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,64 @@
8989
<exo-identity-suggester
9090
v-if="isCustomPermissions"
9191
ref="targetPermissions"
92-
v-model="specificGroupEntries"
92+
v-model="specificGroup"
9393
:labels="suggesterLabels"
9494
:search-options="{filterType: 'all'}"
9595
name="specificGroupPermissions"
9696
class="mb-n3"
9797
include-spaces
9898
include-groups
99-
all-groups-for-admin
100-
multiple
101-
required />
99+
all-groups-for-admin />
100+
<div v-if="specificGroupEntries?.length" class="mt-4">
101+
<v-list-item
102+
v-for="g in specificGroupEntries"
103+
:key="g.id"
104+
class="pa-1 pb-1"
105+
dense>
106+
<v-list-item-action class="pa-0 ma-0">
107+
<select
108+
v-model="g.role"
109+
aria-label="hidden"
110+
class="ignore-vuetify-classes width-auto pa-0 ma-0"
111+
@change="updateIndex++"
112+
@blur="updateIndex++">
113+
<option
114+
v-for="role in roles"
115+
:key="role.value"
116+
:value="role.value">
117+
{{ role.text }}
118+
</option>
119+
</select>
120+
</v-list-item-action>
121+
<v-list-item-content class="d-flex align-center pa-0">
122+
<v-list-item-title class="d-flex align-center text-truncate">
123+
<div class="px-2">
124+
{{ $t('sites.permission.in') }}
125+
</div>
126+
<template v-if="g.providerId === 'group'">
127+
<v-icon size="28" class="me-2">
128+
fa-users
129+
</v-icon>
130+
<span class="text-truncate">
131+
{{ g.displayName }}
132+
</span>
133+
</template>
134+
<space-avatar
135+
v-else
136+
:space-id="g.spaceId"
137+
class="text-truncate" />
138+
</v-list-item-title>
139+
</v-list-item-content>
140+
<v-list-item-action class="pa-0 my-auto">
141+
<v-btn
142+
:title="$t('siteNavigation.label.deleteCustomGroup')"
143+
icon
144+
@click.stop.prevent="deleteSpecificGroup(g)">
145+
<v-icon color="error" small>fa-trash</v-icon>
146+
</v-btn>
147+
</v-list-item-action>
148+
</v-list-item>
149+
</div>
102150
</div>
103151
</template>
104152
<script>
@@ -124,8 +172,26 @@ export default {
124172
isAnyPermissions: false,
125173
isCustomPermissions: false,
126174
specificGroupEntries: null,
175+
specificGroup: null,
176+
defaultRole: '*',
177+
updateIndex: 1,
127178
}),
128179
computed: {
180+
roles() {
181+
return [{
182+
value: '*',
183+
text: this.$t('sites.permission.everyone'),
184+
}, {
185+
value: 'redactor',
186+
text: this.$t('sites.permission.redactors'),
187+
}, {
188+
value: 'publisher',
189+
text: this.$t('sites.permission.publishers'),
190+
}, {
191+
value: 'manager',
192+
text: this.$t('sites.permission.managers'),
193+
}];
194+
},
129195
isSpecificGroup() {
130196
return !!this.specificGroupEntries?.length;
131197
},
@@ -140,12 +206,14 @@ export default {
140206
if (this.isGuestPermissions) {
141207
permissions.push(`*:${this.externalsPermission}`);
142208
}
143-
if (this.specificGroupEntries?.length) {
144-
const specificGroupEntries = this.specificGroupEntries?.map?.(g => g.groupId)?.filter?.(g => g) || [];
145-
permissions.push(...specificGroupEntries.map(g => `*:${g}`));
146-
}
147-
if (!permissions.length) {
148-
permissions.push(`*:${this.administratorsPermission}`);
209+
if (this.updateIndex > 0) {
210+
if (this.specificGroupEntries?.length) {
211+
const specificGroupEntries = this.specificGroupEntries?.filter?.(g => g.groupId) || [];
212+
permissions.push(...specificGroupEntries.map(g => `${g.role || this.defaultRole}:${g.groupId}`));
213+
}
214+
if (!permissions.length) {
215+
permissions.push(`*:${this.administratorsPermission}`);
216+
}
149217
}
150218
}
151219
return permissions;
@@ -161,6 +229,19 @@ export default {
161229
permissions() {
162230
this.$emit('input', this.permissions);
163231
},
232+
async specificGroup() {
233+
if (this.specificGroup) {
234+
if (!this.specificGroupEntries) {
235+
this.specificGroupEntries = [];
236+
}
237+
this.specificGroupEntries.push({
238+
...this.specificGroup,
239+
role: this.defaultRole,
240+
});
241+
await this.$nextTick();
242+
this.specificGroup = null;
243+
}
244+
},
164245
isAnyPermissions() {
165246
if (this.isAnyPermissions) {
166247
this.isAdministrationPermissions = true;
@@ -186,12 +267,19 @@ export default {
186267
}) || null;
187268
this.isCustomPermissions = !!specificGroupEntries?.length;
188269
if (specificGroupEntries?.length) {
189-
specificGroupEntries.forEach(this.retrieveObject);
270+
specificGroupEntries.forEach(id => this.retrieveObject(id, '*')); // * specifically to not have to migrate data
190271
}
191272
},
192273
methods: {
193-
async retrieveObject(groupId) {
274+
deleteSpecificGroup(group) {
275+
const index = this.specificGroupEntries.findIndex(g => group.id === g.id);
276+
if (index >= 0) {
277+
this.specificGroupEntries.splice(index, 1);
278+
}
279+
},
280+
async retrieveObject(groupId, defaultRole) {
194281
try {
282+
const role = groupId.includes(':') ? groupId.split(':')[0] : defaultRole || this.defaultRole;
195283
groupId = groupId.includes(':') ? groupId.split(':')[1] : groupId;
196284
if (groupId.indexOf('/spaces/') === 0) {
197285
const space = await this.$spaceService.getSpaceByGroupId(groupId);
@@ -203,6 +291,7 @@ export default {
203291
groupId: space.groupId,
204292
providerId: 'space',
205293
displayName: space.displayName,
294+
role,
206295
profile: {
207296
fullName: space.displayName,
208297
originalName: space.shortName,
@@ -220,6 +309,7 @@ export default {
220309
groupId: groupId,
221310
providerId: 'group',
222311
displayName: group.profile?.fullname,
312+
role,
223313
profile: {
224314
fullName: group.profile?.fullname,
225315
originalName: group.profile?.fullname,
Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,64 @@
4646
</template>
4747
</v-radio>
4848
</v-radio-group>
49-
<exo-identity-suggester
50-
v-if="!isAdministrationPermissions"
51-
ref="targetPermissions"
52-
v-model="specificGroupEntry"
53-
:labels="suggesterLabels"
54-
:search-options="{filterType: 'all'}"
55-
name="specificGroupPermission"
56-
class="mb-n3 mt-n3"
57-
include-spaces
58-
include-groups
59-
all-groups-for-admin
60-
required />
49+
<template v-if="!isAdministrationPermissions">
50+
<exo-identity-suggester
51+
v-if="!specificGroupEntry"
52+
ref="targetPermissions"
53+
v-model="specificGroup"
54+
:labels="suggesterLabels"
55+
:search-options="{filterType: 'all'}"
56+
name="specificGroupPermission"
57+
class="mb-n3 mt-n3"
58+
include-spaces
59+
include-groups
60+
all-groups-for-admin />
61+
<v-list-item
62+
v-else
63+
class="pa-1 pb-1"
64+
dense>
65+
<v-list-item-action class="pa-0 ma-0">
66+
<select
67+
v-model="specificGroupEntry.role"
68+
aria-label="hidden"
69+
class="ignore-vuetify-classes width-auto pa-0 ma-0">
70+
<option
71+
v-for="role in roles"
72+
:key="role.value"
73+
:value="role.value">
74+
{{ role.text }}
75+
</option>
76+
</select>
77+
</v-list-item-action>
78+
<v-list-item-content class="d-flex align-center pa-0">
79+
<v-list-item-title class="d-flex align-center text-truncate">
80+
<div class="px-2">
81+
{{ $t('sites.permission.in') }}
82+
</div>
83+
<template v-if="specificGroupEntry.providerId === 'group'">
84+
<v-icon size="28" class="me-2">
85+
fa-users
86+
</v-icon>
87+
<span class="text-truncate">
88+
{{ specificGroupEntry.displayName }}
89+
</span>
90+
</template>
91+
<space-avatar
92+
v-else
93+
:space-id="specificGroupEntry.spaceId"
94+
class="text-truncate" />
95+
</v-list-item-title>
96+
</v-list-item-content>
97+
<v-list-item-action class="pa-0 my-auto">
98+
<v-btn
99+
:title="$t('siteNavigation.label.deleteCustomGroup')"
100+
icon
101+
@click.stop.prevent="deleteSpecificGroup">
102+
<v-icon color="error" small>fa-trash</v-icon>
103+
</v-btn>
104+
</v-list-item-action>
105+
</v-list-item>
106+
</template>
61107
</div>
62108
</template>
63109
<script>
@@ -76,14 +122,31 @@ export default {
76122
administratorsPermission: '/platform/administrators',
77123
isAdministrationPermissions: true,
78124
specificGroupEntry: null,
125+
specificGroup: null,
126+
defaultRole: 'manager',
79127
}),
80128
computed: {
129+
roles() {
130+
return [{
131+
value: '*',
132+
text: this.$t('sites.permission.everyone'),
133+
}, {
134+
value: 'redactor',
135+
text: this.$t('sites.permission.redactors'),
136+
}, {
137+
value: 'publisher',
138+
text: this.$t('sites.permission.publishers'),
139+
}, {
140+
value: 'manager',
141+
text: this.$t('sites.permission.managers'),
142+
}];
143+
},
81144
isSpecificGroup() {
82145
return this.specificGroupEntry;
83146
},
84147
permission() {
85148
if (!this.isAdministrationPermissions && this.specificGroupEntry?.groupId) {
86-
return `*:${this.specificGroupEntry.groupId}`;
149+
return `${this.specificGroupEntry.role || this.defaultRole}:${this.specificGroupEntry.groupId}`;
87150
} else {
88151
return `*:${this.administratorsPermission}`;
89152
}
@@ -99,6 +162,16 @@ export default {
99162
permission() {
100163
this.$emit('input', this.permission);
101164
},
165+
async specificGroup() {
166+
if (this.specificGroup) {
167+
this.specificGroupEntry = {
168+
...this.specificGroup,
169+
role: this.defaultRole,
170+
};
171+
await this.$nextTick();
172+
this.specificGroup = null;
173+
}
174+
},
102175
isAdministrationPermissions() {
103176
if (this.isAdministrationPermissions) {
104177
this.specificGroupEntry = null;
@@ -111,14 +184,18 @@ export default {
111184
if (this.isAdministrationPermissions) {
112185
this.specificGroupEntry = null;
113186
} else if (permission) {
114-
await this.retrieveObject(permission);
187+
await this.retrieveObject(this.value, '*');
115188
} else {
116189
this.isAdministrationPermissions = true;
117190
this.specificGroupEntry = null;
118191
}
119192
},
120193
methods: {
121-
async retrieveObject(groupId) {
194+
deleteSpecificGroup() {
195+
this.specificGroupEntry = null;
196+
},
197+
async retrieveObject(groupId, defaultRole) {
198+
const role = groupId.includes(':') ? groupId.split(':')[0] : defaultRole || this.defaultRole;
122199
groupId = groupId.includes(':') ? groupId.split(':')[1] : groupId;
123200
if (groupId.indexOf('/spaces/') === 0) {
124201
const space = await this.$spaceService.getSpaceByGroupId(groupId);
@@ -130,6 +207,7 @@ export default {
130207
groupId: space.groupId,
131208
providerId: 'space',
132209
displayName: space.displayName,
210+
role,
133211
profile: {
134212
fullName: space.displayName,
135213
originalName: space.shortName,
@@ -147,6 +225,7 @@ export default {
147225
groupId: groupId,
148226
providerId: 'group',
149227
displayName: group.profile?.fullname,
228+
role,
150229
profile: {
151230
fullName: group.profile?.fullname,
152231
originalName: group.profile?.fullname,

layout-webapp/src/main/webapp/vue-app/common-sites/components/manage-permissions/ManagePermissionsDrawer.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@
3232
</template>
3333
<template v-if="drawer" #content>
3434
<v-card class="mx-4 my-4 px-2 py-2 elevation-0">
35-
<site-edit-permission
35+
<manage-permissions-edit
3636
v-model="editPermission"
3737
:is-site="isSite" />
38-
<site-access-permissions
38+
<manage-permissions-access
3939
v-model="accessPermissions"
4040
class="mt-4"
4141
:is-site="isSite" />

layout-webapp/src/main/webapp/vue-app/common-sites/components/site-navigation/SiteNavigationDrawer.vue

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,21 @@
7272
class="btn btn-primary ms-2">
7373
{{ $t('siteNavigation.label.btn.createNode') }}
7474
</v-btn>
75-
<v-spacer v-if="$refs.drawer?.expand" />
76-
<select
77-
id="siteNavigationDrawerFilterSelect"
78-
v-model="filter"
79-
v-if="$refs.drawer?.expand"
80-
class="ignore-vuetify-classes width-auto pa-0 me-5 mb-0">
81-
<option
82-
v-for="item in navigationsFilter"
83-
:key="item.value"
84-
:value="item.value">
85-
{{ item.text }}
86-
</option>
87-
</select>
75+
<template v-if="$refs.drawer?.expand">
76+
<v-spacer />
77+
<select
78+
id="siteNavigationDrawerFilterSelect"
79+
v-model="filter"
80+
aria-label="hidden"
81+
class="ignore-vuetify-classes width-auto pa-0 me-5 mb-0">
82+
<option
83+
v-for="item in navigationsFilter"
84+
:key="item.value"
85+
:value="item.value">
86+
{{ item.text }}
87+
</option>
88+
</select>
89+
</template>
8890
</v-toolbar>
8991
<site-navigation-nodes-list
9092
:navigation-nodes="navigationNodesToDisplay"

0 commit comments

Comments
 (0)