Skip to content

Commit cc21ead

Browse files
Merge pull request #4734 from kobotoolbox/hardcode-permissions-labels
Hardcode permissions labels
2 parents a31ca29 + ed18734 commit cc21ead

File tree

5 files changed

+151
-103
lines changed

5 files changed

+151
-103
lines changed

jsapp/js/components/permissions/permConstants.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,33 @@ export const PARTIAL_IMPLIED_CHECKBOX_PAIRS = {
166166
[CHECKBOX_NAMES.submissionsEditPartialByUsers]: CHECKBOX_NAMES.submissionsAdd,
167167
};
168168
Object.freeze(PARTIAL_IMPLIED_CHECKBOX_PAIRS);
169+
170+
/**
171+
* Most of these labels are also available from `api/v2/assets/<uid>/` endpoint
172+
* in the `assignable_permissions` property. Unfortunately due to how the data
173+
* is architectured, the labels for partial permissions are not going to be
174+
* available for multiple types.
175+
*/
176+
export const CHECKBOX_LABELS: {[key in CheckboxNameAll]: string} = {
177+
formView: t('View form'),
178+
formEdit: t('Edit form'),
179+
formManage: t('Manage project'),
180+
submissionsAdd: t('Add submissions'),
181+
submissionsView: t('View submissions'),
182+
submissionsViewPartialByUsers: t('View submissions only from specific users'),
183+
submissionsEdit: t('Edit submissions'),
184+
submissionsEditPartialByUsers: t('Edit submissions only from specific users'),
185+
submissionsValidate: t('Validate submissions'),
186+
submissionsValidatePartialByUsers: t(
187+
'Validate submissions only from specific users'
188+
),
189+
submissionsDelete: t('Delete submissions'),
190+
submissionsDeletePartialByUsers: t(
191+
'Delete submissions only from specific users'
192+
),
193+
};
194+
Object.freeze(CHECKBOX_LABELS);
195+
196+
export const PARTIAL_BY_USERS_DEFAULT_LABEL = t(
197+
'Act on submissions only from specific users'
198+
);

jsapp/js/components/permissions/userAssetPermsEditor.component.tsx

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
CHECKBOX_NAMES,
1818
CHECKBOX_PERM_PAIRS,
1919
PARTIAL_IMPLIED_CHECKBOX_PAIRS,
20+
CHECKBOX_LABELS,
2021
} from './permConstants';
2122
import type {
2223
CheckboxNameAll,
@@ -25,10 +26,7 @@ import type {
2526
PermissionCodename,
2627
} from './permConstants';
2728
import type {AssignablePermsMap} from './sharingForm.component';
28-
import type {
29-
PermissionBase,
30-
AssignablePermissionPartialLabel,
31-
} from 'js/dataInterface';
29+
import type {PermissionBase} from 'js/dataInterface';
3230
import userExistence from 'js/users/userExistence.store';
3331
import {getPartialByUsersListName} from './utils';
3432

@@ -400,41 +398,6 @@ export default class UserAssetPermsEditor extends React.Component<
400398
return found;
401399
}
402400

403-
getCheckboxLabel(checkboxName: CheckboxNameAll) {
404-
// We need both of these pieces of data, and most probably both of them
405-
// should be available. But because of types we need to be extra safe. If
406-
// anything goes awry, we will return checkbox name as fallback.
407-
const permDef = permConfig.getPermissionByCodename(
408-
CHECKBOX_PERM_PAIRS[checkboxName]
409-
);
410-
if (!permDef) {
411-
return checkboxName;
412-
}
413-
const assignablePerm = this.props.assignablePerms.get(permDef.url);
414-
if (!assignablePerm) {
415-
return checkboxName;
416-
}
417-
418-
// For partial permission we need to dig deeper
419-
if (checkboxName in PARTIAL_PERM_PAIRS) {
420-
// We need to get regular (non partial) permission name that matches
421-
// the partial permission. This is because each partial permissions is
422-
// being stored as `partial_submissions` first, and the actual respective
423-
// submission second.
424-
const permName = PARTIAL_PERM_PAIRS[checkboxName as CheckboxNamePartialByUsers];
425-
if (typeof assignablePerm !== 'string' && permName in assignablePerm) {
426-
return (
427-
assignablePerm[permName as keyof AssignablePermissionPartialLabel] ||
428-
checkboxName
429-
);
430-
}
431-
return checkboxName;
432-
} else {
433-
// We cast it as string, because it is definitely not partial checkbox
434-
return assignablePerm as string;
435-
}
436-
}
437-
438401
isAssignable(permCodename: PermissionCodename) {
439402
const permDef = permConfig.getPermissionByCodename(permCodename);
440403
if (!permDef) {
@@ -551,7 +514,7 @@ export default class UserAssetPermsEditor extends React.Component<
551514
checked={this.state[checkboxName]}
552515
disabled={isDisabled}
553516
onChange={this.onCheckboxChange.bind(this, checkboxName)}
554-
label={this.getCheckboxLabel(checkboxName)}
517+
label={CHECKBOX_LABELS[checkboxName]}
555518
/>
556519
);
557520
}

jsapp/js/components/permissions/userPermissionRow.component.tsx

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import permConfig from './permConfig';
99
import type {UserPerm} from './permParser';
1010
import type {PermissionBase} from 'js/dataInterface';
1111
import type {AssignablePermsMap} from './sharingForm.component';
12+
import {getPermLabel, getFriendlyPermName} from './utils';
1213

1314
interface UserPermissionRowProps {
1415
assetUid: string;
@@ -108,72 +109,17 @@ export default class UserPermissionRow extends React.Component<
108109
this.setState({isEditFormVisible: !this.state.isEditFormVisible});
109110
}
110111

111-
// TODO: This doesn't display `partial_permissions` in a nice way, as it
112-
// assumes that there can be only "view" in them, but this is partially
113-
// backend's fault for giving a non universal label to "partial_permissions".
114-
// See: https://github.com/kobotoolbox/kpi/issues/4641
112+
/**
113+
* Note that this renders partial permission using a general label with a list
114+
* of related conditions.
115+
*/
115116
renderPermissions(permissions: UserPerm[]) {
116-
const maxParentheticalUsernames = 3;
117117
return (
118118
<bem.UserRow__perms>
119119
{permissions.map((perm) => {
120-
let permUsers: string[] = [];
121-
122-
if (perm.partial_permissions) {
123-
perm.partial_permissions.forEach((partial) => {
124-
partial.filters.forEach((filter) => {
125-
if (filter._submitted_by) {
126-
permUsers = permUsers.concat(filter._submitted_by.$in);
127-
}
128-
});
129-
});
130-
}
131-
132-
// Keep only unique values
133-
permUsers = [...new Set(permUsers)];
134-
135-
// We fallback to "???" so it's clear when some error happens
136-
let permLabel: string = '???';
137-
if (this.props.assignablePerms.has(perm.permission)) {
138-
const assignablePerm = this.props.assignablePerms.get(
139-
perm.permission
140-
);
141-
if (typeof assignablePerm === 'object') {
142-
// let's assume back end always returns a `default` property with
143-
// nested permissions
144-
permLabel = assignablePerm.default;
145-
} else if (assignablePerm) {
146-
permLabel = assignablePerm;
147-
}
148-
}
149-
150-
// Hopefully this is friendly to translators of RTL languages
151-
let permNameTemplate;
152-
if (permUsers.length === 0) {
153-
permNameTemplate = '##permission_label##';
154-
} else if (permUsers.length <= maxParentheticalUsernames) {
155-
permNameTemplate = t('##permission_label## (##username_list##)');
156-
} else if (permUsers.length === maxParentheticalUsernames + 1) {
157-
permNameTemplate = t(
158-
'##permission_label## (##username_list## and 1 other)'
159-
);
160-
} else {
161-
permNameTemplate = t(
162-
'##permission_label## (##username_list## and ' +
163-
'##hidden_username_count## others)'
164-
);
165-
}
166-
167-
const friendlyPermName = permNameTemplate
168-
.replace('##permission_label##', permLabel)
169-
.replace(
170-
'##username_list##',
171-
permUsers.slice(0, maxParentheticalUsernames).join(', ')
172-
)
173-
.replace(
174-
'##hidden_username_count##',
175-
String(permUsers.length - maxParentheticalUsernames)
176-
);
120+
const permLabel = getPermLabel(perm);
121+
122+
const friendlyPermName = getFriendlyPermName(perm);
177123

178124
return (
179125
<bem.UserRow__perm key={permLabel}>

jsapp/js/components/permissions/utils.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import type {
1414
CheckboxNamePartialByUsers,
1515
PartialByUsersListName,
1616
} from './permConstants';
17-
import {CHECKBOX_PERM_PAIRS} from './permConstants';
17+
import {
18+
CHECKBOX_PERM_PAIRS,
19+
CHECKBOX_LABELS,
20+
PARTIAL_BY_USERS_DEFAULT_LABEL,
21+
} from './permConstants';
22+
import type {UserPerm} from './permParser';
1823

1924
/** For `.find`-ing the permissions */
2025
function _doesPermMatch(
@@ -250,3 +255,100 @@ export function getCheckboxNameByPermission(
250255
}
251256
return found;
252257
}
258+
259+
/** Detects if partial permissions is of "by users" kind */
260+
export function isPartialByUsers(perm: UserPerm) {
261+
// TODO for now this only checks if this is partial permission, as there is
262+
// only one type (more to come). In future this would need some smart way
263+
// to recognize what Django filter is being used in the `partial_permissions`
264+
// object.
265+
return (
266+
'partial_permissions' in perm && perm.partial_permissions !== undefined
267+
);
268+
}
269+
270+
/**
271+
* Returns a human readable permission label, has to do some juggling for
272+
* partial permissions. Fallback is permission codename.
273+
*/
274+
export function getPermLabel(perm: UserPerm) {
275+
// For partial permissions we return a general label that matches all possible
276+
// partial permissions (i.e. same label for "View submissions only from
277+
// specific users" and "Edit submissions only from specific users" etc.)
278+
if (isPartialByUsers(perm)) {
279+
return PARTIAL_BY_USERS_DEFAULT_LABEL;
280+
}
281+
282+
// Get permission definition
283+
const permDef = permConfig.getPermission(perm.permission);
284+
285+
if (permDef) {
286+
const checkboxName = getCheckboxNameByPermission(permDef.codename);
287+
288+
if (checkboxName) {
289+
return CHECKBOX_LABELS[checkboxName];
290+
}
291+
}
292+
293+
// If we couldn't get the definition, we will display "???", so it's clear
294+
// something is terribly wrong. But this case is ~impossible to get, and we
295+
// mostly have it for TS reasons.
296+
return '???';
297+
}
298+
299+
/**
300+
* Displays a user friendly name of given permission. For partial permissions it
301+
* will include the list of users (limited by `maxParentheticalUsernames`) in
302+
* the name.
303+
*/
304+
export function getFriendlyPermName(
305+
perm: UserPerm,
306+
maxParentheticalUsernames = 3
307+
) {
308+
const permLabel = getPermLabel(perm);
309+
310+
let permUsers: string[] = [];
311+
312+
if (perm.partial_permissions) {
313+
perm.partial_permissions.forEach((partial) => {
314+
partial.filters.forEach((filter) => {
315+
if (filter._submitted_by) {
316+
permUsers = permUsers.concat(filter._submitted_by.$in);
317+
}
318+
});
319+
});
320+
}
321+
322+
// Keep only unique values
323+
permUsers = [...new Set(permUsers)];
324+
325+
// Hopefully this is friendly to translators of RTL languages
326+
let permNameTemplate;
327+
if (permUsers.length === 0) {
328+
permNameTemplate = '##permission_label##';
329+
} else if (permUsers.length <= maxParentheticalUsernames) {
330+
permNameTemplate = t('##permission_label## (##username_list##)');
331+
} else if (permUsers.length === maxParentheticalUsernames + 1) {
332+
permNameTemplate = t(
333+
'##permission_label## (##username_list## and 1 other)'
334+
);
335+
} else {
336+
permNameTemplate = t(
337+
'##permission_label## (##username_list## and ' +
338+
'##hidden_username_count## others)'
339+
);
340+
}
341+
342+
const friendlyPermName = permNameTemplate
343+
.replace('##permission_label##', permLabel)
344+
.replace(
345+
'##username_list##',
346+
permUsers.slice(0, maxParentheticalUsernames).join(', ')
347+
)
348+
.replace(
349+
'##hidden_username_count##',
350+
String(permUsers.length - maxParentheticalUsernames)
351+
);
352+
353+
return friendlyPermName;
354+
}

jsapp/js/dataInterface.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ interface AssignablePermissionRegular {
197197
label: string;
198198
}
199199

200+
/**
201+
* A list of labels for partial permissions.
202+
*
203+
* WARNING: it only includes labels for `…PartialByUsers` type ("…only from
204+
* specific users"), so please use `CHECKBOX_LABELS` from `permConstants` file
205+
* instead.
206+
*/
200207
export interface AssignablePermissionPartialLabel {
201208
default: string;
202209
view_submissions: string;

0 commit comments

Comments
 (0)