@memberjunction/ng-entity-permissions 2.42.1 → 2.44.0
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/README.md +402 -91
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -2,15 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
The `@memberjunction/ng-entity-permissions` package provides Angular components for displaying and editing permissions for MemberJunction entities. It allows administrators to manage role-based access control (RBAC) for entities in a user-friendly grid interface.
|
|
4
4
|
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package is part of the MemberJunction platform and provides essential UI components for managing entity-level permissions. It offers two primary components:
|
|
8
|
+
|
|
9
|
+
1. **EntityPermissionsGridComponent** - A grid that displays and allows editing of permissions for either a specific entity across all roles, or for a specific role across all entities
|
|
10
|
+
2. **EntityPermissionsSelectorWithGridComponent** - A composite component that combines an entity dropdown selector with the permissions grid
|
|
11
|
+
|
|
5
12
|
## Features
|
|
6
13
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
+
- **Dual-mode operation**: View permissions by entity (all roles for one entity) or by role (all entities for one role)
|
|
15
|
+
- **Grid-based interface** for intuitive permission management
|
|
16
|
+
- **Batch editing** with transaction support for atomic updates
|
|
17
|
+
- **Visual feedback** with dirty state indicators and row highlighting
|
|
18
|
+
- **Quick actions**:
|
|
19
|
+
- Flip all permissions of a specific type (Read/Create/Update/Delete)
|
|
20
|
+
- Toggle all permissions in a row with intelligent logic
|
|
21
|
+
- Revert individual row changes
|
|
22
|
+
- **Real-time permission change events** with cancellation support
|
|
23
|
+
- **Automatic permission record creation** for missing role/entity combinations
|
|
24
|
+
- **Responsive loading states** with Kendo UI indicators
|
|
14
25
|
|
|
15
26
|
## Installation
|
|
16
27
|
|
|
@@ -20,159 +31,459 @@ npm install @memberjunction/ng-entity-permissions
|
|
|
20
31
|
|
|
21
32
|
## Requirements
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
- @
|
|
26
|
-
- @
|
|
27
|
-
- @
|
|
28
|
-
- @
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
### Peer Dependencies
|
|
35
|
+
- Angular 18.0.2+
|
|
36
|
+
- @angular/common
|
|
37
|
+
- @angular/core
|
|
38
|
+
- @angular/forms
|
|
39
|
+
- @angular/router
|
|
40
|
+
|
|
41
|
+
### Runtime Dependencies
|
|
42
|
+
- @memberjunction/core (v2.43.0+)
|
|
43
|
+
- @memberjunction/core-entities (v2.43.0+)
|
|
44
|
+
- @memberjunction/global (v2.43.0+)
|
|
45
|
+
- @memberjunction/ng-container-directives (v2.43.0+)
|
|
46
|
+
- @memberjunction/ng-shared (v2.43.0+)
|
|
47
|
+
- @progress/kendo-angular-grid (v16.2.0)
|
|
48
|
+
- @progress/kendo-angular-dropdowns (v16.2.0)
|
|
49
|
+
- @progress/kendo-angular-buttons (v16.2.0)
|
|
50
|
+
- @progress/kendo-angular-dialog (v16.2.0)
|
|
51
|
+
- @progress/kendo-angular-indicators (v16.2.0)
|
|
31
52
|
|
|
32
53
|
## Usage
|
|
33
54
|
|
|
34
|
-
###
|
|
55
|
+
### Module Setup
|
|
35
56
|
|
|
36
|
-
First, import the EntityPermissionsModule in your module:
|
|
57
|
+
First, import the `EntityPermissionsModule` in your Angular module:
|
|
37
58
|
|
|
38
59
|
```typescript
|
|
39
60
|
import { EntityPermissionsModule } from '@memberjunction/ng-entity-permissions';
|
|
40
61
|
|
|
41
62
|
@NgModule({
|
|
42
63
|
imports: [
|
|
43
|
-
|
|
64
|
+
CommonModule,
|
|
65
|
+
FormsModule,
|
|
66
|
+
// ... other imports
|
|
44
67
|
EntityPermissionsModule
|
|
45
68
|
],
|
|
46
69
|
})
|
|
47
70
|
export class YourModule { }
|
|
48
71
|
```
|
|
49
72
|
|
|
50
|
-
###
|
|
73
|
+
### Basic Usage Examples
|
|
51
74
|
|
|
52
|
-
|
|
75
|
+
#### Entity Permissions Grid - Entity Mode
|
|
53
76
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
Display and manage permissions for a specific entity across all roles:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { Component } from '@angular/core';
|
|
81
|
+
import { EntityPermissionChangedEvent } from '@memberjunction/ng-entity-permissions';
|
|
82
|
+
|
|
83
|
+
@Component({
|
|
84
|
+
selector: 'app-entity-permissions',
|
|
85
|
+
template: `
|
|
86
|
+
<mj-entity-permissions-grid
|
|
87
|
+
[EntityName]="'Customers'"
|
|
88
|
+
[Mode]="'Entity'"
|
|
89
|
+
[BottomMargin]="20"
|
|
90
|
+
(PermissionChanged)="onPermissionChanged($event)">
|
|
91
|
+
</mj-entity-permissions-grid>
|
|
92
|
+
`
|
|
93
|
+
})
|
|
94
|
+
export class EntityPermissionsComponent {
|
|
95
|
+
onPermissionChanged(event: EntityPermissionChangedEvent) {
|
|
96
|
+
console.log(`Permission ${event.PermissionTypeChanged} changed to ${event.Value} for role ${event.RoleID}`);
|
|
97
|
+
|
|
98
|
+
// Optionally cancel the change based on business logic
|
|
99
|
+
if (this.shouldPreventChange(event)) {
|
|
100
|
+
event.Cancel = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private shouldPreventChange(event: EntityPermissionChangedEvent): boolean {
|
|
105
|
+
// Implement your business logic here
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
60
109
|
```
|
|
61
110
|
|
|
62
|
-
|
|
111
|
+
#### Entity Permissions Grid - Role Mode
|
|
112
|
+
|
|
113
|
+
Display and manage permissions for a specific role across all entities:
|
|
63
114
|
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
```typescript
|
|
116
|
+
@Component({
|
|
117
|
+
selector: 'app-role-permissions',
|
|
118
|
+
template: `
|
|
119
|
+
<mj-entity-permissions-grid
|
|
120
|
+
[RoleName]="selectedRole"
|
|
121
|
+
[Mode]="'Role'"
|
|
122
|
+
(PermissionChanged)="onPermissionChanged($event)">
|
|
123
|
+
</mj-entity-permissions-grid>
|
|
124
|
+
`
|
|
125
|
+
})
|
|
126
|
+
export class RolePermissionsComponent {
|
|
127
|
+
selectedRole = 'Administrator';
|
|
128
|
+
|
|
129
|
+
onPermissionChanged(event: EntityPermissionChangedEvent) {
|
|
130
|
+
// Handle permission changes for the role
|
|
131
|
+
console.log(`Entity ${event.EntityName}: ${event.PermissionTypeChanged} = ${event.Value}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
70
134
|
```
|
|
71
135
|
|
|
72
|
-
|
|
136
|
+
#### Entity Selector with Permissions Grid
|
|
137
|
+
|
|
138
|
+
Provide a dropdown to select entities dynamically:
|
|
73
139
|
|
|
74
140
|
```typescript
|
|
75
|
-
|
|
76
|
-
|
|
141
|
+
@Component({
|
|
142
|
+
selector: 'app-dynamic-permissions',
|
|
143
|
+
template: `
|
|
144
|
+
<div class="permissions-container">
|
|
145
|
+
<h2>Entity Permissions Manager</h2>
|
|
146
|
+
<mj-entity-permissions-selector-with-grid
|
|
147
|
+
[EntityName]="initialEntity"
|
|
148
|
+
[BottomMargin]="30"
|
|
149
|
+
(PermissionChanged)="handlePermissionChange($event)">
|
|
150
|
+
</mj-entity-permissions-selector-with-grid>
|
|
151
|
+
</div>
|
|
152
|
+
`
|
|
153
|
+
})
|
|
154
|
+
export class DynamicPermissionsComponent {
|
|
155
|
+
initialEntity = 'Customers'; // Optional initial selection
|
|
156
|
+
|
|
157
|
+
handlePermissionChange(event: EntityPermissionChangedEvent) {
|
|
158
|
+
// Process permission changes
|
|
159
|
+
this.logPermissionChange(event);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private logPermissionChange(event: EntityPermissionChangedEvent) {
|
|
163
|
+
console.log('Permission change:', {
|
|
164
|
+
entity: event.EntityName,
|
|
165
|
+
role: event.RoleID,
|
|
166
|
+
type: event.PermissionTypeChanged,
|
|
167
|
+
value: event.Value
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Advanced Usage
|
|
174
|
+
|
|
175
|
+
#### Programmatic Grid Control
|
|
176
|
+
|
|
177
|
+
Access the grid component directly to perform operations:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { ViewChild } from '@angular/core';
|
|
181
|
+
import { EntityPermissionsGridComponent } from '@memberjunction/ng-entity-permissions';
|
|
77
182
|
|
|
78
183
|
@Component({
|
|
79
|
-
|
|
80
|
-
|
|
184
|
+
template: `
|
|
185
|
+
<mj-entity-permissions-grid #permGrid
|
|
186
|
+
[EntityName]="'Orders'"
|
|
187
|
+
[Mode]="'Entity'">
|
|
188
|
+
</mj-entity-permissions-grid>
|
|
189
|
+
|
|
190
|
+
<button (click)="refreshPermissions()">Refresh</button>
|
|
191
|
+
<button (click)="saveAllChanges()">Save All</button>
|
|
192
|
+
<button (click)="flipReadPermissions()">Toggle All Read</button>
|
|
193
|
+
`
|
|
81
194
|
})
|
|
82
|
-
export class
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
195
|
+
export class AdvancedPermissionsComponent {
|
|
196
|
+
@ViewChild('permGrid') permissionsGrid!: EntityPermissionsGridComponent;
|
|
197
|
+
|
|
198
|
+
async refreshPermissions() {
|
|
199
|
+
await this.permissionsGrid.Refresh();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async saveAllChanges() {
|
|
203
|
+
if (this.permissionsGrid.NumDirtyPermissions > 0) {
|
|
204
|
+
await this.permissionsGrid.savePermissions();
|
|
205
|
+
console.log('All permissions saved successfully');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
flipReadPermissions() {
|
|
210
|
+
this.permissionsGrid.flipAllPermissions('Read');
|
|
86
211
|
}
|
|
87
212
|
}
|
|
88
213
|
```
|
|
89
214
|
|
|
90
|
-
|
|
215
|
+
#### Handling Transactions
|
|
91
216
|
|
|
92
|
-
|
|
217
|
+
The component automatically handles batch updates using MemberJunction's transaction system:
|
|
93
218
|
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
219
|
+
```typescript
|
|
220
|
+
// The grid component internally uses transaction groups for atomic updates
|
|
221
|
+
// When savePermissions() is called, all dirty permissions are saved in a single transaction
|
|
222
|
+
// This ensures data consistency and allows for rollback if any permission update fails
|
|
98
223
|
```
|
|
99
224
|
|
|
100
225
|
## API Reference
|
|
101
226
|
|
|
102
227
|
### EntityPermissionsGridComponent
|
|
103
228
|
|
|
104
|
-
|
|
229
|
+
**Selector:** `mj-entity-permissions-grid`
|
|
230
|
+
|
|
231
|
+
#### Properties
|
|
232
|
+
|
|
233
|
+
##### Inputs
|
|
105
234
|
|
|
106
235
|
| Name | Type | Default | Description |
|
|
107
236
|
|------|------|---------|-------------|
|
|
108
|
-
| `Mode` | `'Entity' \| 'Role'` | `'Entity'` |
|
|
109
|
-
| `EntityName` | `string` | `undefined` |
|
|
110
|
-
| `RoleName` | `string` | `undefined` |
|
|
111
|
-
| `BottomMargin` | `number` | `0` |
|
|
237
|
+
| `Mode` | `'Entity' \| 'Role'` | `'Entity'` | Determines whether to display permissions for a specific entity across all roles or for a specific role across all entities |
|
|
238
|
+
| `EntityName` | `string` | `undefined` | Required when Mode is 'Entity'. The name of the entity to show permissions for |
|
|
239
|
+
| `RoleName` | `string` | `undefined` | Required when Mode is 'Role'. The name of the role to show permissions for |
|
|
240
|
+
| `BottomMargin` | `number` | `0` | Bottom margin in pixels to apply to the grid container |
|
|
112
241
|
|
|
113
|
-
|
|
242
|
+
##### Outputs
|
|
114
243
|
|
|
115
244
|
| Name | Type | Description |
|
|
116
245
|
|------|------|-------------|
|
|
117
|
-
| `PermissionChanged` | `EventEmitter<EntityPermissionChangedEvent>` | Emitted
|
|
246
|
+
| `PermissionChanged` | `EventEmitter<EntityPermissionChangedEvent>` | Emitted whenever a permission checkbox is toggled. The event can be cancelled by setting `event.Cancel = true` |
|
|
247
|
+
|
|
248
|
+
##### Public Properties
|
|
249
|
+
|
|
250
|
+
| Name | Type | Description |
|
|
251
|
+
|------|------|-------------|
|
|
252
|
+
| `permissions` | `EntityPermissionEntity[]` | Array of permission records currently displayed in the grid |
|
|
253
|
+
| `gridHeight` | `number` | Height of the grid in pixels (default: 750) |
|
|
254
|
+
| `isLoading` | `boolean` | Loading state indicator |
|
|
255
|
+
| `NumDirtyPermissions` | `number` | Count of permissions that have been modified but not saved |
|
|
118
256
|
|
|
119
257
|
#### Methods
|
|
120
258
|
|
|
121
|
-
|
|
|
122
|
-
|
|
123
|
-
| `Refresh` | None | `Promise<void>` | Reloads permissions data |
|
|
124
|
-
| `savePermissions` | None | `Promise<void>` | Saves all modified permissions |
|
|
125
|
-
| `cancelEdit` | None | `Promise<void>` | Reverts all unsaved changes |
|
|
126
|
-
| `flipAllPermissions` | `type: 'Read' \| 'Create' \| 'Update' \| 'Delete'` | `void` |
|
|
259
|
+
| Method | Parameters | Return Type | Description |
|
|
260
|
+
|--------|------------|-------------|-------------|
|
|
261
|
+
| `Refresh()` | None | `Promise<void>` | Reloads all permissions data from the database. Automatically creates missing permission records for display |
|
|
262
|
+
| `savePermissions()` | None | `Promise<void>` | Saves all modified permissions in a single transaction. Only saves permissions that are truly dirty |
|
|
263
|
+
| `cancelEdit()` | None | `Promise<void>` | Reverts all unsaved changes to their original values |
|
|
264
|
+
| `flipAllPermissions(type)` | `type: 'Read' \| 'Create' \| 'Update' \| 'Delete'` | `void` | Intelligently toggles all permissions of the specified type. If majority are ON, turns all OFF; otherwise turns all ON |
|
|
265
|
+
| `flipRow(permission)` | `permission: EntityPermissionEntity` | `void` | Toggles all permissions in a row. If 2+ are ON, turns all OFF; otherwise turns all ON |
|
|
266
|
+
| `revertRow(event, permission)` | `event: MouseEvent, permission: EntityPermissionEntity` | `void` | Reverts a single row to its original state |
|
|
127
267
|
|
|
128
268
|
### EntityPermissionsSelectorWithGridComponent
|
|
129
269
|
|
|
130
|
-
|
|
270
|
+
**Selector:** `mj-entity-permissions-selector-with-grid`
|
|
271
|
+
|
|
272
|
+
#### Properties
|
|
273
|
+
|
|
274
|
+
##### Inputs
|
|
131
275
|
|
|
132
276
|
| Name | Type | Default | Description |
|
|
133
277
|
|------|------|---------|-------------|
|
|
134
|
-
| `EntityName` | `string` | `undefined` | Name of the initially selected entity |
|
|
135
|
-
| `BottomMargin` | `number` | `0` |
|
|
278
|
+
| `EntityName` | `string` | `undefined` | Optional. Name of the initially selected entity |
|
|
279
|
+
| `BottomMargin` | `number` | `0` | Bottom margin in pixels to apply to the component |
|
|
280
|
+
| `CurrentEntity` | `EntityInfo \| undefined` | `undefined` | The currently selected entity object. Can be used for two-way binding |
|
|
136
281
|
|
|
137
|
-
|
|
282
|
+
##### Outputs
|
|
138
283
|
|
|
139
284
|
| Name | Type | Description |
|
|
140
285
|
|------|------|-------------|
|
|
141
|
-
| `PermissionChanged` | `EventEmitter<EntityPermissionChangedEvent>` |
|
|
286
|
+
| `PermissionChanged` | `EventEmitter<EntityPermissionChangedEvent>` | Bubbles up permission change events from the embedded grid |
|
|
287
|
+
|
|
288
|
+
##### Public Properties
|
|
289
|
+
|
|
290
|
+
| Name | Type | Description |
|
|
291
|
+
|------|------|-------------|
|
|
292
|
+
| `entityList` | `EntityInfo[]` | Alphabetically sorted list of all entities available for selection |
|
|
142
293
|
|
|
143
294
|
### EntityPermissionChangedEvent Interface
|
|
144
295
|
|
|
145
296
|
```typescript
|
|
146
|
-
type EntityPermissionChangedEvent = {
|
|
147
|
-
EntityName: string
|
|
148
|
-
RoleID: string
|
|
149
|
-
PermissionTypeChanged: 'Read' | 'Create' | 'Update' | 'Delete' //
|
|
150
|
-
Value: boolean
|
|
151
|
-
Cancel: boolean
|
|
297
|
+
export type EntityPermissionChangedEvent = {
|
|
298
|
+
EntityName: string; // Name of the entity whose permission changed
|
|
299
|
+
RoleID: string; // ID of the role whose permission changed
|
|
300
|
+
PermissionTypeChanged: 'Read' | 'Create' | 'Update' | 'Delete'; // The specific permission type that was modified
|
|
301
|
+
Value: boolean; // The new value of the permission (true = granted, false = revoked)
|
|
302
|
+
Cancel: boolean; // Set to true in event handler to cancel the change
|
|
152
303
|
}
|
|
153
304
|
```
|
|
154
305
|
|
|
306
|
+
### Internal Behavior Notes
|
|
307
|
+
|
|
308
|
+
1. **Automatic Record Creation**: When loading permissions, the component automatically creates unsaved EntityPermission records for any missing role/entity combinations. These appear in the grid but are only saved if the user enables at least one permission.
|
|
309
|
+
|
|
310
|
+
2. **Dirty State Logic**: A permission is considered "really dirty" if:
|
|
311
|
+
- It's an existing saved record that has been modified, OR
|
|
312
|
+
- It's a new record with at least one permission enabled
|
|
313
|
+
|
|
314
|
+
3. **Transaction Support**: All save operations use MemberJunction's TransactionGroup to ensure atomic updates across multiple permission records.
|
|
315
|
+
|
|
316
|
+
4. **Event Handling**: The grid uses a sophisticated event system where clicking on checkboxes or table cells properly toggles permissions while preventing event bubbling conflicts.
|
|
317
|
+
|
|
155
318
|
## Styling
|
|
156
319
|
|
|
157
|
-
The components use
|
|
320
|
+
The components use standard HTML tables with custom CSS classes that can be overridden:
|
|
321
|
+
|
|
322
|
+
### CSS Classes
|
|
323
|
+
|
|
324
|
+
| Class | Applied To | Description |
|
|
325
|
+
|-------|------------|-------------|
|
|
326
|
+
| `.grid` | `<table>` | Main permissions grid table |
|
|
327
|
+
| `.permission-left-col` | First `<td>` in each row | Left column containing entity/role names and revert icon |
|
|
328
|
+
| `.dirty-row` | `<tr>` | Applied to rows that have unsaved changes |
|
|
329
|
+
| `.entity-selector` | `<kendo-dropdownlist>` | Styles the entity selection dropdown |
|
|
330
|
+
| `.inner-grid` | `<mj-entity-permissions-grid>` | Applied to the grid within the selector component |
|
|
331
|
+
| `.fa-arrow-rotate-left` | `<span>` icon | Revert icon shown in dirty rows |
|
|
332
|
+
|
|
333
|
+
### Customization Example
|
|
334
|
+
|
|
335
|
+
```css
|
|
336
|
+
/* Custom styling for your application */
|
|
337
|
+
::ng-deep {
|
|
338
|
+
.mj-entity-permissions-grid {
|
|
339
|
+
.grid {
|
|
340
|
+
border: 1px solid #ddd;
|
|
341
|
+
width: 100%;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.dirty-row {
|
|
345
|
+
background-color: #fff3cd;
|
|
346
|
+
|
|
347
|
+
.permission-left-col {
|
|
348
|
+
font-weight: bold;
|
|
349
|
+
|
|
350
|
+
.fa-arrow-rotate-left {
|
|
351
|
+
color: #856404;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
margin-left: 10px;
|
|
354
|
+
|
|
355
|
+
&:hover {
|
|
356
|
+
color: #533f03;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
th {
|
|
363
|
+
cursor: pointer;
|
|
364
|
+
user-select: none;
|
|
365
|
+
|
|
366
|
+
&:hover {
|
|
367
|
+
background-color: #f0f0f0;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Integration with MemberJunction
|
|
158
375
|
|
|
159
|
-
|
|
160
|
-
- `.permission-left-col`: Left column in the grid (entity/role name)
|
|
161
|
-
- `.dirty-row`: Applied to rows with unsaved changes
|
|
162
|
-
- `.entity-selector`: Applied to the entity dropdown
|
|
163
|
-
- `.inner-grid`: Applied to the grid within the selector component
|
|
376
|
+
This package integrates seamlessly with the MemberJunction ecosystem:
|
|
164
377
|
|
|
165
|
-
|
|
378
|
+
### Entity Permission System
|
|
379
|
+
- Works with the `EntityPermissionEntity` from `@memberjunction/core-entities`
|
|
380
|
+
- Respects the MemberJunction metadata system for entities and roles
|
|
381
|
+
- Uses MemberJunction's transaction system for atomic updates
|
|
166
382
|
|
|
167
|
-
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
383
|
+
### Metadata Integration
|
|
384
|
+
- Automatically discovers all entities and roles from the Metadata provider
|
|
385
|
+
- Validates entity and role names against the metadata
|
|
386
|
+
- Creates properly typed entity objects using the metadata system
|
|
387
|
+
|
|
388
|
+
### RunView Integration
|
|
389
|
+
- Uses `RunView` for efficient data loading with proper filtering
|
|
390
|
+
- Leverages `ResultType: 'entity_object'` for automatic entity instantiation
|
|
391
|
+
- Implements proper ordering for consistent display
|
|
392
|
+
|
|
393
|
+
## Build and Development
|
|
394
|
+
|
|
395
|
+
This package uses the Angular compiler (`ngc`) for building:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
# Build the package
|
|
399
|
+
npm run build
|
|
400
|
+
|
|
401
|
+
# The built files will be in the dist/ directory
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### TypeScript Configuration
|
|
405
|
+
- Targets ES2015 with ES2020 modules
|
|
406
|
+
- Generates declaration files with source maps
|
|
407
|
+
- Uses strict mode for type safety
|
|
408
|
+
- Configured for Angular library compilation with strict templates
|
|
409
|
+
|
|
410
|
+
## Performance Considerations
|
|
411
|
+
|
|
412
|
+
1. **Efficient Loading**: The component loads all permissions in a single query and creates missing records in memory
|
|
413
|
+
2. **Batch Updates**: Uses transaction groups to minimize database round trips
|
|
414
|
+
3. **Smart Dirty Checking**: Only saves records that have actual changes
|
|
415
|
+
4. **Lazy Initialization**: Permission records for missing combinations are created on-demand
|
|
416
|
+
|
|
417
|
+
## Common Use Cases
|
|
418
|
+
|
|
419
|
+
### Admin Dashboard
|
|
420
|
+
```typescript
|
|
421
|
+
// Create a comprehensive admin interface for managing all permissions
|
|
422
|
+
@Component({
|
|
423
|
+
template: `
|
|
424
|
+
<mat-tab-group>
|
|
425
|
+
<mat-tab label="By Entity">
|
|
426
|
+
<mj-entity-permissions-selector-with-grid></mj-entity-permissions-selector-with-grid>
|
|
427
|
+
</mat-tab>
|
|
428
|
+
<mat-tab label="By Role">
|
|
429
|
+
<select [(ngModel)]="selectedRole">
|
|
430
|
+
<option *ngFor="let role of roles" [value]="role.Name">{{role.Name}}</option>
|
|
431
|
+
</select>
|
|
432
|
+
<mj-entity-permissions-grid
|
|
433
|
+
*ngIf="selectedRole"
|
|
434
|
+
[Mode]="'Role'"
|
|
435
|
+
[RoleName]="selectedRole">
|
|
436
|
+
</mj-entity-permissions-grid>
|
|
437
|
+
</mat-tab>
|
|
438
|
+
</mat-tab-group>
|
|
439
|
+
`
|
|
440
|
+
})
|
|
441
|
+
export class PermissionsDashboardComponent {
|
|
442
|
+
// Implementation
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Role Creation Wizard
|
|
447
|
+
```typescript
|
|
448
|
+
// Include permissions step in role creation
|
|
449
|
+
@Component({
|
|
450
|
+
template: `
|
|
451
|
+
<div *ngIf="currentStep === 'permissions'">
|
|
452
|
+
<h3>Configure Permissions for {{newRole.Name}}</h3>
|
|
453
|
+
<mj-entity-permissions-grid
|
|
454
|
+
[Mode]="'Role'"
|
|
455
|
+
[RoleName]="newRole.Name"
|
|
456
|
+
(PermissionChanged)="trackPermissionChanges($event)">
|
|
457
|
+
</mj-entity-permissions-grid>
|
|
458
|
+
</div>
|
|
459
|
+
`
|
|
460
|
+
})
|
|
461
|
+
export class RoleWizardComponent {
|
|
462
|
+
// Track changes and save with role
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Troubleshooting
|
|
467
|
+
|
|
468
|
+
### Common Issues
|
|
469
|
+
|
|
470
|
+
1. **"EntityName is required when Mode is 'Entity'"**
|
|
471
|
+
- Ensure you provide the EntityName input when using Entity mode
|
|
472
|
+
- Check that the entity name matches exactly (case-sensitive)
|
|
473
|
+
|
|
474
|
+
2. **"Entity not found: [EntityName]"**
|
|
475
|
+
- Verify the entity exists in MemberJunction metadata
|
|
476
|
+
- Ensure metadata is properly loaded before component initialization
|
|
477
|
+
|
|
478
|
+
3. **Permissions not saving**
|
|
479
|
+
- Check browser console for transaction errors
|
|
480
|
+
- Verify user has permission to modify EntityPermission records
|
|
481
|
+
- Ensure database constraints are not violated
|
|
482
|
+
|
|
483
|
+
4. **Grid not displaying**
|
|
484
|
+
- Confirm all Kendo UI modules are properly imported
|
|
485
|
+
- Check that EntityPermissionsModule is imported in your module
|
|
486
|
+
|
|
487
|
+
## License
|
|
488
|
+
|
|
489
|
+
ISC License - see the root MemberJunction repository for details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-entity-permissions",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.44.0",
|
|
4
4
|
"description": "MemberJunction: Angular components for displaying/editing permissions for an entity, and related components.",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"@angular/router": "18.0.2"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@memberjunction/core-entities": "2.
|
|
29
|
-
"@memberjunction/global": "2.
|
|
30
|
-
"@memberjunction/core": "2.
|
|
31
|
-
"@memberjunction/ng-container-directives": "2.
|
|
32
|
-
"@memberjunction/ng-shared": "2.
|
|
28
|
+
"@memberjunction/core-entities": "2.44.0",
|
|
29
|
+
"@memberjunction/global": "2.44.0",
|
|
30
|
+
"@memberjunction/core": "2.44.0",
|
|
31
|
+
"@memberjunction/ng-container-directives": "2.44.0",
|
|
32
|
+
"@memberjunction/ng-shared": "2.44.0",
|
|
33
33
|
"@progress/kendo-angular-dropdowns": "16.2.0",
|
|
34
34
|
"@progress/kendo-angular-grid": "16.2.0",
|
|
35
35
|
"@progress/kendo-angular-buttons": "16.2.0",
|