Skip to content

Commit 981bc8d

Browse files
authored
Facilitation of Collaborative Work (#1863)
* Any user (not tester) can create an organization * isUserOwner unified * only study owner can "Edit Access Level" * Allow sharing service with everyone * default thumbnail if null
1 parent acf3f6d commit 981bc8d

File tree

11 files changed

+97
-60
lines changed

11 files changed

+97
-60
lines changed

services/web/client/source/class/osparc/component/export/Permissions.js

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ qx.Class.define("osparc.component.export.Permissions", {
2222
/**
2323
* @param serializedData {Object} Object containing the Serialized Data
2424
*/
25-
construct: function(serializedData) {
25+
construct: function(serializedData, initCollabs = []) {
2626
this.base(arguments);
2727

2828
this.__serializedData = serializedData;
@@ -31,7 +31,11 @@ qx.Class.define("osparc.component.export.Permissions", {
3131

3232
this.__buildLayout();
3333

34-
this.__getMyFriends();
34+
this.__collaborators = {};
35+
initCollabs.forEach(initCollab => {
36+
this.__collaborators[initCollab["gid"]] = initCollab;
37+
});
38+
this.__getCollaborators();
3539
},
3640

3741
statics: {
@@ -58,7 +62,7 @@ qx.Class.define("osparc.component.export.Permissions", {
5862
__serializedData: null,
5963
__organizationsAndMembers: null,
6064
__collaboratorsModel: null,
61-
__myFrieds: null,
65+
__collaborators: null,
6266
__addCollaboratorBtn: null,
6367

6468
__buildLayout: function() {
@@ -128,12 +132,12 @@ qx.Class.define("osparc.component.export.Permissions", {
128132
bindItem: (ctrl, item, id) => {
129133
ctrl.bindProperty("gid", "model", null, item, id);
130134
ctrl.bindProperty("gid", "key", null, item, id);
135+
ctrl.bindProperty("collabType", "collabType", null, item, id);
131136
ctrl.bindProperty("thumbnail", "thumbnail", null, item, id);
132137
ctrl.bindProperty("name", "title", null, item, id); // user
133138
ctrl.bindProperty("label", "title", null, item, id); // organization
134139
ctrl.bindProperty("login", "subtitle", null, item, id); // user
135140
ctrl.bindProperty("description", "subtitle", null, item, id); // organization
136-
ctrl.bindProperty("isOrg", "isOrganization", null, item, id);
137141
ctrl.bindProperty("accessRights", "accessRights", null, item, id);
138142
ctrl.bindProperty("showOptions", "showOptions", null, item, id);
139143
},
@@ -159,9 +163,7 @@ qx.Class.define("osparc.component.export.Permissions", {
159163
return vBox;
160164
},
161165

162-
__getMyFriends: function() {
163-
this.__myFrieds = {};
164-
166+
__getCollaborators: function() {
165167
const store = osparc.store.Store.getInstance();
166168
const promises = [];
167169
promises.push(store.getGroupsOrganizations());
@@ -171,13 +173,13 @@ qx.Class.define("osparc.component.export.Permissions", {
171173
const orgs = values[0];
172174
const orgMembers = values[1];
173175
orgs.forEach(org => {
174-
org["isOrg"] = true;
175-
this.__myFrieds[org["gid"]] = org;
176+
org["collabType"] = 1;
177+
this.__collaborators[org["gid"]] = org;
176178
});
177179
for (const gid of Object.keys(orgMembers)) {
178180
const orgMember = orgMembers[gid];
179-
orgMember["isOrg"] = false;
180-
this.__myFrieds[gid] = orgMember;
181+
orgMember["collabType"] = 2;
182+
this.__collaborators[gid] = orgMember;
181183
}
182184
this.__reloadOrganizationsAndMembers();
183185
this.__reloadCollaboratorsList();
@@ -188,17 +190,29 @@ qx.Class.define("osparc.component.export.Permissions", {
188190
this.__organizationsAndMembers.reset();
189191

190192
const aceessRights = this.__getAccessRights();
191-
const myFriends = Object.values(this.__myFrieds);
193+
const myFriends = Object.values(this.__collaborators);
192194

193195
// sort them first
194196
myFriends.sort((a, b) => (a["label"] > b["label"]) ? 1 : -1);
195-
myFriends.sort((a, b) => (a["isOrg"] && !b["isOrg"]) ? -1 : 1);
197+
myFriends.sort((a, b) => (a["collabType"] > b["collabType"]) ? 1 : -1);
196198

197199
myFriends.forEach(myFriend => {
198200
const gid = myFriend["gid"];
199201
if (parseInt(gid) !== osparc.auth.Data.getInstance().getGroupId() && !(parseInt(gid) in aceessRights)) {
200202
const btn = this.__organizationsAndMembers.addOption(myFriend);
201-
btn.setIcon(myFriend["isOrg"] ? "@FontAwesome5Solid/users/14" : "@FontAwesome5Solid/user/14");
203+
let iconPath = null;
204+
switch (myFriend["collabType"]) {
205+
case 0:
206+
iconPath = "@FontAwesome5Solid/globe/14";
207+
break;
208+
case 1:
209+
iconPath = "@FontAwesome5Solid/users/14";
210+
break;
211+
case 2:
212+
iconPath = "@FontAwesome5Solid/user/14";
213+
break;
214+
}
215+
btn.setIcon(iconPath);
202216
}
203217
});
204218
},
@@ -208,8 +222,8 @@ qx.Class.define("osparc.component.export.Permissions", {
208222

209223
const aceessRights = this.__getAccessRights();
210224
Object.keys(aceessRights).forEach(gid => {
211-
if (Object.prototype.hasOwnProperty.call(this.__myFrieds, gid)) {
212-
const collaborator = this.__myFrieds[gid];
225+
if (Object.prototype.hasOwnProperty.call(this.__collaborators, gid)) {
226+
const collaborator = this.__collaborators[gid];
213227
if ("first_name" in collaborator) {
214228
collaborator["thumbnail"] = osparc.utils.Avatar.getUrl(collaborator["login"], 32);
215229
collaborator["name"] = osparc.utils.Utils.firstsUp(collaborator["first_name"], collaborator["last_name"]);

services/web/client/source/class/osparc/component/export/ServicePermissions.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ qx.Class.define("osparc.component.export.ServicePermissions", {
3333
construct: function(serviceData) {
3434
this.__serviceData = osparc.utils.Utils.deepCloneObject(serviceData);
3535

36-
this.base(arguments, this.__serviceData);
36+
const initCollabs = this.self().getEveryoneObj();
37+
this.base(arguments, this.__serviceData, [initCollabs]);
3738
},
3839

3940
events: {
@@ -57,6 +58,17 @@ qx.Class.define("osparc.component.export.ServicePermissions", {
5758

5859
removeCollaborator: function(serializedData, gid) {
5960
return delete serializedData["access_rights"][gid];
61+
},
62+
63+
getEveryoneObj: function() {
64+
return {
65+
"gid": 1,
66+
"label": "Everyone",
67+
"description": "",
68+
"thumbnail": null,
69+
"accessRights": this.getCollaboratorAccessRight(),
70+
"collabType": 0
71+
};
6072
}
6173
},
6274

services/web/client/source/class/osparc/component/export/StudyPermissions.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,7 @@ qx.Class.define("osparc.component.export.StudyPermissions", {
6666
__studyData: null,
6767

6868
_isUserOwner: function() {
69-
const myGid = osparc.auth.Data.getInstance().getGroupId();
70-
const aceessRights = this.__studyData["accessRights"];
71-
if (myGid in aceessRights) {
72-
return aceessRights[myGid]["delete"];
73-
}
74-
return false;
69+
return osparc.data.model.Study.isOwner(this.__studyData);
7570
},
7671

7772
_addCollaborator: function() {

services/web/client/source/class/osparc/component/metadata/StudyDetailsEditor.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ qx.Class.define("osparc.component.metadata.StudyDetailsEditor", {
8080
},
8181

8282
__createButtons: function(isTemplate) {
83-
const isCurrentUserOwner = this.__isUserOwner();
83+
const isCurrentUserOwner = osparc.data.model.Study.isOwner(this.__studyData);
8484
const canUpdateTemplate = osparc.data.Permissions.getInstance().canDo("studies.template.update");
8585

8686
const buttonsToolbar = new qx.ui.toolbar.ToolBar();
@@ -106,7 +106,7 @@ qx.Class.define("osparc.component.metadata.StudyDetailsEditor", {
106106
},
107107

108108
__createEditView: function(isTemplate) {
109-
const isCurrentUserOwner = this.__isUserOwner();
109+
const isCurrentUserOwner = osparc.data.model.Study.isOwner(this.__studyData);
110110
const canUpdateTemplate = osparc.data.Permissions.getInstance().canDo("studies.template.update");
111111
const fieldIsEnabled = isCurrentUserOwner && (!isTemplate || canUpdateTemplate);
112112

@@ -282,13 +282,6 @@ qx.Class.define("osparc.component.metadata.StudyDetailsEditor", {
282282
this.__stack.setSelection([this.__editView]);
283283
break;
284284
}
285-
},
286-
287-
__isUserOwner: function() {
288-
if (this.__studyData) {
289-
return this.__studyData.prjOwner === osparc.auth.Data.getInstance().getEmail();
290-
}
291-
return false;
292285
}
293286
}
294287
});

services/web/client/source/class/osparc/component/node/BaseNodeView.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ qx.Class.define("osparc.component.node.BaseNodeView", {
203203
},
204204

205205
__buildToolbar: function() {
206+
const study = osparc.store.Store.getInstance().getCurrentStudy();
207+
206208
const toolbar = this.__toolbar = new qx.ui.toolbar.ToolBar();
207209
const titlePart = new qx.ui.toolbar.Part();
208210
const infoPart = new qx.ui.toolbar.Part();
@@ -222,7 +224,6 @@ qx.Class.define("osparc.component.node.BaseNodeView", {
222224
if (node) {
223225
node.renameNode(evt.getData());
224226
}
225-
const study = osparc.store.Store.getInstance().getCurrentStudy();
226227
qx.event.message.Bus.getInstance().dispatchByName("updateStudy", study.serializeStudy());
227228
}
228229
}, this);
@@ -232,8 +233,8 @@ qx.Class.define("osparc.component.node.BaseNodeView", {
232233
infoBtn.addListener("execute", () => this.__openServiceDetails(), this);
233234
infoPart.add(infoBtn);
234235

235-
if (osparc.data.Permissions.getInstance().canDo("study.node.update")) {
236-
const editAccessLevel = new qx.ui.toolbar.Button(this.tr("Edit Access Level"));
236+
if (osparc.data.Permissions.getInstance().canDo("study.node.update") && osparc.data.model.Study.isOwner(study)) {
237+
const editAccessLevel = new qx.ui.toolbar.Button(this.tr("Edit Access Level"), "@FontAwesome5Solid/edit/14");
237238
editAccessLevel.addListener("execute", () => this._openEditAccessLevel(), this);
238239
infoPart.add(editAccessLevel);
239240
}

services/web/client/source/class/osparc/dashboard/CollaboratorListItem.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ qx.Class.define("osparc.dashboard.CollaboratorListItem", {
2323
},
2424

2525
properties: {
26-
isOrganization: {
27-
check: "Boolean",
28-
event: "changeIsOrganization",
26+
collabType: {
27+
check: [0, 1, 2],
28+
event: "changeCollabType",
2929
nullable: true
3030
},
3131

@@ -76,6 +76,25 @@ qx.Class.define("osparc.dashboard.CollaboratorListItem", {
7676
return control || this.base(arguments, id);
7777
},
7878

79+
// overriden
80+
_applyThumbnail: function(value) {
81+
if (value === null) {
82+
const collabType = this.getCollabType();
83+
switch (collabType) {
84+
case 0:
85+
value = "@FontAwesome5Solid/globe/28";
86+
break;
87+
case 1:
88+
value = "@FontAwesome5Solid/users/28";
89+
break;
90+
case 2:
91+
value = "@FontAwesome5Solid/user/28";
92+
break;
93+
}
94+
}
95+
this.base(arguments, value);
96+
},
97+
7998
_applyAccessRights: function(value) {
8099
if (value === null) {
81100
return;
@@ -108,7 +127,7 @@ qx.Class.define("osparc.dashboard.CollaboratorListItem", {
108127
});
109128

110129
const accessRights = this.getAccessRights();
111-
if (!osparc.component.export.Permissions.canDelete(accessRights) && !this.getIsOrganization()) {
130+
if (!osparc.component.export.Permissions.canDelete(accessRights) && this.getCollabType() === 2) {
112131
const makeOwnerButton = new qx.ui.menu.Button(this.tr("Make Owner"));
113132
makeOwnerButton.addListener("execute", () => {
114133
this.fireDataEvent("promoteCollaborator", {

services/web/client/source/class/osparc/dashboard/ExploreBrowser.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,14 +416,14 @@ qx.Class.define("osparc.dashboard.ExploreBrowser", {
416416
if (this.self().isTemplate(study)) {
417417
item.set({
418418
uuid: study.uuid,
419-
creator: study.prjOwner ? study.prjOwner : "",
419+
owner: study.prjOwner ? study.prjOwner : "",
420420
accessRights: study.accessRights ? study.accessRights : {},
421421
icon: study.thumbnail ? study.thumbnail : "@FontAwesome5Solid/copy/50"
422422
});
423423
} else if (this.self().isService(study)) {
424424
item.set({
425425
uuid: study.key,
426-
creator: study.owner ? study.owner : "",
426+
owner: study.owner ? study.owner : "",
427427
accessRights: study.access_rights ? study.access_rights : {},
428428
icon: study.thumbnail ? study.thumbnail : "@FontAwesome5Solid/paw/50"
429429
});
@@ -673,10 +673,10 @@ qx.Class.define("osparc.dashboard.ExploreBrowser", {
673673
},
674674

675675
__isUserOwner: function(studyData) {
676-
const myEmail = osparc.auth.Data.getInstance().getEmail();
677676
if (this.self().isTemplate(studyData)) {
678-
return studyData.prjOwner === myEmail;
677+
return osparc.data.model.Study.isOwner(studyData);
679678
} else if (this.self().isService(studyData)) {
679+
const myEmail = osparc.auth.Data.getInstance().getEmail();
680680
return studyData.owner === myEmail;
681681
}
682682
return false;

services/web/client/source/class/osparc/dashboard/StudyBrowser.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
389389
uuid: study.uuid,
390390
studyTitle: study.name,
391391
studyDescription: study.description,
392-
creator: study.prjOwner ? study.prjOwner : null,
392+
owner: study.prjOwner ? study.prjOwner : null,
393393
accessRights: study.accessRights ? study.accessRights : null,
394394
lastChangeDate: study.lastChangeDate ? new Date(study.lastChangeDate) : null,
395395
icon: study.thumbnail || defaultThumbnail,
@@ -436,7 +436,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
436436
const studyServicesButton = this.__getStudyServicesMenuButton(studyData);
437437
menu.add(studyServicesButton);
438438

439-
const isCurrentUserOwner = this.__isUserOwner(studyData);
439+
const isCurrentUserOwner = osparc.data.model.Study.isOwner(studyData);
440440
const canCreateTemplate = osparc.data.Permissions.getInstance().canDo("studies.template.create");
441441
if (isCurrentUserOwner && canCreateTemplate) {
442442
const saveAsTemplateButton = this.__getSaveAsTemplateMenuButton(studyData);
@@ -658,16 +658,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
658658
__createConfirmWindow: function(isMulti) {
659659
const msg = isMulti ? this.tr("Are you sure you want to delete the studies?") : this.tr("Are you sure you want to delete the study?");
660660
return new osparc.ui.window.Confirmation(msg);
661-
},
662-
663-
__isUserOwner: function(studyData) {
664-
const myEmail = osparc.auth.Data.getInstance().getEmail();
665-
if ("prjOwner" in studyData) {
666-
return studyData.prjOwner === myEmail;
667-
} else if ("getCreator" in studyData) {
668-
return studyData.getCreator() === myEmail;
669-
}
670-
return false;
671661
}
672662
}
673663
});

services/web/client/source/class/osparc/dashboard/StudyBrowserButtonItem.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ qx.Class.define("osparc.dashboard.StudyBrowserButtonItem", {
7272
nullable: true
7373
},
7474

75-
creator: {
75+
owner: {
7676
check: "String",
77-
apply: "_applyCreator",
77+
apply: "_applyOwner",
7878
nullable: true
7979
},
8080

@@ -266,7 +266,7 @@ qx.Class.define("osparc.dashboard.StudyBrowserButtonItem", {
266266
}
267267
},
268268

269-
_applyCreator: function(value, old) {
269+
_applyOwner: function(value, old) {
270270
if (this.isResourceType("service") || this.isResourceType("template")) {
271271
const label = this.getChildControl("description2");
272272
label.setValue(value);
@@ -413,7 +413,7 @@ qx.Class.define("osparc.dashboard.StudyBrowserButtonItem", {
413413
if (text) {
414414
const checks = [
415415
this.getStudyTitle(),
416-
this.getCreator()
416+
this.getOwner()
417417
];
418418
if (checks.filter(label => label.toLowerCase().trim().includes(text)).length == 0) {
419419
return true;

services/web/client/source/class/osparc/data/Permissions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ qx.Class.define("osparc.data.Permissions", {
144144
"user.token.create",
145145
"user.token.delete",
146146
"user.tag",
147+
"user.organizations.create",
147148
"study.node.create",
148149
"study.node.delete",
149150
"study.node.update",
@@ -162,7 +163,6 @@ qx.Class.define("osparc.data.Permissions", {
162163
"studies.template.create.all",
163164
"services.all.read",
164165
"user.role.update",
165-
"user.organizations.create",
166166
"study.nodestree.uuid.read",
167167
"study.filestree.uuid.read",
168168
"study.logger.debug.read",

services/web/client/source/class/osparc/data/model/Study.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,19 @@ qx.Class.define("osparc.data.model.Study", {
188188
return true;
189189
}
190190
return false;
191+
},
192+
193+
isOwner: function(studyData) {
194+
if (studyData instanceof osparc.data.model.Study) {
195+
const myEmail = osparc.auth.Data.getInstance().getEmail();
196+
return studyData.getPrjOwner() === myEmail;
197+
}
198+
const myGid = osparc.auth.Data.getInstance().getGroupId();
199+
const aceessRights = studyData["accessRights"];
200+
if (myGid in aceessRights) {
201+
return aceessRights[myGid]["delete"];
202+
}
203+
return false;
191204
}
192205
},
193206

0 commit comments

Comments
 (0)