| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830 | 1
1
1
1
1
1
28
28
28
28
28
1
9
9
51
1
1
50
1
1
49
49
85
49
79
49
49
49
49
217
217
8
8
49
217
49
36
36
36
36
36
217
49
323
49
4
45
391
236
236
45
50
50
50
50
22
23
22
16
6
6
22
22
16
6
6
22
22
16
6
6
22
22
16
6
6
22
49
42
7
7
49
1
43
36
7
7
43
42
35
7
7
42
22
8
9
4
4
4
4
4
7
7
7
7
7
7
2
1
1
2
2
2
2
2
7
7
2
7
7
2
2
5
7
2
2
2
10
142
142
142
142
142
142
26
13
13
26
26
26
26
26
26
26
26
26
26
26
26
26
142
142
142
142
142
142
142
142
1
| var Collection = require("./../Collection").Collection;
var Permission = require("./Permission").Permission;
var Users = require("./../user/Users").Users;
var CORS = require("./../CORS").CORS;
var Q = require("q");
/**
* @class Permissions
* @name Permissions
*
* @description A collection of open ended permissions which can be applied to any object
* (usually a corpus, but could be a datalist, or datum, or datumField). A permission
* can be thought of roughly as a phrase:
* <pre>
*
* User [{username: "lingllama", gravatar: "123"}]
* has permission ["admin","write","read","comment","export","import",etc]
* to "corpus"/"datalist"/"datum"/"datumField"
*
* <pre>
*
* @property {Permission} admins Users who can perform admin operations on the corpus/datalist/datum/datumField.
* @property {Permission} exporters Users who can export the items in a corpus/datalist/datum/datumField.
* @property {Permission} writers Users who can modify the items in a corpus/datalist/datum/datumField.
* @property {Permission} commenters Users who can comment on the items in a corpus/datalist/datum/datumField.
* @property {Permission} readers Users who can read the items in a corpus/datalist/datum/datumField.
*
* @property {Object} viewAsBasePermissionSystem This is just syntactic sugar which points to the actual
* permission system. The actual permission system should be used by apps who want provide a power user or
* fine grained and open ended control over the permissions, or whose users want to understand
* the fine graned control at a lower level.
* - Kind of like Unix permissions.
* @property {Object} viewAsDataBasePermissionSystem This is syntactic sugar which makes
* the permission system look like there is an implicative relationship betwween roles.
* - Kind of like PhP/MySQL systems.
* @property {Object} viewAsGroupedPermissionSystem This is syntactic sugar which shows users in
* only one category (readOnly, writeOnly, read/write, admins) it also makes
* the permission system look like there is an implicative relationship betwween roles, but with some hint that
* there are some rare roles (such as write only) for crowdsourcing/non-standard data entry contexts.
* - Kind of like PhP/MySQL systems.
* @property {Object} viewAsEmailingTeamPermissionSystem This might be the way that
* some computational linguistics teams will understand the permission system best,
* but the words `collaborator` and `contributor` are so similar that thus far no one
* has used these terms (that we know of).
* - Kind of like GitHub.
*
* @extends Collection
* @tutorial tests/permission/PermissionTest.js
*/
var Permissions = function Permissions(options) {
Eif (!this._fieldDBtype) {
this._fieldDBtype = "Permissions";
}
this.debug("Constructing Permissions ", options);
this.cacheTimeLength = 1000;
Collection.apply(this, arguments);
};
Permissions.prototype = Object.create(Collection.prototype, /** @lends Permissions.prototype */ {
constructor: {
value: Permissions
},
INTERNAL_MODELS: {
value: {
item: Permission,
usersNotOnTeam: Users,
allUsers: Users
}
},
/*** Syntactic sugar for research teams how have been collaborating via email
sharing (To, BCC, CC) and/or Dropbox and/or github ***/
/**
* Syntactic sugar for users who have reader+commenter+writer permissions.
*
* This is a common permission in version control systems
* where the user can do everything, including categorize data, assign data cleaning
* to team members, edit information about the database but cannot perform
* except admin functions such as adding other users to the database.
*
* This should be the default permission for most users on the corpus, as the corpus
* is version controlled any action can be undone if users use their power too much,
* they can be downgraded to a collaborator or read only role.
*
* Email collaboration analog: This is for users who would be in a
* To or CC category for emails,
*
* Dropbox analog: This is for users who would be invited to join a shared
* folder on Dropbox.
*
* @type {Permission}
*/
contributors: {
get: function() {
return this.readerCommenterWriters;
},
set: function() {
this.warn("contributors cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for users who have reader+commenter permissions.
*
* This is a common permission used by research teams where an external reviewer,
* or a second language consultant can review and coment on the data,
* but cannot modify the data itself.
*
* Email collaboration analog: This is for users who would be in a BCC category
* for emails (they can read the paper, but they arent supposed to send back a
* version which they modified for the team to accept),
*
* Dropbox analog: This is for uswers who would be given a URL to a folder in Dropbox
* (but cannot download the files and modify them with the others)
*
* @type {Permission}
*/
collaborators: {
get: function() {
return this.readerCommenterOnlys;
},
set: function() {
this.warn("collaborators cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for users who have reader+writer+admin permissions.
*
* This is a common permission used by most data base management systems meaning the user can do anything.
*
* Email collaboration analog: The person who initiates the email thread
*
* Dropbox analog: The person who creates the folder in dropbox and adds others to it.
*
* @type {Permission}
*/
owners: {
get: function() {
return this.admins;
},
set: function(value) {
this.admins = value;
}
},
/*** Gropued permissions (useful to show permissions in an app) ***/
giveMeUsersWithTheseRolesAndNotTheseRoles: {
value: function(wantTheseRoles, dontWantTheseRoles) {
if (!wantTheseRoles || !dontWantTheseRoles) {
this.bug("Invalid request for users, you must supply the roles you want, and the roles you dont want.");
return new Users();
}
if (Object.prototype.toString.call(wantTheseRoles) !== "[object Array]" || Object.prototype.toString.call(dontWantTheseRoles) !== "[object Array]") {
this.bug("Invalid request for users, you must supply an array of the roles you want, and the roles you dont want.");
return new Users();
}
// this.debugMode = true;
var empty = new Users(),
permissionType,
permissionTypeIndex,
self = this,
groupLabel = wantTheseRoles.join(" ") + " onlys",
usersWhoAreOnlyInThisPermissionType = [];
/* accept requests for the plural or singular of the permission type */
wantTheseRoles = wantTheseRoles.map(function(role) {
return (role + "s").replace(/ss$/, "s");
});
dontWantTheseRoles = dontWantTheseRoles.map(function(role) {
return (role + "s").replace(/ss+$/, "s");
});
// Add users who have the wanted roles
permissionType = wantTheseRoles.pop();
Eif (this[permissionType]) {
usersWhoAreOnlyInThisPermissionType = new Users(this[permissionType].users.toJSON());
} else {
usersWhoAreOnlyInThisPermissionType = new Users();
}
var howToRemoveSomeoneWhoIsntInAllRoles = function(user) {
self.debug(" Checking on " + user.username);
if (self[permissionType].users.indexOf(user.username) === -1) {
self.debug(" - removing " + user.username + " from " + groupLabel + " because they are not in " + permissionType);
usersWhoAreOnlyInThisPermissionType.remove(user.username);
}
};
var showUsernames = function(user) {
return user.username;
};
for (permissionTypeIndex = wantTheseRoles.length - 1; permissionTypeIndex >= 0; permissionTypeIndex--) {
permissionType = wantTheseRoles[permissionTypeIndex];
self.debug("Making sure " + usersWhoAreOnlyInThisPermissionType.map(showUsernames) + " are " + permissionType);
Iif (!self[permissionType]) {
continue;
}
Iif (!self[permissionType]) {
this.warn(" This team doesn't have a " + permissionType + " so this means the " + groupLabel + " is empty.");
return empty;
} else {
// self.debug(" who are ", self[permissionType].users.map(function(user) {
// return user.username;
// }));
}
// If the user isn't in this permisisonType also, remove it
for (var userIndex = usersWhoAreOnlyInThisPermissionType._collection.length - 1; userIndex >= 0; userIndex--) {
howToRemoveSomeoneWhoIsntInAllRoles(usersWhoAreOnlyInThisPermissionType._collection[userIndex]);
}
}
this.debug("These users " + usersWhoAreOnlyInThisPermissionType.map(function(user) {
return user.username;
}).join(" ") + " have all the roles requested: " + groupLabel.replace("onlys", ""));
if (!usersWhoAreOnlyInThisPermissionType || usersWhoAreOnlyInThisPermissionType.length === 0) {
return empty;
}
var howToRemoveSomeoneWhoIsInTheUnWantedPermissions = function(user) {
if (usersWhoAreOnlyInThisPermissionType[user.username]) {
self.debug(" - " + user.username + " is also in " + permissionType + " so removing them from the " + groupLabel + ", there are " + usersWhoAreOnlyInThisPermissionType.length + " left");
usersWhoAreOnlyInThisPermissionType.remove(user.username);
}
};
for (permissionTypeIndex = dontWantTheseRoles.length - 1; permissionTypeIndex >= 0; permissionTypeIndex--) {
permissionType = dontWantTheseRoles[permissionTypeIndex];
Iif (!self[permissionType]) {
continue;
}
// remove users who are in the permission type we dont want
self[permissionType].users.map(howToRemoveSomeoneWhoIsInTheUnWantedPermissions);
if (usersWhoAreOnlyInThisPermissionType.length === 0) {
return empty;
}
}
return usersWhoAreOnlyInThisPermissionType;
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not in each low level permissions.
*
* This permission is side effect of when a user has only admin permission.
* In this case the user can only access to only admin functionalities such
* as adding new users to the database. This role is used by project managers or IT staff
* who dont know anything about the data itself, and are only setting up the corpus for
* the team to use if the PI or corpus owner doesnt know how to do the permissions.
* set-up themselves.
*
* @type {Permission}
*/
adminOnlys: {
get: function() {
if (this._adminOnlys && this._adminOnlys.timestamp && (Date.now() - this._adminOnlys.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of adminOnlys, its fresh enough. " + new Date(this._adminOnlys.timestamp));
} else {
this._adminOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["admin"], ["writer", "reader"]);
this._adminOnlys.timestamp = Date.now();
}
return this._adminOnlys;
},
set: function() {
this.warn("adminOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This permission is side effect of when a user has only write permission.
* In this case the user can add new data to the database, but cannot
* review or read or see existing data. They can edit data if it is specifically
* presented to them, they cannot query the database. As all data is version
* controlled edits can be undone so this user is not able to destroy a database that they cant read.
*
* This is not a common permission to use in the system.
*
* This permission can be used for psycholinguistics or crowdsourcing experiemnts
* where users are anonymous (identified anoymously by session or by device)
* and visit a given website or Android app and can respond to stimuli and their responses
* become new data points in the system. These users cannot access fieldlinguistics apps
* which permit browsing of the data, but is a rare permission used by web widgets or other smaller apps which write to a corpus.
*
* @type {Permission}
*/
writeOnlys: {
get: function() {
if (this._writeOnlys && this._writeOnlys.timestamp && (Date.now() - this._writeOnlys.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of writeOnlys, its fresh enough. " + new Date(this._writeOnlys.timestamp));
} else {
this._writeOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["writer"], ["admin", "reader"]);
this._writeOnlys.timestamp = Date.now();
}
return this._writeOnlys;
},
set: function() {
this.warn("writeOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This permission is side effect of when a user has only read permission.
* In this case the user might be part of a grant commitees or the general public (for
* the aspects of the corpus which have been made @publicic),
* language consultants who might leave mean comments on other consultant's data,
* or other sorts of external viewers of the data who the team doesnt want to leave
* comments.
*
* @type {Permission}
*/
readOnlys: {
get: function() {
if (this._readOnlys && this._readOnlys.timestamp && (Date.now() - this._readOnlys.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of readOnlys, its fresh enough. " + new Date(this._readOnlys.timestamp));
} else {
this._readOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader"], ["admin", "writer"]);
this._readOnlys.timestamp = Date.now();
}
return this._readOnlys;
},
set: function() {
this.warn("readOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This permission is side effect of when a user has only comment permission.
* This is a rare case which might be used in a web widget external to the data entry apps
* where users or anonymous visitors can leave a comment on data if it is presented to them,
* but cannot browse the database.
*
* @type {Permission}
*/
commentOnlys: {
get: function() {
if (this._commentOnlys && this._commentOnlys.timestamp && (Date.now() - this._commentOnlys.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of commentOnlys, its fresh enough. " + new Date(this._commentOnlys.timestamp));
} else {
this._commentOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["commenter"], ["admin", "writer", "reader"]);
this._commentOnlys.timestamp = Date.now();
}
return this._commentOnlys;
},
set: function() {
this.warn("commentOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This permission is side effect of when a user has only read and comment permission.
* This is a common permission used by research teams where an external reviewer,
* or a second language consultant can review and coment on the data,
* but cannot modify the data itself.
*
* @type {Permission}
*/
readerCommenterOnlys: {
get: function() {
if (this._readerCommenterOnlys && this._readerCommenterOnlys.timestamp && (Date.now() - this._readerCommenterOnlys.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of readerCommenterOnlys, its fresh enough. " + new Date(this._readerCommenterOnlys.timestamp));
} else {
this._readerCommenterOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader", "commenter"], ["admin", "writer"]);
this._readerCommenterOnlys.timestamp = Date.now();
}
return this._readerCommenterOnlys;
},
set: function() {
this.warn("readerCommenterOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This permission is side effect of when a user has only read and write permission.
* This is a rare permission where the user has been leaving abusive comments,
* and are thus only permitted to browse and edit the data itself, not add or edit comments.
*
* If you want to give someone access as a reader+writer, use readerWritersComenters.
*
* @type {Permission}
*/
readerWriters: {
get: function() {
return this.readerCommenterWriters;
},
set: function() {
this.warn("readerWriters cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This permission is side effect of when a user has only read comment and write permission.
* This is syntactic sugar which is often refered to as "writers" in traditional databases permissions.
* It means the user can write data, read data and comment on data
*
* @return {Permission} A permissions group with an array of Users who fall into this category
*/
readerCommenterWriters: {
get: function() {
if (this._readerCommenterWriterOnlys && this._readerCommenterWriterOnlys.timestamp && (Date.now() - this._readerCommenterWriterOnlys.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of readerCommenterWriterOnlys, its fresh enough. " + new Date(this._readerCommenterWriterOnlys.timestamp));
} else {
// this.debugMode = true;
this._readerCommenterWriterOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader", "writer", "commenter"], ["admin"]);
this._readerCommenterWriterOnlys.timestamp = Date.now();
// this.debugMode = false;
}
return this._readerCommenterWriterOnlys;
},
set: function() {
this.warn("readerCommenterWriters cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* Often refered to as "admins" in traditional databases permissio ns
* It means the user can write data, read data and comment on data, and perform any operation on the data.
*
* @return {Permission} A permission group with an array of Users who fall into this category
*/
readerCommenterWriterAdmins: {
get: function() {
if (this._readerCommenterWriterAdminAlso && this._readerCommenterWriterAdminAlso.timestamp && (Date.now() - this._readerCommenterWriterAdminAlso.timestamp < this.cacheTimeLength)) {
this.debug("Not regenerating the list of readerCommenterWriterAdminAlso, its fresh enough. " + new Date(this._readerCommenterWriterAdminAlso.timestamp));
} else {
this._readerCommenterWriterAdminAlso = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader", "writer", "admin"], []);
this._readerCommenterWriterAdminAlso.timestamp = Date.now();
}
return this._readerCommenterWriterAdminAlso;
},
set: function() {
this.warn("readerCommenterWriterAdmins cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/*** Permissions to control unruly users who the team is not able to convince to work as a team ***/
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This is a permission provided for teams who want many language consultants to contribute to one
* database without seeing the data other consultants have added. We expect this may be a common
* permission especially for teams where there is no `standard` dialect and speakers of other dialects
* are very prescriptive and think others are making `errors` which need to be corrected, this permission prevents them from `correcting` others data.
*
* @return {Permission} A permission group with an array of Users who fall into this category
*/
readOwnDataWriteOwnDataOnlys: {
get: function() {
return this.readOwnDataWriteOwnDataOnly || [];
},
set: function() {
this.warn("readOwnDataWriteOwnDataOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This is a rare permission provided for team members who have been editing other people's
* data either to update it to their own dialect (when it was supposed to be the dialect
* of the original source) or other sorts of 'destructive' edits. A user with this permission
* will know that they have been limited to commenting on others' data, and editing their own
* data only (similar to Facebook).
*
* This permission should not be a default permission among research teams, instead the readerCommenterWriter
* permission should be used to reduce territoriality in the data and makes it harder for the database to be kept clean
* because users cannot update/comment on data when they notice a problem. All data is versioned so any
* mistakes can be undone.
*
* @return {Permission} A permission group with an array of Users who fall into this category
*/
readCommentAllWriteOwnDataOnlys: {
get: function() {
return this.readCommentAllWriteOwnDataOnly || [];
},
set: function() {
this.warn("readCommentAllWriteOwnDataOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
*
* This is a rare permission provided for team members who have been editing other people's
* data either to update it to their own dialect (when it was supposed to be the dialect
* of the original source) or other sorts of 'destructive' edits which the team wants to
* prevent the user from doing this without making the user uncomfortable. A user with this permission
* will know that they have been limited to editing their own data only (similar to Facebook) and cannot
* edit other people's data.
*
* This permission should not be a default permission among research teams, instead the readerCommenterWriter
* permission should be used to reduce territoriality in the data and makes it harder for the database to be kept clean
* because users cannot update/comment on data when they notice a problem. All data is versioned so any
* mistakes can be undone.
*
* @return {Permission} A permission group with an array of Users who fall into this category
*/
readAllWriteOwnDataOnlys: {
get: function() {
return this.readAllWriteOwnDataOnly || [];
},
set: function() {
this.warn("readAllWriteOwnDataOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
}
},
/**
* This might be used by teams who want to have fine grained control over the
* permissions, or who want to understand the fine grained control at a lower level.
*
* @return {Object} an object with the 3 categories of permission like you
* would have in a PHPmyAdmin console.
*/
viewAsBasePermissionSystem: {
get: function() {
return this;
}
},
/**
* This view will be missing some members of the corpora since not all combinations of
* permission have syntactic sugar methods in this library. This shoudlnt be used
* unless the app dev is sure that the users wont be using corpora for psycholinguistics or
* crowdsourcing or computational linguistics.
*
* @return {Object} an object with the 3 categories of permission like you
* would have in a PHPmyAdmin console.
*/
viewAsGroupedPermissionSystem: {
get: function() {
return {
adminOnlys: this.adminOnlys,
writeOnlys: this.writeOnlys,
readOnlys: this.readOnlys,
readerCommenterOnlys: this.readerCommenterOnlys,
readerWriters: this.readerCommenterWriters,
admins: this.readerCommenterWriterAdmins,
commentOnlys: this.commentOnlys,
// readerCommenterWriters: readerCommenterWriters,
//writerCommenterOnlys
//writerAdminOnlys
//writerCommenterAdminOnlys
};
}
},
/**
* This is the way most field linguistics apps present the permissions system.
*
* @return {Object} an object with the 3 categories of permission like you
* would have in a PHPmyAdmin console.
*/
viewAsDataBasePermissionSystem: {
get: function() {
return {
admins: this.readerCommenterWriterAdmins,
writers: this.readerCommenterWriters,
readers: this.readerCommenterOnlys,
commenters: this.readerCommenterOnlys
};
}
},
/**
* This might be the way that some computational linguistics teams will
* understand the permission system best, but the words
* `collaborator` and `contributor` are so similar that thus far no one
* has used these terms.
*
* @return {Object} an object with the 2 categories of permission like you
* would have on GitHub
*/
viewAsEmailingTeamPermissionSystem: {
get: function() {
return {
contributors: this.contributors,
collaborators: this.collaborators,
owners: this.readerCommenterWriterAdmins
};
}
},
addUser: {
value: function(user) {
Iif (!user || !user.roles) {
this.warn("You should provide roles you want to add... doing nothing.");
return;
}
this.debug(user.roles);
var roles = user.roles;
delete user.roles;
for (var roleIndex = roles.length - 1; roleIndex >= 0; roleIndex--) {
var permissionType = roles[roleIndex];
Eif (!this[permissionType]) {
permissionType = permissionType.toLowerCase() + "s";
Iif (!this[permissionType]) {
var newPermission = this.buildPermissionFromAPermissionType(permissionType);
this.debug(" Creating a permssion group for " + permissionType, newPermission);
this.add(newPermission);
}
}
this[permissionType].users.add(user);
this.debug("the user was added to the permission group " + permissionType + " there are a total of " + this[permissionType].length + " users with this sort of access ", this[permissionType].users);
}
}
},
/**
* Removes user from roles on this permissions team, or removes user entirely if only a username is supplied.
*
* @param {Object} user A user with minimally a username and roles which should be removed, or alternatively can be just a username if you want to remove the user entirely from the team.
*/
removeUser: {
value: function(user) {
if (typeof user === "string") {
user = {
username: user,
roles: this.currentPermissions
};
this.warn("Remove was requested of a username, removing the username entirely from the permission team.");
}
Iif (!user || !user.roles) {
this.warn("You should provide roles you want to remove... doing nothing.");
return;
}
// this.debugMode = true;
this.debug(user.roles);
var roles = user.roles;
delete user.roles;
for (var roleIndex = roles.length - 1; roleIndex >= 0; roleIndex--) {
var permissionType = roles[roleIndex];
if (!this[permissionType]) {
permissionType = permissionType.toLowerCase() + "s";
}
Eif (this[permissionType]) {
// this[permissionType].users.debugMode = true;
if (this[permissionType].users[user.username]) {
var userWasInThisPermission = this[permissionType].users.remove(user);
this.debug("removed ", userWasInThisPermission);
} else {
this.warn("The user " + user.username + " was not in the " + permissionType + " anyway.");
}
this.debug("the user " + user.username + " is not in " + permissionType + " there are a total of " + this[permissionType].length + " users with this sort of access ", this[permissionType].users);
}
}
}
},
/**
* A list of "hats" which team members can wear in this team.
*/
currentPermissions: {
get: function() {
Iif (!this._collection || this._collection === 0) {
return [];
}
var self = this;
return this._collection.map(function(permission) {
return permission[self.primaryKey];
});
}
},
buildPermissionFromAPermissionType: {
value: function(permissionType) {
var verb = permissionType.replace(/s/, "").replace(/writer/, "write").replace(/er/, "");
var label = permissionType;
Eif (label && label.length > 2) {
label = label[0].toUpperCase() + label.substring(1, label.length);
}
var helpLinguists = "These users can perform " + verb + " operations on the corpus";
return {
users: [],
verb: verb,
id: permissionType,
labelFieldLinguists: label,
helpLinguists: helpLinguists,
directObject: "corpus",
// debugMode: true
};
}
},
fetch: {
value: function() {
var deferred = Q.defer(),
self = this;
if (!this.parent) {
Q.nextTick(function() {
self.fetching = self.loading = false;
deferred.reject({
error: "Cannot fetch if the permissions are not attached to a corpus"
});
});
return deferred.promise;
}
if (!this.application || !this.application.authentication || !this.application.authentication.user || !this.application.authentication.user.username) {
Q.nextTick(function() {
self.fetching = self.loading = false;
deferred.reject({
error: "Cannot fetch if the permissions are not in an application."
});
});
return deferred.promise;
}
if (this.loaded) {
this.warn("not fetching permissions, they are fresh.");
Q.nextTick(function() {
self.fetching = self.loading = false;
deferred.resolve(self);
});
return deferred.promise;
}
var dataToPost = {
connection: this.parent.connection.toJSON(),
username: this.application.authentication.user.username,
};
dataToPost.connection = dataToPost.couchConnection = dataToPost.connection;
this.fetching = this.loading = true;
CORS.makeCORSRequest({
type: "POST",
data: dataToPost,
dataType: "json",
url: this.parent.connection.authUrl + "/corpusteam"
}).then(function(result) {
self.fetching = self.loading = false;
self.loaded = true;
self.populate(result.users);
deferred.resolve(self);
},
function(reason) {
self.fetching = self.loading = false;
self.debug(reason);
deferred.reject(reason);
}).fail(
function(error) {
console.error(error.stack, self);
deferred.reject(error);
});
return deferred.promise;
}
},
/**
* Accepts an object containing permission groups which are an array of
* users masks which have this permission. This simple object is used to populate the permissions model.
*
* @param {Object} users an object containing keys which are verbs (permission types) and the value is an array of users who are in this category
*/
populate: {
value: function(users) {
if (!users || users === "defaults") {
users = {};
this.debug("Using default permission types");
}
users.admins = users.admins || [];
users.writers = users.writers || [];
users.readers = users.readers || [];
users.commenters = users.commenters || [];
users.exporters = users.exporters || [];
users.notonteam = users.notonteam || [];
users.allusers = users.allusers || [];
this.usersNotOnTeam = new Users(users.notonteam);
this.allUsers = new Users(users.allusers);
delete users.allusers;
delete users.notonteam;
var permissionType;
for (permissionType in users) {
Eif (users.hasOwnProperty(permissionType) && permissionType) {
/* if the permission is just an array of users, construct basic permission meta data around it */
Eif (Object.prototype.toString.call(users[permissionType]) === "[object Array]") {
var usersArray = users[permissionType];
users[permissionType] = this.buildPermissionFromAPermissionType(permissionType);
users[permissionType].users = usersArray;
}
this.debug("adding " + permissionType, users[permissionType]);
this.add(new Permission(users[permissionType]));
}
}
}
},
sanitizeStringForPrimaryKey: {
value: function(value) {
return value;
}
}
});
exports.Permissions = Permissions;
|