@nymphjs/tilmeld 1.0.0-beta.9 → 1.0.0-beta.91
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/CHANGELOG.md +414 -0
- package/README.md +15 -2
- package/dist/AbleObject.d.ts +15 -0
- package/dist/AbleObject.js +18 -6
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +181 -15
- package/dist/Group.js +551 -192
- package/dist/Group.js.map +1 -1
- package/dist/Group.test.js +83 -13
- package/dist/Group.test.js.map +1 -1
- package/dist/Tilmeld.d.ts +241 -12
- package/dist/Tilmeld.js +636 -254
- package/dist/Tilmeld.js.map +1 -1
- package/dist/Tilmeld.types.d.ts +2 -2
- package/dist/Tilmeld.types.js +1 -2
- package/dist/User.d.ts +395 -22
- package/dist/User.js +1316 -365
- package/dist/User.js.map +1 -1
- package/dist/User.test.js +132 -13
- package/dist/User.test.js.map +1 -1
- package/dist/conf/d.d.ts +287 -7
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +188 -141
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/enforceTilmeld.d.ts +3 -0
- package/dist/enforceTilmeld.js +18 -0
- package/dist/enforceTilmeld.js.map +1 -0
- package/dist/errors/AccessControlError.d.ts +2 -1
- package/dist/errors/AccessControlError.js +3 -6
- package/dist/errors/AccessControlError.js.map +1 -1
- package/dist/errors/BadDataError.d.ts +2 -1
- package/dist/errors/BadDataError.js +3 -6
- package/dist/errors/BadDataError.js.map +1 -1
- package/dist/errors/BadEmailError.d.ts +2 -1
- package/dist/errors/BadEmailError.js +3 -6
- package/dist/errors/BadEmailError.js.map +1 -1
- package/dist/errors/BadUsernameError.d.ts +2 -1
- package/dist/errors/BadUsernameError.js +3 -6
- package/dist/errors/BadUsernameError.js.map +1 -1
- package/dist/errors/CouldNotChangeDefaultPrimaryGroupError.js +1 -5
- package/dist/errors/CouldNotChangeDefaultPrimaryGroupError.js.map +1 -1
- package/dist/errors/EmailChangeRateLimitExceededError.js +1 -5
- package/dist/errors/EmailChangeRateLimitExceededError.js.map +1 -1
- package/dist/errors/index.d.ts +6 -6
- package/dist/errors/index.js +6 -22
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.js +16 -35
- package/dist/index.js.map +1 -1
- package/dist/tilmeldJoiProps.js +13 -18
- package/dist/tilmeldJoiProps.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +34 -31
- package/src/AbleObject.ts +2 -2
- package/src/Group.test.ts +104 -6
- package/src/Group.ts +661 -226
- package/src/Tilmeld.ts +420 -254
- package/src/Tilmeld.types.ts +2 -2
- package/src/User.test.ts +163 -6
- package/src/User.ts +1557 -454
- package/src/conf/d.ts +63 -17
- package/src/conf/defaults.ts +162 -109
- package/src/conf/index.ts +2 -2
- package/src/enforceTilmeld.ts +20 -0
- package/src/errors/AccessControlError.ts +4 -2
- package/src/errors/BadDataError.ts +4 -2
- package/src/errors/BadEmailError.ts +4 -2
- package/src/errors/BadUsernameError.ts +4 -2
- package/src/errors/index.ts +6 -6
- package/src/index.ts +12 -10
- package/src/tilmeldJoiProps.ts +11 -11
- package/src/type/locutus.d.ts +3 -0
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
package/dist/Group.js
CHANGED
|
@@ -1,16 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { nanoid } from '@nymphjs/guid';
|
|
3
|
+
import { difference, xor } from 'lodash-es';
|
|
4
|
+
import { splitn } from '@sciactive/splitn';
|
|
5
|
+
import { enforceTilmeld } from './enforceTilmeld.js';
|
|
6
|
+
import AbleObject from './AbleObject.js';
|
|
7
|
+
import { AccessControlError, BadDataError, BadEmailError, BadUsernameError, CouldNotChangeDefaultPrimaryGroupError, } from './errors/index.js';
|
|
8
|
+
import UserClass from './User.js';
|
|
9
|
+
/**
|
|
10
|
+
* A user group data model.
|
|
11
|
+
*
|
|
12
|
+
* Written by Hunter Perrin for SciActive.
|
|
13
|
+
*
|
|
14
|
+
* @author Hunter Perrin <hperrin@gmail.com>
|
|
15
|
+
* @copyright SciActive Inc
|
|
16
|
+
* @see http://nymph.io/
|
|
17
|
+
*/
|
|
18
|
+
export default class Group extends AbleObject {
|
|
19
|
+
/**
|
|
20
|
+
* The instance of Tilmeld to use for queries.
|
|
21
|
+
*/
|
|
22
|
+
static ETYPE = 'tilmeld_group';
|
|
23
|
+
static class = 'Group';
|
|
24
|
+
static checkGroupnameCallbacks = [];
|
|
25
|
+
static beforeSaveCallbacks = [];
|
|
26
|
+
static afterSaveCallbacks = [];
|
|
27
|
+
static beforeDeleteCallbacks = [];
|
|
28
|
+
static afterDeleteCallbacks = [];
|
|
29
|
+
static DEFAULT_PRIVATE_DATA = ['email', 'phone', 'abilities', 'user'];
|
|
30
|
+
static DEFAULT_ALLOWLIST_DATA = [];
|
|
31
|
+
$tags = [];
|
|
32
|
+
$clientEnabledMethods = [
|
|
33
|
+
'$checkGroupname',
|
|
34
|
+
'$checkEmail',
|
|
35
|
+
'$getAvatar',
|
|
36
|
+
'$getChildren',
|
|
37
|
+
'$getDescendants',
|
|
38
|
+
'$getLevel',
|
|
39
|
+
'$isDescendant',
|
|
40
|
+
];
|
|
41
|
+
static clientEnabledStaticMethods = [
|
|
42
|
+
'getPrimaryGroups',
|
|
43
|
+
'getSecondaryGroups',
|
|
44
|
+
];
|
|
45
|
+
$privateData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
46
|
+
static searchRestrictedData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
47
|
+
$allowlistData = [...Group.DEFAULT_ALLOWLIST_DATA];
|
|
48
|
+
$allowlistTags = [];
|
|
49
|
+
/**
|
|
50
|
+
* This should only be used by the backend.
|
|
51
|
+
*/
|
|
52
|
+
$skipAcWhenSaving = false;
|
|
53
|
+
/**
|
|
54
|
+
* This should only be used by the backend.
|
|
55
|
+
*/
|
|
56
|
+
$skipAcWhenDeleting = false;
|
|
57
|
+
/**
|
|
58
|
+
* Used to save the current groupname for domain admin permissions.
|
|
59
|
+
*/
|
|
60
|
+
$originalGroupname;
|
|
14
61
|
static async factoryGroupname(groupname) {
|
|
15
62
|
const entity = new this();
|
|
16
63
|
if (groupname != null) {
|
|
@@ -26,63 +73,89 @@ class Group extends AbleObject_1.default {
|
|
|
26
73
|
}
|
|
27
74
|
return entity;
|
|
28
75
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'$checkGroupname',
|
|
37
|
-
'$checkEmail',
|
|
38
|
-
'$getAvatar',
|
|
39
|
-
'$getChildren',
|
|
40
|
-
'$getDescendants',
|
|
41
|
-
'$getLevel',
|
|
42
|
-
'$isDescendant',
|
|
43
|
-
];
|
|
44
|
-
this.$privateData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
45
|
-
this.$allowlistData = [...Group.DEFAULT_ALLOWLIST_DATA];
|
|
46
|
-
this.$allowlistTags = [];
|
|
47
|
-
this.$skipAcWhenSaving = false;
|
|
48
|
-
if (this.guid == null) {
|
|
49
|
-
this.$data.enabled = true;
|
|
50
|
-
this.$data.abilities = [];
|
|
51
|
-
this.$updateDataProtection();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
76
|
+
/**
|
|
77
|
+
* Get all the groups that can be assigned as primary groups.
|
|
78
|
+
*
|
|
79
|
+
* @param options The options for an optional search query.
|
|
80
|
+
* @param selectors The selectors for an optional search query.
|
|
81
|
+
* @returns An array of the assignable primary groups.
|
|
82
|
+
*/
|
|
54
83
|
static async getPrimaryGroups(options, selectors) {
|
|
55
|
-
|
|
84
|
+
const tilmeld = enforceTilmeld(this);
|
|
85
|
+
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
56
86
|
throw new Error("You don't have permission to do that.");
|
|
57
87
|
}
|
|
58
|
-
return await this.getAssignableGroups(
|
|
88
|
+
return await this.getAssignableGroups(tilmeld.config.highestPrimary, { ...options, class: tilmeld.Group, return: 'entity' }, [...(selectors ?? [])]);
|
|
59
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all the groups that can be assigned as secondary groups.
|
|
92
|
+
*
|
|
93
|
+
* @param options The options for an optional search query.
|
|
94
|
+
* @param selectors The selectors for an optional search query.
|
|
95
|
+
* @returns An array of the assignable secondary groups.
|
|
96
|
+
*/
|
|
60
97
|
static async getSecondaryGroups(options, selectors) {
|
|
61
|
-
|
|
98
|
+
const tilmeld = enforceTilmeld(this);
|
|
99
|
+
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
62
100
|
throw new Error("You don't have permission to do that.");
|
|
63
101
|
}
|
|
64
|
-
return await this.getAssignableGroups(
|
|
102
|
+
return await this.getAssignableGroups(tilmeld.config.highestSecondary, { ...options, class: tilmeld.Group, return: 'entity' }, [...(selectors ?? [])]);
|
|
65
103
|
}
|
|
66
104
|
static async getAssignableGroups(highestParent, options, selectors) {
|
|
105
|
+
const tilmeld = enforceTilmeld(this);
|
|
67
106
|
let assignableGroups = [];
|
|
68
107
|
if (highestParent === false) {
|
|
69
108
|
return assignableGroups;
|
|
70
109
|
}
|
|
71
|
-
assignableGroups = await this.nymph.getEntities({ ...options, class:
|
|
110
|
+
assignableGroups = await this.nymph.getEntities({ ...options, class: tilmeld.Group }, ...selectors);
|
|
72
111
|
if (highestParent !== true) {
|
|
73
|
-
assignableGroups = assignableGroups.
|
|
112
|
+
assignableGroups = (await Promise.all(assignableGroups.map(async (group) => {
|
|
74
113
|
let curGroup = group;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
114
|
+
let parent = curGroup.parent;
|
|
115
|
+
await parent?.$wake();
|
|
116
|
+
while (parent != null && parent?.cdate != null) {
|
|
117
|
+
if (parent?.guid === highestParent) {
|
|
118
|
+
return group;
|
|
78
119
|
}
|
|
79
|
-
curGroup =
|
|
120
|
+
curGroup = parent;
|
|
121
|
+
parent = curGroup.parent;
|
|
122
|
+
await parent?.$wake();
|
|
80
123
|
}
|
|
81
|
-
return
|
|
82
|
-
});
|
|
124
|
+
return null;
|
|
125
|
+
}))).filter((group) => group != null);
|
|
83
126
|
}
|
|
84
127
|
return assignableGroups;
|
|
85
128
|
}
|
|
129
|
+
constructor() {
|
|
130
|
+
super();
|
|
131
|
+
// Defaults.
|
|
132
|
+
this.$data.enabled = true;
|
|
133
|
+
this.$data.abilities = [];
|
|
134
|
+
this.$updateDataProtection();
|
|
135
|
+
}
|
|
136
|
+
$setNymph(nymph) {
|
|
137
|
+
this.$nymph = nymph;
|
|
138
|
+
if (!this.$asleep()) {
|
|
139
|
+
if (this.$data.user && this.$data.user.$nymph !== nymph) {
|
|
140
|
+
this.$data.user.$setNymph(nymph);
|
|
141
|
+
}
|
|
142
|
+
if (this.$data.parent && this.$data.parent.$nymph !== nymph) {
|
|
143
|
+
this.$data.parent.$setNymph(nymph);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async $getUniques() {
|
|
148
|
+
const tilmeld = enforceTilmeld(this);
|
|
149
|
+
const uniques = [`u:${this.$data.groupname}`];
|
|
150
|
+
if (!tilmeld.config.emailUsernames &&
|
|
151
|
+
tilmeld.config.userFields.includes('email')) {
|
|
152
|
+
uniques.push(`e:${this.$data.email}`);
|
|
153
|
+
}
|
|
154
|
+
if (this.$data.defaultPrimary) {
|
|
155
|
+
uniques.push(`o:default-primary`);
|
|
156
|
+
}
|
|
157
|
+
return uniques;
|
|
158
|
+
}
|
|
86
159
|
$getAvatar() {
|
|
87
160
|
if (this.$data.avatar != null && this.$data.avatar !== '') {
|
|
88
161
|
return this.$data.avatar;
|
|
@@ -91,60 +164,117 @@ class Group extends AbleObject_1.default {
|
|
|
91
164
|
return 'https://secure.gravatar.com/avatar/?d=mm&s=40';
|
|
92
165
|
}
|
|
93
166
|
return ('https://secure.gravatar.com/avatar/' +
|
|
94
|
-
|
|
167
|
+
crypto
|
|
168
|
+
.createHash('sha256')
|
|
169
|
+
.update(this.$data.email.trim().toLowerCase())
|
|
170
|
+
.digest('hex')
|
|
171
|
+
.toLowerCase() +
|
|
95
172
|
'?d=identicon&s=40');
|
|
96
173
|
}
|
|
97
174
|
$jsonAcceptData(input, allowConflict = false) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.$data.abilities?.indexOf('system/admin') === -1 &&
|
|
102
|
-
tilmeld.gatekeeper('tilmeld/admin') &&
|
|
103
|
-
!tilmeld.gatekeeper('system/admin')) {
|
|
104
|
-
throw new errors_1.BadDataError("You don't have the authority to make this group a system admin.");
|
|
175
|
+
this.$check();
|
|
176
|
+
if ('abilities' in input.data) {
|
|
177
|
+
this.$checkIncomingAbilities(input.data.abilities);
|
|
105
178
|
}
|
|
106
179
|
super.$jsonAcceptData(input, allowConflict);
|
|
107
180
|
}
|
|
108
181
|
$jsonAcceptPatch(patch, allowConflict = false) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.$data.abilities?.indexOf('system/admin') === -1 &&
|
|
113
|
-
tilmeld.gatekeeper('tilmeld/admin') &&
|
|
114
|
-
!tilmeld.gatekeeper('system/admin')) {
|
|
115
|
-
throw new errors_1.BadDataError("You don't have the authority to make this group a system admin.");
|
|
182
|
+
this.$check();
|
|
183
|
+
if ('abilities' in patch.set) {
|
|
184
|
+
this.$checkIncomingAbilities(patch.set.abilities);
|
|
116
185
|
}
|
|
117
186
|
super.$jsonAcceptPatch(patch, allowConflict);
|
|
118
187
|
}
|
|
119
|
-
$
|
|
120
|
-
|
|
188
|
+
$checkIncomingAbilities(incoming) {
|
|
189
|
+
const tilmeld = enforceTilmeld(this);
|
|
190
|
+
if (!Array.isArray(incoming) ||
|
|
191
|
+
incoming.find((ability) => typeof ability !== 'string')) {
|
|
192
|
+
throw new BadDataError('Abilities must be an array of strings.');
|
|
193
|
+
}
|
|
194
|
+
if (incoming.includes('system/admin') !==
|
|
195
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
196
|
+
(!tilmeld.currentUser ||
|
|
197
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
198
|
+
throw new BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
199
|
+
}
|
|
200
|
+
if (incoming.includes('tilmeld/admin') !==
|
|
201
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
202
|
+
(!tilmeld.currentUser ||
|
|
203
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
204
|
+
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
205
|
+
}
|
|
206
|
+
if (incoming.includes('tilmeld/switch') !==
|
|
207
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
208
|
+
(!tilmeld.currentUser ||
|
|
209
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
210
|
+
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
211
|
+
}
|
|
212
|
+
if (xor(incoming.filter((ability) => ability.startsWith('tilmeld/')), this.$data.abilities?.filter((ability) => ability.startsWith('tilmeld/')) ?? []).length &&
|
|
213
|
+
(!tilmeld.currentUser ||
|
|
214
|
+
(!tilmeld.currentUser.abilities?.includes('system/admin') &&
|
|
215
|
+
!tilmeld.currentUser.abilities?.includes('tilmeld/admin')))) {
|
|
216
|
+
throw new BadDataError("You don't have the authority to grant or revoke Tilmeld abilities.");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
$putData(data, sdata, source) {
|
|
220
|
+
super.$putData(data, sdata, source);
|
|
221
|
+
if (source === 'server') {
|
|
222
|
+
this.$originalGroupname = this.$data.groupname;
|
|
223
|
+
}
|
|
121
224
|
this.$updateDataProtection();
|
|
122
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Update the data protection arrays for a user.
|
|
228
|
+
*
|
|
229
|
+
* @param givenUser User to update protection for. If undefined, will use the currently logged in user.
|
|
230
|
+
*/
|
|
123
231
|
$updateDataProtection(givenUser) {
|
|
124
|
-
const tilmeld = this
|
|
232
|
+
const tilmeld = enforceTilmeld(this);
|
|
125
233
|
let user = givenUser ?? tilmeld.User.current();
|
|
234
|
+
let [_groupname, domain] = tilmeld.config.domainSupport
|
|
235
|
+
? splitn(this.$data.groupname ?? '', '@', 2)
|
|
236
|
+
: [this.$data.groupname, null];
|
|
237
|
+
// Lowercase the domain part.
|
|
238
|
+
if (domain) {
|
|
239
|
+
domain = domain.toLowerCase();
|
|
240
|
+
}
|
|
126
241
|
this.$privateData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
127
242
|
this.$allowlistData = [...Group.DEFAULT_ALLOWLIST_DATA];
|
|
128
243
|
if (tilmeld.config.emailUsernames) {
|
|
129
244
|
this.$privateData.push('groupname');
|
|
130
245
|
}
|
|
131
246
|
if (user != null &&
|
|
132
|
-
(
|
|
133
|
-
|
|
247
|
+
(user.abilities?.includes('tilmeld/admin') ||
|
|
248
|
+
user.abilities?.includes('system/admin'))) {
|
|
249
|
+
// Users who can edit groups can see their data.
|
|
134
250
|
this.$privateData = [];
|
|
135
251
|
this.$allowlistData = undefined;
|
|
136
252
|
}
|
|
137
253
|
else if (this.$data.user != null &&
|
|
138
254
|
user != null &&
|
|
139
|
-
this.$data.user
|
|
255
|
+
this.$data.user.guid === user.guid) {
|
|
256
|
+
// Users can see their group's data.
|
|
257
|
+
this.$privateData = [];
|
|
258
|
+
}
|
|
259
|
+
else if (user != null &&
|
|
260
|
+
tilmeld.config.domainSupport &&
|
|
261
|
+
domain &&
|
|
262
|
+
user.abilities?.includes(`tilmeld/domain/${domain}/admin`)) {
|
|
263
|
+
// Domain admins can see their groups' data.
|
|
140
264
|
this.$privateData = [];
|
|
141
265
|
}
|
|
142
266
|
}
|
|
143
|
-
|
|
144
|
-
|
|
267
|
+
/**
|
|
268
|
+
* Check whether the group is a descendant of a group.
|
|
269
|
+
*
|
|
270
|
+
* @param group The group, or the group's GUID.
|
|
271
|
+
* @returns True or false.
|
|
272
|
+
*/
|
|
273
|
+
async $isDescendant(givenGroup) {
|
|
274
|
+
const tilmeld = enforceTilmeld(this);
|
|
145
275
|
let group;
|
|
146
276
|
if (typeof givenGroup === 'string') {
|
|
147
|
-
group = tilmeld.Group.
|
|
277
|
+
group = await tilmeld.Group.factory(givenGroup);
|
|
148
278
|
}
|
|
149
279
|
else {
|
|
150
280
|
group = givenGroup;
|
|
@@ -152,27 +282,41 @@ class Group extends AbleObject_1.default {
|
|
|
152
282
|
if (group.guid == null) {
|
|
153
283
|
return false;
|
|
154
284
|
}
|
|
155
|
-
if
|
|
285
|
+
// Check to see if the group is a descendant of the given group.
|
|
286
|
+
const parent = this.$data.parent;
|
|
287
|
+
await parent?.$wake();
|
|
288
|
+
if (parent == null) {
|
|
156
289
|
return false;
|
|
157
290
|
}
|
|
158
|
-
if (
|
|
291
|
+
if (parent.$is(group)) {
|
|
159
292
|
return true;
|
|
160
293
|
}
|
|
161
|
-
if (
|
|
294
|
+
if (await parent.$isDescendant(group)) {
|
|
162
295
|
return true;
|
|
163
296
|
}
|
|
164
297
|
return false;
|
|
165
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Gets an array of the group's child groups.
|
|
301
|
+
*
|
|
302
|
+
* @returns An array of groups.
|
|
303
|
+
*/
|
|
166
304
|
async $getChildren() {
|
|
167
|
-
const tilmeld = this
|
|
305
|
+
const tilmeld = enforceTilmeld(this);
|
|
168
306
|
return await this.$nymph.getEntities({ class: tilmeld.Group }, {
|
|
169
307
|
type: '&',
|
|
170
308
|
equal: ['enabled', true],
|
|
171
309
|
ref: ['parent', this],
|
|
172
310
|
});
|
|
173
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Gets an array of the group's descendant groups.
|
|
314
|
+
*
|
|
315
|
+
* @param andSelf Include this group in the returned array.
|
|
316
|
+
* @returns An array of groups.
|
|
317
|
+
*/
|
|
174
318
|
async $getDescendants(andSelf = false) {
|
|
175
|
-
const tilmeld = this
|
|
319
|
+
const tilmeld = enforceTilmeld(this);
|
|
176
320
|
let groups = [];
|
|
177
321
|
const entities = await this.$nymph.getEntities({ class: tilmeld.Group }, {
|
|
178
322
|
type: '&',
|
|
@@ -187,39 +331,41 @@ class Group extends AbleObject_1.default {
|
|
|
187
331
|
}
|
|
188
332
|
return groups;
|
|
189
333
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (andSelf) {
|
|
207
|
-
groups.push(this);
|
|
208
|
-
}
|
|
209
|
-
return groups;
|
|
210
|
-
}
|
|
211
|
-
$getLevel() {
|
|
212
|
-
const tilmeld = this.$nymph.tilmeld;
|
|
213
|
-
let group = tilmeld.Group.factorySync(this.guid ?? undefined);
|
|
334
|
+
/**
|
|
335
|
+
* Get the number of parents the group has.
|
|
336
|
+
*
|
|
337
|
+
* If the group is a top level group, this will return 0. If it is a child of
|
|
338
|
+
* a top level group, this will return 1. If it is a grandchild of a top level
|
|
339
|
+
* group, this will return 2, and so on.
|
|
340
|
+
*
|
|
341
|
+
* Levels will max out at 1024 to avoid recursive loops.
|
|
342
|
+
*
|
|
343
|
+
* @returns The level of the group.
|
|
344
|
+
*/
|
|
345
|
+
async $getLevel() {
|
|
346
|
+
const tilmeld = enforceTilmeld(this);
|
|
347
|
+
let group = await tilmeld.Group.factory(this.guid ?? undefined);
|
|
348
|
+
let parent = group.parent;
|
|
349
|
+
await parent?.$wake();
|
|
214
350
|
let level = 0;
|
|
215
|
-
while (
|
|
351
|
+
while (parent != null && parent.cdate != null && level < 1024) {
|
|
216
352
|
level++;
|
|
217
|
-
group =
|
|
353
|
+
group = parent;
|
|
354
|
+
parent = group.parent;
|
|
355
|
+
await parent?.$wake();
|
|
218
356
|
}
|
|
219
357
|
return level;
|
|
220
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Gets an array of users in the group.
|
|
361
|
+
*
|
|
362
|
+
* @param descendants Include users in all descendant groups too.
|
|
363
|
+
* @param limit The limit for the query.
|
|
364
|
+
* @param offset The offset for the query.
|
|
365
|
+
* @returns An array of users.
|
|
366
|
+
*/
|
|
221
367
|
async $getUsers(descendants = false, limit, offset) {
|
|
222
|
-
const tilmeld = this
|
|
368
|
+
const tilmeld = enforceTilmeld(this);
|
|
223
369
|
let groups = [];
|
|
224
370
|
if (descendants) {
|
|
225
371
|
groups = await this.$getDescendants();
|
|
@@ -247,14 +393,37 @@ class Group extends AbleObject_1.default {
|
|
|
247
393
|
],
|
|
248
394
|
});
|
|
249
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Check that a groupname is valid.
|
|
398
|
+
*
|
|
399
|
+
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
400
|
+
*/
|
|
250
401
|
async $checkGroupname() {
|
|
251
|
-
const tilmeld = this
|
|
402
|
+
const tilmeld = enforceTilmeld(this);
|
|
252
403
|
if (!tilmeld.config.emailUsernames) {
|
|
253
|
-
|
|
404
|
+
let [groupname, domain] = tilmeld.config.domainSupport
|
|
405
|
+
? splitn(this.$data.groupname ?? '', '@', 2)
|
|
406
|
+
: [this.$data.groupname, null];
|
|
407
|
+
// Lowercase the domain part.
|
|
408
|
+
if (domain) {
|
|
409
|
+
domain = domain.toLowerCase();
|
|
410
|
+
this.$data.groupname = `${groupname}@${domain}`;
|
|
411
|
+
}
|
|
412
|
+
if (this.$data.groupname == null ||
|
|
413
|
+
!this.$data.groupname.length ||
|
|
414
|
+
groupname == null ||
|
|
415
|
+
!groupname.length) {
|
|
254
416
|
return { result: false, message: 'Please specify a groupname.' };
|
|
255
417
|
}
|
|
256
|
-
if (tilmeld.config.
|
|
257
|
-
|
|
418
|
+
if (groupname.length < tilmeld.config.minUsernameLength) {
|
|
419
|
+
return {
|
|
420
|
+
result: false,
|
|
421
|
+
message: 'Groupnames must be at least ' +
|
|
422
|
+
tilmeld.config.minUsernameLength +
|
|
423
|
+
' characters.',
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (groupname.length > tilmeld.config.maxUsernameLength) {
|
|
258
427
|
return {
|
|
259
428
|
result: false,
|
|
260
429
|
message: 'Groupnames must not exceed ' +
|
|
@@ -262,18 +431,34 @@ class Group extends AbleObject_1.default {
|
|
|
262
431
|
' characters.',
|
|
263
432
|
};
|
|
264
433
|
}
|
|
265
|
-
if (
|
|
434
|
+
if (difference(groupname.split(''), tilmeld.config.validChars.split(''))
|
|
435
|
+
.length) {
|
|
266
436
|
return {
|
|
267
437
|
result: false,
|
|
268
438
|
message: tilmeld.config.validCharsNotice,
|
|
269
439
|
};
|
|
270
440
|
}
|
|
271
|
-
if (!tilmeld.config.validRegex.test(
|
|
441
|
+
if (!tilmeld.config.validRegex.test(groupname)) {
|
|
272
442
|
return {
|
|
273
443
|
result: false,
|
|
274
444
|
message: tilmeld.config.validRegexNotice,
|
|
275
445
|
};
|
|
276
446
|
}
|
|
447
|
+
if (domain != null) {
|
|
448
|
+
if (!tilmeld.config.validDomainRegex.test(domain)) {
|
|
449
|
+
return {
|
|
450
|
+
result: false,
|
|
451
|
+
message: tilmeld.config.validDomainRegexNotice,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (!this.$is(tilmeld.currentUser?.group) &&
|
|
455
|
+
!tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`)) {
|
|
456
|
+
return {
|
|
457
|
+
result: false,
|
|
458
|
+
message: "You don't have the authority to manage groups on this domain.",
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
}
|
|
277
462
|
const selector = {
|
|
278
463
|
type: '&',
|
|
279
464
|
ilike: [
|
|
@@ -288,6 +473,17 @@ class Group extends AbleObject_1.default {
|
|
|
288
473
|
if (test != null) {
|
|
289
474
|
return { result: false, message: 'That groupname is taken.' };
|
|
290
475
|
}
|
|
476
|
+
for (let callback of this.constructor
|
|
477
|
+
.checkGroupnameCallbacks) {
|
|
478
|
+
if (callback) {
|
|
479
|
+
const result = await callback(this, {
|
|
480
|
+
groupname: this.$data.groupname,
|
|
481
|
+
});
|
|
482
|
+
if (!result.result) {
|
|
483
|
+
return { message: 'Groupname is not available.', ...result };
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
291
487
|
return {
|
|
292
488
|
result: true,
|
|
293
489
|
message: this.guid != null ? 'Groupname is valid.' : 'Groupname is available!',
|
|
@@ -298,8 +494,15 @@ class Group extends AbleObject_1.default {
|
|
|
298
494
|
if (this.$data.groupname == null || !this.$data.groupname.length) {
|
|
299
495
|
return { result: false, message: 'Please specify an email.' };
|
|
300
496
|
}
|
|
301
|
-
if (tilmeld.config.
|
|
302
|
-
|
|
497
|
+
if (this.$data.groupname.length < tilmeld.config.minUsernameLength) {
|
|
498
|
+
return {
|
|
499
|
+
result: false,
|
|
500
|
+
message: 'Emails must be at least ' +
|
|
501
|
+
tilmeld.config.minUsernameLength +
|
|
502
|
+
' characters.',
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
if (this.$data.groupname.length > tilmeld.config.maxUsernameLength) {
|
|
303
506
|
return {
|
|
304
507
|
result: false,
|
|
305
508
|
message: 'Emails must not exceed ' +
|
|
@@ -310,8 +513,13 @@ class Group extends AbleObject_1.default {
|
|
|
310
513
|
return await this.$checkEmail();
|
|
311
514
|
}
|
|
312
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
* Check that an email is unique.
|
|
518
|
+
*
|
|
519
|
+
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
520
|
+
*/
|
|
313
521
|
async $checkEmail() {
|
|
314
|
-
const tilmeld = this
|
|
522
|
+
const tilmeld = enforceTilmeld(this);
|
|
315
523
|
if (this.$data.email === '') {
|
|
316
524
|
return { result: true, message: '' };
|
|
317
525
|
}
|
|
@@ -344,10 +552,42 @@ class Group extends AbleObject_1.default {
|
|
|
344
552
|
};
|
|
345
553
|
}
|
|
346
554
|
async $save() {
|
|
347
|
-
|
|
555
|
+
let tilmeld = enforceTilmeld(this);
|
|
348
556
|
if (this.$data.groupname == null || !this.$data.groupname.trim().length) {
|
|
349
557
|
return false;
|
|
350
558
|
}
|
|
559
|
+
// Lowercase the domain part.
|
|
560
|
+
if (tilmeld.config.domainSupport) {
|
|
561
|
+
const [groupname, domain] = splitn(this.$data.groupname ?? '', '@', 2);
|
|
562
|
+
if (domain) {
|
|
563
|
+
this.$data.groupname = `${groupname}@${domain.toLowerCase()}`;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (tilmeld.config.domainSupport) {
|
|
567
|
+
const [_groupname, domain] = splitn(this.$data.groupname ?? '', '@', 2);
|
|
568
|
+
const [_originalGroupname, originalDomain] = splitn(this.$originalGroupname ?? '', '@', 2);
|
|
569
|
+
if (this.guid != null &&
|
|
570
|
+
((originalDomain == null && domain != null) ||
|
|
571
|
+
(domain == null && originalDomain != null))) {
|
|
572
|
+
throw new AccessControlError("Groups can't switch to or from having a domain once created.");
|
|
573
|
+
}
|
|
574
|
+
if (!this.$skipAcWhenSaving &&
|
|
575
|
+
!tilmeld.gatekeeper('tilmeld/admin') &&
|
|
576
|
+
!tilmeld.gatekeeper('system/admin')) {
|
|
577
|
+
if (domain != null &&
|
|
578
|
+
!this.$is(tilmeld.currentUser) &&
|
|
579
|
+
!tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`)) {
|
|
580
|
+
throw new AccessControlError("You don't have the authority to modify groups on this domain.");
|
|
581
|
+
}
|
|
582
|
+
if (originalDomain != null &&
|
|
583
|
+
originalDomain !== domain &&
|
|
584
|
+
(!tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`) ||
|
|
585
|
+
!tilmeld.gatekeeper(`tilmeld/domain/${originalDomain}/admin`))) {
|
|
586
|
+
throw new AccessControlError("You don't have the authority to modify groups on this domain.");
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// Formatting.
|
|
351
591
|
this.$data.groupname = this.$data.groupname.trim();
|
|
352
592
|
if (tilmeld.config.emailUsernames) {
|
|
353
593
|
this.$data.email = this.$data.groupname;
|
|
@@ -356,16 +596,19 @@ class Group extends AbleObject_1.default {
|
|
|
356
596
|
this.$data.name = (this.$data.name ?? '').trim();
|
|
357
597
|
this.$data.phone = (this.$data.phone ?? '').trim();
|
|
358
598
|
this.$data.enabled = !!this.$data.enabled;
|
|
359
|
-
|
|
599
|
+
// Clear empty values.
|
|
600
|
+
if (this.$data.name === '' || !tilmeld.config.userFields.includes('name')) {
|
|
360
601
|
delete this.$data.name;
|
|
361
602
|
}
|
|
362
|
-
if (this.$data.email === ''
|
|
603
|
+
if (this.$data.email === '' ||
|
|
604
|
+
!tilmeld.config.userFields.includes('email')) {
|
|
363
605
|
delete this.$data.email;
|
|
364
606
|
}
|
|
365
607
|
if (this.$data.avatar === '') {
|
|
366
608
|
delete this.$data.avatar;
|
|
367
609
|
}
|
|
368
|
-
if (this.$data.phone === ''
|
|
610
|
+
if (this.$data.phone === '' ||
|
|
611
|
+
!tilmeld.config.userFields.includes('phone')) {
|
|
369
612
|
delete this.$data.phone;
|
|
370
613
|
}
|
|
371
614
|
if (this.$data.parent == null) {
|
|
@@ -383,46 +626,102 @@ class Group extends AbleObject_1.default {
|
|
|
383
626
|
if (!this.$data.unverifiedSecondary) {
|
|
384
627
|
delete this.$data.unverifiedSecondary;
|
|
385
628
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
629
|
+
// Groups should never have 'system/admin', 'tilmeld/admin',
|
|
630
|
+
// 'tilmeld/switch', or domain admin abilities.
|
|
631
|
+
this.$data.abilities = (this.$data.abilities ?? []).filter((ability) => {
|
|
632
|
+
if (['system/admin', 'tilmeld/admin', 'tilmeld/switch'].indexOf(ability) !==
|
|
633
|
+
-1) {
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
if (ability == null) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
if (ability.startsWith('tilmeld/domain/') && ability.endsWith('/admin')) {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
return true;
|
|
643
|
+
});
|
|
644
|
+
// Verification.
|
|
392
645
|
const unCheck = await this.$checkGroupname();
|
|
393
646
|
if (!unCheck.result) {
|
|
394
|
-
throw new
|
|
647
|
+
throw new BadUsernameError(unCheck.message);
|
|
395
648
|
}
|
|
396
|
-
if (!tilmeld.config.emailUsernames
|
|
649
|
+
if (!tilmeld.config.emailUsernames &&
|
|
650
|
+
tilmeld.config.userFields.includes('email')) {
|
|
397
651
|
const emCheck = await this.$checkEmail();
|
|
398
652
|
if (!emCheck.result) {
|
|
399
|
-
throw new
|
|
653
|
+
throw new BadEmailError(emCheck.message);
|
|
400
654
|
}
|
|
401
655
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
656
|
+
// Validate group parent. Make sure it's not a descendant of this group.
|
|
657
|
+
const parent = this.$data.parent;
|
|
658
|
+
await parent?.$wake();
|
|
659
|
+
if (parent &&
|
|
660
|
+
(parent.cdate == null ||
|
|
661
|
+
this.$is(parent) ||
|
|
662
|
+
(await parent.$isDescendant(this)))) {
|
|
663
|
+
throw new BadDataError("Group parent can't be itself or descendant of itself.");
|
|
407
664
|
}
|
|
408
665
|
try {
|
|
409
|
-
tilmeld.config.validatorGroup(this);
|
|
666
|
+
tilmeld.config.validatorGroup(tilmeld, this);
|
|
410
667
|
}
|
|
411
668
|
catch (e) {
|
|
412
|
-
throw new
|
|
669
|
+
throw new BadDataError(e?.message);
|
|
413
670
|
}
|
|
671
|
+
// Only one default primary group is allowed.
|
|
414
672
|
if (this.$data.defaultPrimary) {
|
|
415
673
|
const currentPrimary = await this.$nymph.getEntity({ class: tilmeld.Group }, { type: '&', truthy: 'defaultPrimary' });
|
|
416
674
|
if (currentPrimary != null && !this.$is(currentPrimary)) {
|
|
417
675
|
currentPrimary.defaultPrimary = false;
|
|
418
|
-
if (
|
|
419
|
-
|
|
676
|
+
if (this.$skipAcWhenSaving
|
|
677
|
+
? !(await currentPrimary.$saveSkipAC())
|
|
678
|
+
: !(await currentPrimary.$save())) {
|
|
679
|
+
throw new CouldNotChangeDefaultPrimaryGroupError('Could not change new user primary group from ' +
|
|
420
680
|
`${currentPrimary.groupname}.`);
|
|
421
681
|
}
|
|
422
682
|
}
|
|
423
683
|
}
|
|
424
|
-
|
|
684
|
+
const transaction = 'tilmeld-save-group-' + (this.guid || nanoid());
|
|
685
|
+
const nymph = this.$nymph;
|
|
686
|
+
const tnymph = await nymph.startTransaction(transaction);
|
|
687
|
+
this.$setNymph(tnymph);
|
|
688
|
+
tilmeld = enforceTilmeld(this);
|
|
689
|
+
let preGuid = this.guid;
|
|
690
|
+
let preCdate = this.cdate;
|
|
691
|
+
let preMdate = this.mdate;
|
|
692
|
+
try {
|
|
693
|
+
for (let callback of this.constructor
|
|
694
|
+
.beforeSaveCallbacks) {
|
|
695
|
+
if (callback) {
|
|
696
|
+
await callback(this);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
let ret = await super.$save();
|
|
700
|
+
if (ret) {
|
|
701
|
+
this.$originalGroupname = this.$data.groupname;
|
|
702
|
+
}
|
|
703
|
+
for (let callback of this.constructor
|
|
704
|
+
.afterSaveCallbacks) {
|
|
705
|
+
if (callback) {
|
|
706
|
+
await callback(this);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
ret = await tnymph.commit(transaction);
|
|
710
|
+
this.$setNymph(nymph);
|
|
711
|
+
return ret;
|
|
712
|
+
}
|
|
713
|
+
catch (e) {
|
|
714
|
+
await tnymph.rollback(transaction);
|
|
715
|
+
this.guid = preGuid;
|
|
716
|
+
this.cdate = preCdate;
|
|
717
|
+
this.mdate = preMdate;
|
|
718
|
+
this.$setNymph(nymph);
|
|
719
|
+
throw e;
|
|
720
|
+
}
|
|
425
721
|
}
|
|
722
|
+
/*
|
|
723
|
+
* This should *never* be accessible on the client.
|
|
724
|
+
*/
|
|
426
725
|
async $saveSkipAC() {
|
|
427
726
|
this.$skipAcWhenSaving = true;
|
|
428
727
|
return await this.$save();
|
|
@@ -435,73 +734,133 @@ class Group extends AbleObject_1.default {
|
|
|
435
734
|
return false;
|
|
436
735
|
}
|
|
437
736
|
async $delete() {
|
|
438
|
-
let tilmeld = this
|
|
439
|
-
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
440
|
-
throw new
|
|
737
|
+
let tilmeld = enforceTilmeld(this);
|
|
738
|
+
if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
|
|
739
|
+
throw new AccessControlError("You don't have the authority to delete groups.");
|
|
740
|
+
}
|
|
741
|
+
if (this.$data.groupname !== this.$originalGroupname) {
|
|
742
|
+
throw new AccessControlError("You can't delete a group while switching its groupname.");
|
|
441
743
|
}
|
|
442
|
-
const transaction = 'tilmeld-delete-' + this.guid;
|
|
744
|
+
const transaction = 'tilmeld-delete-group-' + this.guid;
|
|
443
745
|
const nymph = this.$nymph;
|
|
444
746
|
const tnymph = await nymph.startTransaction(transaction);
|
|
445
|
-
this.$
|
|
446
|
-
tilmeld = this
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
for (let
|
|
450
|
-
|
|
747
|
+
this.$setNymph(tnymph);
|
|
748
|
+
tilmeld = enforceTilmeld(this);
|
|
749
|
+
const User = tnymph.getEntityClass(UserClass);
|
|
750
|
+
try {
|
|
751
|
+
for (let callback of this.constructor
|
|
752
|
+
.beforeDeleteCallbacks) {
|
|
753
|
+
if (callback) {
|
|
754
|
+
await callback(this);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
// Delete descendants.
|
|
758
|
+
const descendants = await this.$getDescendants();
|
|
759
|
+
if (descendants.length) {
|
|
760
|
+
for (let curGroup of descendants) {
|
|
761
|
+
if (this.$skipAcWhenDeleting
|
|
762
|
+
? !(await curGroup.$deleteSkipAC())
|
|
763
|
+
: !(await curGroup.$delete())) {
|
|
764
|
+
await tnymph.rollback(transaction);
|
|
765
|
+
this.$setNymph(nymph);
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
// Remove users from this primary group.
|
|
771
|
+
const primaryUsers = await tnymph.getEntities({
|
|
772
|
+
class: User,
|
|
773
|
+
skipAc: true,
|
|
774
|
+
}, {
|
|
775
|
+
type: '&',
|
|
776
|
+
ref: ['group', this],
|
|
777
|
+
});
|
|
778
|
+
for (let user of primaryUsers) {
|
|
779
|
+
delete user.group;
|
|
780
|
+
if (this.$skipAcWhenDeleting
|
|
781
|
+
? !(await user.$saveSkipAC())
|
|
782
|
+
: !(await user.$save())) {
|
|
451
783
|
await tnymph.rollback(transaction);
|
|
452
|
-
this.$nymph
|
|
784
|
+
this.$setNymph(nymph);
|
|
453
785
|
return false;
|
|
454
786
|
}
|
|
455
787
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
788
|
+
// Remove users from this secondary group.
|
|
789
|
+
const secondaryUsers = await tnymph.getEntities({
|
|
790
|
+
class: User,
|
|
791
|
+
skipAc: true,
|
|
792
|
+
}, {
|
|
793
|
+
type: '&',
|
|
794
|
+
ref: ['groups', this],
|
|
795
|
+
});
|
|
796
|
+
for (let user of secondaryUsers) {
|
|
797
|
+
user.$delGroup(this);
|
|
798
|
+
if (this.$skipAcWhenDeleting
|
|
799
|
+
? !(await user.$saveSkipAC())
|
|
800
|
+
: !(await user.$save())) {
|
|
801
|
+
await tnymph.rollback(transaction);
|
|
802
|
+
this.$setNymph(nymph);
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
470
805
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
806
|
+
// Delete the group.
|
|
807
|
+
let success = await super.$delete();
|
|
808
|
+
if (success) {
|
|
809
|
+
for (let callback of this.constructor
|
|
810
|
+
.afterDeleteCallbacks) {
|
|
811
|
+
if (callback) {
|
|
812
|
+
await callback(this);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
success = await tnymph.commit(transaction);
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
482
818
|
await tnymph.rollback(transaction);
|
|
483
|
-
return false;
|
|
484
819
|
}
|
|
820
|
+
this.$setNymph(nymph);
|
|
821
|
+
return success;
|
|
485
822
|
}
|
|
486
|
-
|
|
487
|
-
if (success) {
|
|
488
|
-
await tnymph.commit(transaction);
|
|
489
|
-
}
|
|
490
|
-
else {
|
|
823
|
+
catch (e) {
|
|
491
824
|
await tnymph.rollback(transaction);
|
|
825
|
+
this.$setNymph(nymph);
|
|
826
|
+
throw e;
|
|
492
827
|
}
|
|
493
|
-
|
|
494
|
-
|
|
828
|
+
}
|
|
829
|
+
/*
|
|
830
|
+
* This should *never* be accessible on the client.
|
|
831
|
+
*/
|
|
832
|
+
async $deleteSkipAC() {
|
|
833
|
+
this.$skipAcWhenDeleting = true;
|
|
834
|
+
return await this.$delete();
|
|
835
|
+
}
|
|
836
|
+
$tilmeldDeleteSkipAC() {
|
|
837
|
+
if (this.$skipAcWhenDeleting) {
|
|
838
|
+
this.$skipAcWhenDeleting = false;
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
static on(event, callback) {
|
|
844
|
+
const prop = (event + 'Callbacks');
|
|
845
|
+
if (!(prop in this)) {
|
|
846
|
+
throw new Error('Invalid event type.');
|
|
847
|
+
}
|
|
848
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
849
|
+
this[prop].push(callback);
|
|
850
|
+
return () => this.off(event, callback);
|
|
851
|
+
}
|
|
852
|
+
static off(event, callback) {
|
|
853
|
+
const prop = (event + 'Callbacks');
|
|
854
|
+
if (!(prop in this)) {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
858
|
+
const i = this[prop].indexOf(callback);
|
|
859
|
+
if (i > -1) {
|
|
860
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
861
|
+
this[prop].splice(i, 1);
|
|
862
|
+
}
|
|
863
|
+
return true;
|
|
495
864
|
}
|
|
496
865
|
}
|
|
497
|
-
exports.default = Group;
|
|
498
|
-
Group.ETYPE = 'tilmeld_group';
|
|
499
|
-
Group.class = 'Group';
|
|
500
|
-
Group.DEFAULT_PRIVATE_DATA = ['email', 'phone', 'abilities', 'user'];
|
|
501
|
-
Group.DEFAULT_ALLOWLIST_DATA = [];
|
|
502
|
-
Group.clientEnabledStaticMethods = [
|
|
503
|
-
'getPrimaryGroups',
|
|
504
|
-
'getSecondaryGroups',
|
|
505
|
-
];
|
|
506
|
-
Group.searchRestrictedData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
507
866
|
//# sourceMappingURL=Group.js.map
|