@onehat/ui 0.3.334 → 0.3.335
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Components/Editor/Editor.js +2 -2
- package/src/Components/Form/Form.js +7 -1
- package/src/Components/Grid/Grid.js +62 -40
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +56 -1
- package/src/Components/Hoc/withEditor.js +77 -1
- package/src/Components/Hoc/withPdfButtons.js +11 -2
- package/src/Components/Hoc/withPermissions.js +34 -11
- package/src/Components/Hoc/withPresetButtons.js +6 -6
- package/src/Components/Messages/Unauthorized.js +17 -0
- package/src/Components/Tree/Tree.js +18 -0
- package/src/Components/Viewer/Viewer.js +17 -9
- package/src/Functions/chunkArray.js +7 -0
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ function Editor(props) {
|
|
|
15
15
|
onEditorDelete: onDelete,
|
|
16
16
|
editorMode,
|
|
17
17
|
onEditMode,
|
|
18
|
-
|
|
18
|
+
canRecordBeEdited,
|
|
19
19
|
_viewer = {},
|
|
20
20
|
_form = {
|
|
21
21
|
containerProps: {}
|
|
@@ -36,7 +36,7 @@ function Editor(props) {
|
|
|
36
36
|
const propsToPass = _.omit(props, ['self', 'reference', 'parent']);
|
|
37
37
|
|
|
38
38
|
let canEdit = true;
|
|
39
|
-
if (
|
|
39
|
+
if (canRecordBeEdited && !canRecordBeEdited(selection)) {
|
|
40
40
|
canEdit = false;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
ScrollView,
|
|
8
8
|
Text,
|
|
9
9
|
} from 'native-base';
|
|
10
|
+
import {
|
|
11
|
+
VIEW,
|
|
12
|
+
} from '../../Constants/Commands.js';
|
|
10
13
|
import {
|
|
11
14
|
EDITOR_TYPE__INLINE,
|
|
12
15
|
EDITOR_TYPE__WINDOWED,
|
|
@@ -106,6 +109,9 @@ function Form(props) {
|
|
|
106
109
|
|
|
107
110
|
// withData
|
|
108
111
|
Repository,
|
|
112
|
+
|
|
113
|
+
// withPermissions
|
|
114
|
+
canUser,
|
|
109
115
|
|
|
110
116
|
// withEditor
|
|
111
117
|
isEditorViewOnly = false,
|
|
@@ -922,7 +928,7 @@ function Form(props) {
|
|
|
922
928
|
>Back</Button>}
|
|
923
929
|
<Text fontSize={20} ml={2} color="trueGray.500">Edit Mode</Text>
|
|
924
930
|
</Row>
|
|
925
|
-
{onViewMode && !disableView &&
|
|
931
|
+
{onViewMode && !disableView && (!canUser || canUser(VIEW)) &&
|
|
926
932
|
<Button
|
|
927
933
|
{...testProps('toViewBtn')}
|
|
928
934
|
key="viewBtn"
|
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
SELECTION_MODE_SINGLE,
|
|
15
15
|
SELECTION_MODE_MULTI,
|
|
16
16
|
} from '../../Constants/Selection.js';
|
|
17
|
+
import {
|
|
18
|
+
EDIT,
|
|
19
|
+
VIEW,
|
|
20
|
+
} from '../../Constants/Commands.js';
|
|
17
21
|
import {
|
|
18
22
|
DROP_POSITION_BEFORE,
|
|
19
23
|
DROP_POSITION_AFTER,
|
|
@@ -59,6 +63,7 @@ import Toolbar from '../Toolbar/Toolbar.js';
|
|
|
59
63
|
import NoReorderRows from '../Icons/NoReorderRows.js';
|
|
60
64
|
import ReorderRows from '../Icons/ReorderRows.js';
|
|
61
65
|
import ColumnSelectorWindow from './ColumnSelectorWindow.js';
|
|
66
|
+
import Unauthorized from '../Messages/Unauthorized.js';
|
|
62
67
|
import _ from 'lodash';
|
|
63
68
|
|
|
64
69
|
|
|
@@ -129,7 +134,7 @@ function GridComponent(props) {
|
|
|
129
134
|
h,
|
|
130
135
|
flex,
|
|
131
136
|
bg = '#fff',
|
|
132
|
-
|
|
137
|
+
canRecordBeEdited,
|
|
133
138
|
alternateRowBackgrounds = true,
|
|
134
139
|
alternatingInterval = 2,
|
|
135
140
|
defaultRowHeight = 48,
|
|
@@ -154,6 +159,9 @@ function GridComponent(props) {
|
|
|
154
159
|
idIx,
|
|
155
160
|
displayIx,
|
|
156
161
|
|
|
162
|
+
// withPermissions
|
|
163
|
+
canUser,
|
|
164
|
+
|
|
157
165
|
// withDnd
|
|
158
166
|
isDropTarget,
|
|
159
167
|
canDrop,
|
|
@@ -372,11 +380,17 @@ function GridComponent(props) {
|
|
|
372
380
|
|
|
373
381
|
if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
|
|
374
382
|
if (onView) {
|
|
383
|
+
if (canUser && !canUser(VIEW)) { // permissions
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
375
386
|
onView(true);
|
|
376
387
|
}
|
|
377
388
|
} else {
|
|
378
389
|
if (onEdit) {
|
|
379
|
-
if (
|
|
390
|
+
if (canUser && !canUser(EDIT)) { // permissions
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (canRecordBeEdited && !canRecordBeEdited(selection)) { // record can be edited
|
|
380
394
|
return;
|
|
381
395
|
}
|
|
382
396
|
onEdit();
|
|
@@ -468,49 +482,53 @@ function GridComponent(props) {
|
|
|
468
482
|
dragSelectionRef.current = selection;
|
|
469
483
|
const getSelection = () => dragSelectionRef.current;
|
|
470
484
|
|
|
471
|
-
|
|
472
|
-
if (
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
rowReorderProps.dragSourceType = 'row';
|
|
476
|
-
const dragIx = showHeaders ? index - 1 : index;
|
|
477
|
-
rowReorderProps.dragSourceItem = {
|
|
478
|
-
id: item.id,
|
|
479
|
-
getSelection,
|
|
480
|
-
onDrag: (dragState) => {
|
|
481
|
-
onRowReorderDrag(dragState, dragIx);
|
|
482
|
-
},
|
|
483
|
-
};
|
|
484
|
-
rowReorderProps.onDragEnd = onRowReorderEnd;
|
|
485
|
-
} else {
|
|
486
|
-
// Don't allow drag/drop from withDnd while reordering
|
|
487
|
-
if (areRowsDragSource) {
|
|
485
|
+
const userHasPermissionToDrag = (!canUser || canUser(EDIT));
|
|
486
|
+
if (userHasPermissionToDrag) {
|
|
487
|
+
// assign event handlers
|
|
488
|
+
if (canRowsReorder && isReorderMode) {
|
|
488
489
|
WhichRow = DragSourceGridRow;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
490
|
+
rowReorderProps.isDragSource = true;
|
|
491
|
+
rowReorderProps.dragSourceType = 'row';
|
|
492
|
+
const dragIx = showHeaders ? index - 1 : index;
|
|
493
|
+
rowReorderProps.dragSourceItem = {
|
|
494
|
+
id: item.id,
|
|
495
|
+
getSelection,
|
|
496
|
+
onDrag: (dragState) => {
|
|
497
|
+
onRowReorderDrag(dragState, dragIx);
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
rowReorderProps.onDragEnd = onRowReorderEnd;
|
|
501
|
+
} else {
|
|
502
|
+
// Don't allow drag/drop from withDnd while reordering
|
|
503
|
+
if (areRowsDragSource) {
|
|
504
|
+
WhichRow = DragSourceGridRow;
|
|
505
|
+
rowDragProps.isDragSource = true;
|
|
506
|
+
rowDragProps.dragSourceType = rowDragSourceType;
|
|
507
|
+
if (getRowDragSourceItem) {
|
|
508
|
+
rowDragProps.dragSourceItem = getRowDragSourceItem(item, getSelection, rowDragSourceType);
|
|
509
|
+
} else {
|
|
510
|
+
rowDragProps.dragSourceItem = {
|
|
511
|
+
id: item.id,
|
|
512
|
+
getSelection,
|
|
513
|
+
type: rowDragSourceType,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (areRowsDropTarget) {
|
|
518
|
+
WhichRow = DropTargetGridRow;
|
|
519
|
+
rowDragProps.isDropTarget = true;
|
|
520
|
+
rowDragProps.dropTargetAccept = dropTargetAccept;
|
|
521
|
+
rowDragProps.onDrop = (droppedItem) => {
|
|
522
|
+
// NOTE: item is sometimes getting destroyed, but it still as the id, so you can still use it
|
|
523
|
+
onRowDrop(item, droppedItem); // item is what it was dropped on; droppedItem is the dragSourceItem defined above
|
|
498
524
|
};
|
|
499
525
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
rowDragProps.isDropTarget = true;
|
|
504
|
-
rowDragProps.dropTargetAccept = dropTargetAccept;
|
|
505
|
-
rowDragProps.onDrop = (droppedItem) => {
|
|
506
|
-
// NOTE: item is sometimes getting destroyed, but it still as the id, so you can still use it
|
|
507
|
-
onRowDrop(item, droppedItem); // item is what it was dropped on; droppedItem is the dragSourceItem defined above
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
if (areRowsDragSource && areRowsDropTarget) {
|
|
511
|
-
WhichRow = DragSourceDropTargetGridRow;
|
|
526
|
+
if (areRowsDragSource && areRowsDropTarget) {
|
|
527
|
+
WhichRow = DragSourceDropTargetGridRow;
|
|
528
|
+
}
|
|
512
529
|
}
|
|
513
530
|
}
|
|
531
|
+
|
|
514
532
|
}
|
|
515
533
|
return <WhichRow
|
|
516
534
|
columnsConfig={localColumnsConfig}
|
|
@@ -942,6 +960,10 @@ function GridComponent(props) {
|
|
|
942
960
|
|
|
943
961
|
}, [selectorSelected]);
|
|
944
962
|
|
|
963
|
+
if (canUser && !canUser('view')) {
|
|
964
|
+
return <Unauthorized />;
|
|
965
|
+
}
|
|
966
|
+
|
|
945
967
|
if (self) {
|
|
946
968
|
self.ref = containerRef;
|
|
947
969
|
self.gridRef = gridRef;
|
|
@@ -57,6 +57,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
57
57
|
// withSecondaryData
|
|
58
58
|
SecondaryRepository,
|
|
59
59
|
|
|
60
|
+
// withPermissions
|
|
61
|
+
canUser,
|
|
62
|
+
showPermissionsError,
|
|
63
|
+
|
|
60
64
|
// withSecondarySelection
|
|
61
65
|
secondarySelection,
|
|
62
66
|
secondarySetSelection,
|
|
@@ -69,6 +73,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
69
73
|
secondaryListeners = useRef({}),
|
|
70
74
|
secondaryEditorStateRef = useRef(),
|
|
71
75
|
secondaryNewEntityDisplayValueRef = useRef(),
|
|
76
|
+
secondaryModel = SecondaryRepository?.schema?.name,
|
|
72
77
|
[secondaryCurrentRecord, secondarySetCurrentRecord] = useState(null),
|
|
73
78
|
[secondaryIsAdding, setIsAdding] = useState(false),
|
|
74
79
|
[secondaryIsSaving, setIsSaving] = useState(false),
|
|
@@ -109,6 +114,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
109
114
|
return secondaryNewEntityDisplayValueRef.current;
|
|
110
115
|
},
|
|
111
116
|
secondaryDoAdd = async (e, values) => {
|
|
117
|
+
if (canUser && !canUser(ADD, secondaryModel)) {
|
|
118
|
+
showPermissionsError(ADD, secondaryModel);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
112
122
|
let addValues = values;
|
|
113
123
|
|
|
114
124
|
if (SecondaryRepository?.isLoading) {
|
|
@@ -197,6 +207,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
197
207
|
secondarySetIsEditorShown(true);
|
|
198
208
|
},
|
|
199
209
|
secondaryDoEdit = async () => {
|
|
210
|
+
if (canUser && !canUser(EDIT, secondaryModel)) {
|
|
211
|
+
showPermissionsError(EDIT, secondaryModel);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
200
214
|
if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
|
|
201
215
|
return;
|
|
202
216
|
}
|
|
@@ -211,6 +225,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
211
225
|
secondarySetIsEditorShown(true);
|
|
212
226
|
},
|
|
213
227
|
secondaryDoDelete = async (args) => {
|
|
228
|
+
if (canUser && !canUser(DELETE, secondaryModel)) {
|
|
229
|
+
showPermissionsError(DELETE, secondaryModel);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
214
232
|
let cb = null;
|
|
215
233
|
if (_.isFunction(args)) {
|
|
216
234
|
cb = args;
|
|
@@ -263,6 +281,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
263
281
|
deleteRecord(false, cb);
|
|
264
282
|
},
|
|
265
283
|
deleteRecord = async (moveSubtreeUp, cb) => {
|
|
284
|
+
if (canUser && !canUser(DELETE, secondaryModel)) {
|
|
285
|
+
showPermissionsError(DELETE, secondaryModel);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
266
288
|
if (getListeners().onBeforeDelete) {
|
|
267
289
|
const listenerResult = await getListeners().onBeforeDelete(secondarySelection);
|
|
268
290
|
if (listenerResult === false) {
|
|
@@ -292,6 +314,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
292
314
|
if (!secondaryUserCanView) {
|
|
293
315
|
return;
|
|
294
316
|
}
|
|
317
|
+
if (canUser && !canUser(VIEW, secondaryModel)) {
|
|
318
|
+
showPermissionsError(VIEW, secondaryModel);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
295
321
|
if (secondarySelection.length !== 1) {
|
|
296
322
|
return;
|
|
297
323
|
}
|
|
@@ -307,6 +333,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
307
333
|
if (!secondaryUserCanEdit || secondaryDisableDuplicate) {
|
|
308
334
|
return;
|
|
309
335
|
}
|
|
336
|
+
if (canUser && !canUser(DUPLICATE, secondaryModel)) {
|
|
337
|
+
showPermissionsError(DUPLICATE, secondaryModel);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
310
340
|
if (secondarySelection.length !== 1) {
|
|
311
341
|
return;
|
|
312
342
|
}
|
|
@@ -335,6 +365,12 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
335
365
|
secondaryDoEdit();
|
|
336
366
|
},
|
|
337
367
|
secondaryDoEditorSave = async (data, e) => {
|
|
368
|
+
let mode = editorMode === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
369
|
+
if (canUser && !canUser(mode, secondaryModel)) {
|
|
370
|
+
showPermissionsError(mode, secondaryModel);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
338
374
|
// NOTE: The Form submits onSave for both adds (when not isAutoSsave) and edits.
|
|
339
375
|
const isSingle = secondarySelection.length === 1;
|
|
340
376
|
let useStaged = false;
|
|
@@ -391,7 +427,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
391
427
|
await getListeners().onAfterAdd(secondarySelection);
|
|
392
428
|
}
|
|
393
429
|
setIsAdding(false);
|
|
394
|
-
|
|
430
|
+
if (!canUser || canUser(EDIT, secondaryModel)) {
|
|
431
|
+
secondarySetEditorMode(EDITOR_MODE__EDIT);
|
|
432
|
+
} else {
|
|
433
|
+
secondarySetEditorMode(EDITOR_MODE__VIEW);
|
|
434
|
+
}
|
|
395
435
|
} else if (secondaryEditorMode === EDITOR_MODE__EDIT) {
|
|
396
436
|
if (getListeners().onAfterEdit) {
|
|
397
437
|
await getListeners().onAfterEdit(secondarySelection);
|
|
@@ -436,6 +476,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
436
476
|
secondarySetIsEditorShown(false);
|
|
437
477
|
},
|
|
438
478
|
secondaryDoEditorDelete = async () => {
|
|
479
|
+
if (canUser && !canUser(DELETE, secondaryModel)) {
|
|
480
|
+
showPermissionsError(DELETE, secondaryModel);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
439
484
|
secondaryDoDelete(() => {
|
|
440
485
|
secondarySetEditorMode(EDITOR_MODE__VIEW);
|
|
441
486
|
secondarySetIsEditorShown(false);
|
|
@@ -470,9 +515,19 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
470
515
|
return mode;
|
|
471
516
|
},
|
|
472
517
|
secondarySetEditMode = () => {
|
|
518
|
+
if (canUser && !canUser(EDIT, secondaryModel)) {
|
|
519
|
+
showPermissionsError(EDIT, secondaryModel);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
473
523
|
secondarySetEditorMode(EDITOR_MODE__EDIT);
|
|
474
524
|
},
|
|
475
525
|
secondarySetViewMode = () => {
|
|
526
|
+
if (canUser && !canUser(VIEW, secondaryModel)) {
|
|
527
|
+
showPermissionsError(VIEW, secondaryModel);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
476
531
|
function doIt() {
|
|
477
532
|
secondarySetEditorMode(EDITOR_MODE__VIEW);
|
|
478
533
|
}
|
|
@@ -2,11 +2,22 @@ import { useEffect, useState, useRef, } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
} from 'native-base';
|
|
5
|
+
import {
|
|
6
|
+
ADD,
|
|
7
|
+
EDIT,
|
|
8
|
+
DELETE,
|
|
9
|
+
VIEW,
|
|
10
|
+
COPY,
|
|
11
|
+
DUPLICATE,
|
|
12
|
+
PRINT,
|
|
13
|
+
UPLOAD_DOWNLOAD,
|
|
14
|
+
} from '../../Constants/Commands.js';
|
|
5
15
|
import {
|
|
6
16
|
EDITOR_MODE__VIEW,
|
|
7
17
|
EDITOR_MODE__ADD,
|
|
8
18
|
EDITOR_MODE__EDIT,
|
|
9
19
|
EDITOR_TYPE__SIDE,
|
|
20
|
+
EDITOR_TYPE__INLINE,
|
|
10
21
|
} from '../../Constants/Editor.js';
|
|
11
22
|
import UiGlobals from '../../UiGlobals.js';
|
|
12
23
|
import _ from 'lodash';
|
|
@@ -56,6 +67,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
56
67
|
// withData
|
|
57
68
|
Repository,
|
|
58
69
|
|
|
70
|
+
// withPermissions
|
|
71
|
+
canUser,
|
|
72
|
+
showPermissionsError,
|
|
73
|
+
|
|
59
74
|
// withSelection
|
|
60
75
|
selection,
|
|
61
76
|
setSelection,
|
|
@@ -108,6 +123,11 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
108
123
|
return newEntityDisplayValueRef.current;
|
|
109
124
|
},
|
|
110
125
|
doAdd = async (e, values) => {
|
|
126
|
+
if (canUser && !canUser(ADD)) {
|
|
127
|
+
showPermissionsError(ADD);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
111
131
|
let addValues = values;
|
|
112
132
|
|
|
113
133
|
if (Repository?.isLoading) {
|
|
@@ -201,6 +221,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
201
221
|
setIsEditorShown(true);
|
|
202
222
|
},
|
|
203
223
|
doEdit = async () => {
|
|
224
|
+
if (canUser && !canUser(EDIT)) {
|
|
225
|
+
showPermissionsError(EDIT);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
204
228
|
if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
|
|
205
229
|
return;
|
|
206
230
|
}
|
|
@@ -215,6 +239,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
215
239
|
setIsEditorShown(true);
|
|
216
240
|
},
|
|
217
241
|
doDelete = async (args) => {
|
|
242
|
+
if (canUser && !canUser(DELETE)) {
|
|
243
|
+
showPermissionsError(DELETE);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
218
246
|
let cb = null;
|
|
219
247
|
if (_.isFunction(args)) {
|
|
220
248
|
cb = args;
|
|
@@ -267,6 +295,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
267
295
|
deleteRecord(false, cb);
|
|
268
296
|
},
|
|
269
297
|
deleteRecord = async (moveSubtreeUp, cb) => {
|
|
298
|
+
if (canUser && !canUser(DELETE)) {
|
|
299
|
+
showPermissionsError(DELETE);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
270
302
|
if (getListeners().onBeforeDelete) {
|
|
271
303
|
const listenerResult = await getListeners().onBeforeDelete(selection);
|
|
272
304
|
if (listenerResult === false) {
|
|
@@ -296,6 +328,17 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
296
328
|
if (!userCanView) {
|
|
297
329
|
return;
|
|
298
330
|
}
|
|
331
|
+
if (canUser && !canUser(VIEW)) {
|
|
332
|
+
showPermissionsError(VIEW);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (editorType === EDITOR_TYPE__INLINE) {
|
|
336
|
+
alert('Cannot view in inline editor.');
|
|
337
|
+
return; // inline editor doesn't have a view mode
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// check permissions for view
|
|
341
|
+
|
|
299
342
|
if (selection.length !== 1) {
|
|
300
343
|
return;
|
|
301
344
|
}
|
|
@@ -311,6 +354,14 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
311
354
|
if (!userCanEdit || disableDuplicate) {
|
|
312
355
|
return;
|
|
313
356
|
}
|
|
357
|
+
if (canUser && !canUser(DUPLICATE)) {
|
|
358
|
+
showPermissionsError(DUPLICATE);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// check permissions for duplicate
|
|
363
|
+
|
|
364
|
+
|
|
314
365
|
if (selection.length !== 1) {
|
|
315
366
|
return;
|
|
316
367
|
}
|
|
@@ -339,6 +390,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
339
390
|
doEdit();
|
|
340
391
|
},
|
|
341
392
|
doEditorSave = async (data, e) => {
|
|
393
|
+
let mode = editorMode === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
394
|
+
if (canUser && !canUser(mode)) {
|
|
395
|
+
showPermissionsError(mode);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
342
399
|
// NOTE: The Form submits onSave for both adds (when not isAutoSsave) and edits.
|
|
343
400
|
const isSingle = selection.length === 1;
|
|
344
401
|
let useStaged = false;
|
|
@@ -395,7 +452,11 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
395
452
|
await getListeners().onAfterAddSave(selection);
|
|
396
453
|
}
|
|
397
454
|
setIsAdding(false);
|
|
398
|
-
|
|
455
|
+
if (!canUser || canUser(EDIT)) {
|
|
456
|
+
setEditorMode(EDITOR_MODE__EDIT);
|
|
457
|
+
} else {
|
|
458
|
+
setEditorMode(EDITOR_MODE__VIEW);
|
|
459
|
+
}
|
|
399
460
|
} else if (editorMode === EDITOR_MODE__EDIT) {
|
|
400
461
|
if (getListeners().onAfterEdit) {
|
|
401
462
|
await getListeners().onAfterEdit(selection);
|
|
@@ -440,6 +501,11 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
440
501
|
setIsEditorShown(false);
|
|
441
502
|
},
|
|
442
503
|
doEditorDelete = async () => {
|
|
504
|
+
if (canUser && !canUser(DELETE)) {
|
|
505
|
+
showPermissionsError(DELETE);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
443
509
|
doDelete(() => {
|
|
444
510
|
setEditorMode(EDITOR_MODE__VIEW);
|
|
445
511
|
setIsEditorShown(false);
|
|
@@ -484,9 +550,19 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
484
550
|
return mode;
|
|
485
551
|
},
|
|
486
552
|
setEditMode = () => {
|
|
553
|
+
if (canUser && !canUser(EDIT)) {
|
|
554
|
+
showPermissionsError(EDIT);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
487
558
|
setEditorMode(EDITOR_MODE__EDIT);
|
|
488
559
|
},
|
|
489
560
|
setViewMode = () => {
|
|
561
|
+
if (canUser && !canUser(VIEW)) {
|
|
562
|
+
showPermissionsError(VIEW);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
490
566
|
function doIt() {
|
|
491
567
|
setEditorMode(EDITOR_MODE__VIEW);
|
|
492
568
|
}
|
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
Row,
|
|
5
5
|
Text,
|
|
6
6
|
} from 'native-base';
|
|
7
|
+
import {
|
|
8
|
+
VIEW,
|
|
9
|
+
} from '../../Constants/Commands.js';
|
|
7
10
|
import * as yup from 'yup'; // https://github.com/jquense/yup#string
|
|
8
11
|
import Inflector from 'inflector-js';
|
|
9
12
|
import qs from 'qs';
|
|
@@ -19,15 +22,21 @@ import _ from 'lodash';
|
|
|
19
22
|
export default function withPdfButtons(WrappedComponent) {
|
|
20
23
|
return withModal((props) => {
|
|
21
24
|
|
|
25
|
+
let showButtons = true;
|
|
22
26
|
if (!props.showPdfBtns) {
|
|
27
|
+
showButtons = false;
|
|
28
|
+
}
|
|
29
|
+
if (props.canUser && !props.canUser(VIEW)) { // permissions
|
|
30
|
+
showButtons = false;
|
|
31
|
+
}
|
|
32
|
+
if (!showButtons) {
|
|
23
33
|
// bypass everything.
|
|
24
34
|
// If we don't do this, we get an infinite recursion with Form
|
|
25
35
|
// because this HOC wraps Form and uses Form itself.
|
|
26
36
|
return <WrappedComponent {...props} />;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
const
|
|
30
|
-
{
|
|
39
|
+
const {
|
|
31
40
|
additionalEditButtons = [],
|
|
32
41
|
additionalViewButtons = [],
|
|
33
42
|
items = [],
|
|
@@ -14,18 +14,21 @@ import UiGlobals from '../../UiGlobals.js';
|
|
|
14
14
|
import _ from 'lodash';
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
export default function withPermissions(WrappedComponent) {
|
|
17
|
+
export default function withPermissions(WrappedComponent, forceUsePermissions = false) {
|
|
18
18
|
return (props) => {
|
|
19
19
|
|
|
20
|
-
if (!props.usePermissions) {
|
|
20
|
+
if (!props.usePermissions && !forceUsePermissions) {
|
|
21
21
|
return <WrappedComponent {...props} />;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const {
|
|
25
|
+
// withAlert
|
|
26
|
+
alert,
|
|
27
|
+
|
|
25
28
|
// withData
|
|
26
29
|
Repository,
|
|
27
30
|
} = props,
|
|
28
|
-
model = Repository
|
|
31
|
+
model = Repository?.schema?.name,
|
|
29
32
|
checkPermission = (permission) => {
|
|
30
33
|
const
|
|
31
34
|
reduxState = UiGlobals.redux?.getState(),
|
|
@@ -36,6 +39,15 @@ export default function withPermissions(WrappedComponent) {
|
|
|
36
39
|
return inArray(permission, permissions);
|
|
37
40
|
},
|
|
38
41
|
|
|
42
|
+
showPermissionsError = (permission, modelForAlert = null) => {
|
|
43
|
+
if (!modelForAlert) {
|
|
44
|
+
modelForAlert = model; // use default model if none supplied
|
|
45
|
+
}
|
|
46
|
+
modelForAlert = Inflector.humanize(Inflector.underscore(modelForAlert)); // 'PmEvents' -> 'pm events'
|
|
47
|
+
|
|
48
|
+
alert(`You are not authorized to ${permission} ${modelForAlert}.`);
|
|
49
|
+
},
|
|
50
|
+
|
|
39
51
|
/**
|
|
40
52
|
* Check if user has permission to perform an action
|
|
41
53
|
*
|
|
@@ -50,18 +62,28 @@ export default function withPermissions(WrappedComponent) {
|
|
|
50
62
|
*/
|
|
51
63
|
canUser = (permission, modelToCheck = null) => {
|
|
52
64
|
|
|
53
|
-
//
|
|
65
|
+
// deal with special cases that refer to other permissions
|
|
54
66
|
switch(permission) {
|
|
55
|
-
case COPY:
|
|
56
|
-
case DUPLICATE:
|
|
57
|
-
permission = ADD;
|
|
58
|
-
break;
|
|
59
67
|
case PRINT:
|
|
60
68
|
permission = VIEW;
|
|
61
69
|
break;
|
|
62
|
-
case
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
case COPY:
|
|
71
|
+
case DUPLICATE: {
|
|
72
|
+
// user must have ADD _and_ EDIT permissions, so check both
|
|
73
|
+
const
|
|
74
|
+
hasAddPermission = canUser(ADD, modelToCheck),
|
|
75
|
+
hasEditPermission = canUser(EDIT, modelToCheck);
|
|
76
|
+
return hasAddPermission && hasEditPermission;
|
|
77
|
+
}
|
|
78
|
+
case UPLOAD_DOWNLOAD: {
|
|
79
|
+
// user must have VIEW, ADD, EDIT, and DELETE permissions, so check all of them
|
|
80
|
+
const
|
|
81
|
+
hasViewPermission = canUser(VIEW, modelToCheck),
|
|
82
|
+
hasAddPermission = canUser(ADD, modelToCheck),
|
|
83
|
+
hasEditPermission = canUser(EDIT, modelToCheck),
|
|
84
|
+
hasDeletePermission = canUser(DELETE, modelToCheck);
|
|
85
|
+
return hasViewPermission && hasAddPermission && hasEditPermission && hasDeletePermission;
|
|
86
|
+
}
|
|
65
87
|
default:
|
|
66
88
|
// do nothing
|
|
67
89
|
break;
|
|
@@ -82,6 +104,7 @@ export default function withPermissions(WrappedComponent) {
|
|
|
82
104
|
return <WrappedComponent
|
|
83
105
|
{...props}
|
|
84
106
|
canUser={canUser}
|
|
107
|
+
showPermissionsError={showPermissionsError}
|
|
85
108
|
/>;
|
|
86
109
|
};
|
|
87
110
|
}
|
|
@@ -52,9 +52,9 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
52
52
|
additionalToolbarButtons = [],
|
|
53
53
|
useUploadDownload = false,
|
|
54
54
|
onChangeColumnsConfig,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
canRecordBeEdited,
|
|
56
|
+
canRecordBeDeleted,
|
|
57
|
+
canRecordBeDuplicated,
|
|
58
58
|
...propsToPass
|
|
59
59
|
} = props,
|
|
60
60
|
{
|
|
@@ -204,7 +204,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
204
204
|
if (_.isEmpty(selection) || (_.isArray(selection) && selection.length > 1)) {
|
|
205
205
|
isDisabled = true;
|
|
206
206
|
}
|
|
207
|
-
if (
|
|
207
|
+
if (canRecordBeEdited && !canRecordBeEdited(selection)) {
|
|
208
208
|
isDisabled = true;
|
|
209
209
|
}
|
|
210
210
|
break;
|
|
@@ -219,7 +219,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
219
219
|
if (_.isEmpty(selection) || (_.isArray(selection) && selection.length > 1)) {
|
|
220
220
|
isDisabled = true;
|
|
221
221
|
}
|
|
222
|
-
if (
|
|
222
|
+
if (canRecordBeDeleted && !canRecordBeDeleted(selection)) {
|
|
223
223
|
isDisabled = true;
|
|
224
224
|
}
|
|
225
225
|
if (isTree) {
|
|
@@ -267,7 +267,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
267
267
|
if (_.isEmpty(selection) || selection.length > 1) {
|
|
268
268
|
isDisabled = true;
|
|
269
269
|
}
|
|
270
|
-
if (
|
|
270
|
+
if (canRecordBeDuplicated && !canRecordBeDuplicated(selection)) {
|
|
271
271
|
isDisabled = true;
|
|
272
272
|
}
|
|
273
273
|
break;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Column,
|
|
3
|
+
Text,
|
|
4
|
+
} from 'native-base';
|
|
5
|
+
|
|
6
|
+
export default function Unauthorized(props) {
|
|
7
|
+
const
|
|
8
|
+
{
|
|
9
|
+
text = 'Unauthorized.',
|
|
10
|
+
} = props;
|
|
11
|
+
return <Column justifyContent="center" alignItems="center" w="100%" flex={1}>
|
|
12
|
+
<Text
|
|
13
|
+
textAlign="center"
|
|
14
|
+
color="#f00"
|
|
15
|
+
>{text}</Text>
|
|
16
|
+
</Column>;
|
|
17
|
+
}
|
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
SELECTION_MODE_SINGLE,
|
|
14
14
|
SELECTION_MODE_MULTI,
|
|
15
15
|
} from '../../Constants/Selection.js';
|
|
16
|
+
import {
|
|
17
|
+
EDIT,
|
|
18
|
+
} from '../../Constants/Commands.js';
|
|
16
19
|
import {
|
|
17
20
|
VERTICAL,
|
|
18
21
|
} from '../../Constants/Directions.js';
|
|
@@ -56,6 +59,7 @@ import PaginationToolbar from '../Toolbar/PaginationToolbar.js';
|
|
|
56
59
|
import NoRecordsFound from '../Grid/NoRecordsFound.js';
|
|
57
60
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
58
61
|
import Loading from '../Messages/Loading.js';
|
|
62
|
+
import Unauthorized from '../Messages/Unauthorized.js';
|
|
59
63
|
import _ from 'lodash';
|
|
60
64
|
|
|
61
65
|
const DEPTH_INDENT_PX = 25;
|
|
@@ -106,6 +110,7 @@ function TreeComponent(props) {
|
|
|
106
110
|
reload = null, // Whenever this value changes after initial render, the tree will reload from scratch
|
|
107
111
|
parentIdIx,
|
|
108
112
|
initialSelection,
|
|
113
|
+
canRecordBeEdited,
|
|
109
114
|
onTreeLoad,
|
|
110
115
|
|
|
111
116
|
// withComponent
|
|
@@ -135,6 +140,9 @@ function TreeComponent(props) {
|
|
|
135
140
|
idIx,
|
|
136
141
|
displayIx,
|
|
137
142
|
|
|
143
|
+
// withPermissions
|
|
144
|
+
canUser,
|
|
145
|
+
|
|
138
146
|
// withSelection
|
|
139
147
|
selection,
|
|
140
148
|
setSelection,
|
|
@@ -864,6 +872,12 @@ function TreeComponent(props) {
|
|
|
864
872
|
onNodeClick(item, e); // so reselect it
|
|
865
873
|
}
|
|
866
874
|
if (onEdit) {
|
|
875
|
+
if (canUser && !canUser(EDIT)) { // permissions
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
if (canRecordBeEdited && !canRecordBeEdited(selection)) { // record can be edited
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
867
881
|
onEdit();
|
|
868
882
|
}
|
|
869
883
|
break;
|
|
@@ -1177,6 +1191,10 @@ function TreeComponent(props) {
|
|
|
1177
1191
|
}
|
|
1178
1192
|
}, [selectorId, selectorSelected]);
|
|
1179
1193
|
|
|
1194
|
+
if (canUser && !canUser('view')) {
|
|
1195
|
+
return <Unauthorized />;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1180
1198
|
if (setWithEditListeners) {
|
|
1181
1199
|
setWithEditListeners({ // Update withEdit's listeners on every render
|
|
1182
1200
|
onBeforeAdd,
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
Row,
|
|
7
7
|
Text,
|
|
8
8
|
} from 'native-base';
|
|
9
|
+
import {
|
|
10
|
+
EDIT,
|
|
11
|
+
} from '../../Constants/Commands.js';
|
|
9
12
|
import {
|
|
10
13
|
EDITOR_TYPE__SIDE,
|
|
11
14
|
} from '../../Constants/Editor.js';
|
|
@@ -33,7 +36,7 @@ function Viewer(props) {
|
|
|
33
36
|
columnDefaults = {}, // defaults for each Column defined in items (above)
|
|
34
37
|
record,
|
|
35
38
|
additionalViewButtons,
|
|
36
|
-
|
|
39
|
+
canRecordBeEdited,
|
|
37
40
|
|
|
38
41
|
// withComponent
|
|
39
42
|
self,
|
|
@@ -41,6 +44,10 @@ function Viewer(props) {
|
|
|
41
44
|
// withData
|
|
42
45
|
Repository,
|
|
43
46
|
|
|
47
|
+
// withPermissions
|
|
48
|
+
canUser,
|
|
49
|
+
showPermissionsError,
|
|
50
|
+
|
|
44
51
|
// withEditor
|
|
45
52
|
editorType,
|
|
46
53
|
onEditMode,
|
|
@@ -246,7 +253,7 @@ function Viewer(props) {
|
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
let canEdit = true;
|
|
249
|
-
if (
|
|
256
|
+
if (canRecordBeEdited && !canRecordBeEdited([record])) {
|
|
250
257
|
canEdit = false;
|
|
251
258
|
}
|
|
252
259
|
|
|
@@ -259,13 +266,14 @@ function Viewer(props) {
|
|
|
259
266
|
<Row flex={1} alignItems="center">
|
|
260
267
|
<Text fontSize={20} ml={2} color="trueGray.500">View Mode</Text>
|
|
261
268
|
</Row>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
{(!canUser || canUser(EDIT)) &&
|
|
270
|
+
<Button
|
|
271
|
+
{...testProps('toEditBtn')}
|
|
272
|
+
key="editBtn"
|
|
273
|
+
onPress={onEditMode}
|
|
274
|
+
leftIcon={<Icon as={Pencil} color="#fff" size="sm" />}
|
|
275
|
+
color="#fff"
|
|
276
|
+
>To Edit</Button>}
|
|
269
277
|
</Toolbar>}
|
|
270
278
|
{!_.isEmpty(additionalButtons) &&
|
|
271
279
|
<Toolbar justifyContent="flex-end" flexWrap="wrap">
|