diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index c80231763ed..818db26de2a 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -247,6 +247,21 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/projects/{studyId}/nodes/-/services:access?for_gid={gid}" }, + postAccessRights: { + useCache: false, + method: "POST", + url: statics.API + "/projects/{studyId}/groups/{gId}" + }, + deleteAccessRights: { + useCache: false, + method: "DELETE", + url: statics.API + "/projects/{studyId}/groups/{gId}" + }, + putAccessRights: { + useCache: false, + method: "PUT", + url: statics.API + "/projects/{studyId}/groups/{gId}" + }, addTag: { useCache: false, method: "PUT", diff --git a/services/static-webserver/client/source/class/osparc/data/model/Study.js b/services/static-webserver/client/source/class/osparc/data/model/Study.js index ae4b5cf83d9..ca8a1fe474c 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Study.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Study.js @@ -209,6 +209,11 @@ qx.Class.define("osparc.data.model.Study", { "dev" ], + OwnPatch: [ + "accessRights", + "workbench" + ], + createMyNewStudyObject: function() { let myNewStudyObject = {}; const props = qx.util.PropertyUtil.getProperties(osparc.data.model.Study); @@ -578,6 +583,12 @@ qx.Class.define("osparc.data.model.Study", { }, patchStudy: function(studyChanges) { + const matches = this.self().OwnPatch.filter(el => Object.keys(studyChanges).indexOf(el) !== -1); + if (matches.length) { + console.error(matches, "has it's own PATCH path"); + return null; + } + return new Promise((resolve, reject) => { const params = { url: { diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js index 1d997439d22..72b9bb2e351 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js @@ -241,6 +241,10 @@ qx.Class.define("osparc.desktop.MainPage", { if (templateBrowser) { templateBrowser.taskToTemplateReceived(task, data["studyData"].name); } + task.addListener("resultReceived", e => { + const templateData = e.getData(); + osparc.info.StudyUtils.addCollaborators(templateData, data["accessRights"]); + }); }) .catch(errMsg => { const msg = this.tr("Something went wrong Duplicating the study
") + errMsg; diff --git a/services/static-webserver/client/source/class/osparc/info/StudyLarge.js b/services/static-webserver/client/source/class/osparc/info/StudyLarge.js index 207f6429df2..2401a87e078 100644 --- a/services/static-webserver/client/source/class/osparc/info/StudyLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/StudyLarge.js @@ -244,7 +244,9 @@ qx.Class.define("osparc.info.StudyLarge", { }, __openAccessRights: function() { - const permissionsView = osparc.info.StudyUtils.openAccessRights(this.getStudy().serialize()); + const studyData = this.getStudy().serialize(); + studyData["resourceType"] = this.__isTemplate ? "template" : "study"; + const permissionsView = osparc.info.StudyUtils.openAccessRights(studyData); permissionsView.addListener("updateAccessRights", e => { const updatedData = e.getData(); this.getStudy().setAccessRights(updatedData["accessRights"]); diff --git a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js index 14aeef50fd8..b8d79eeca46 100644 --- a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js +++ b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js @@ -392,39 +392,113 @@ qx.Class.define("osparc.info.StudyUtils", { return box; }, - patchNodeData: function(studyData, nodeId, fieldKey, value) { + patchStudyData: function(studyData, fieldKey, value) { + if (osparc.data.model.Study.OwnPatch.includes(fieldKey)) { + console.error(fieldKey, "has it's own PATCH path"); + return null; + } + const patchData = {}; patchData[fieldKey] = value; const params = { url: { - "studyId": studyData["uuid"], - "nodeId": nodeId + "studyId": studyData["uuid"] }, data: patchData }; - return osparc.data.Resources.fetch("studies", "patchNode", params) + return osparc.data.Resources.fetch("studies", "patch", params) .then(() => { - studyData["workbench"][nodeId][fieldKey] = value; + studyData[fieldKey] = value; // A bit hacky, but it's not sent back to the backend studyData["lastChangeDate"] = new Date().toISOString(); }); }, - patchStudyData: function(studyData, fieldKey, value) { + patchNodeData: function(studyData, nodeId, fieldKey, value) { const patchData = {}; patchData[fieldKey] = value; const params = { url: { - "studyId": studyData["uuid"] + "studyId": studyData["uuid"], + "nodeId": nodeId }, data: patchData }; - return osparc.data.Resources.fetch("studies", "patch", params) + return osparc.data.Resources.fetch("studies", "patchNode", params) .then(() => { - studyData[fieldKey] = value; + studyData["workbench"][nodeId][fieldKey] = value; // A bit hacky, but it's not sent back to the backend studyData["lastChangeDate"] = new Date().toISOString(); }); + }, + + addCollaborator: function(studyData, gid, permissions) { + const params = { + url: { + "studyId": studyData["uuid"], + "gId": gid + }, + data: permissions + }; + return osparc.data.Resources.fetch("studies", "postAccessRights", params) + .then(() => { + studyData["accessRights"][gid] = permissions; + studyData["lastChangeDate"] = new Date().toISOString(); + }) + .catch(err => osparc.FlashMessenger.logAs(err.message, "ERROR")); + }, + + addCollaborators: function(studyData, newCollaborators) { + const promises = []; + Object.keys(newCollaborators).forEach(gid => { + const params = { + url: { + "studyId": studyData["uuid"], + "gId": gid + }, + data: newCollaborators[gid] + }; + promises.push(osparc.data.Resources.fetch("studies", "postAccessRights", params)); + }); + return Promise.all(promises) + .then(() => { + Object.keys(newCollaborators).forEach(gid => { + studyData["accessRights"][gid] = newCollaborators[gid]; + }); + studyData["lastChangeDate"] = new Date().toISOString(); + }) + .catch(err => osparc.FlashMessenger.logAs(err.message, "ERROR")); + }, + + removeCollaborator: function(studyData, gid) { + const params = { + url: { + "studyId": studyData["uuid"], + "gId": gid + } + }; + return osparc.data.Resources.fetch("studies", "deleteAccessRights", params) + .then(() => { + delete studyData["accessRights"]["gid"]; + studyData["lastChangeDate"] = new Date().toISOString(); + }) + .catch(err => osparc.FlashMessenger.logAs(err.message, "ERROR")); + }, + + updateCollaborator: function(studyData, gid, newPermissions) { + const params = { + url: { + "studyId": studyData["uuid"], + "gId": gid + }, + data: newPermissions + }; + return osparc.data.Resources.fetch("studies", "putAccessRights", params) + .then(() => { + studyData["accessRights"][gid] = newPermissions; + studyData["lastChangeDate"] = new Date().toISOString(); + }) + .catch(err => osparc.FlashMessenger.logAs(err.message, "ERROR")); } } }); diff --git a/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js b/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js index 418e09618f9..818c4b80606 100644 --- a/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js +++ b/services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js @@ -133,88 +133,32 @@ qx.Class.define("osparc.share.CollaboratorsStudy", { return; } - const newAccessRights = this._serializedDataCopy["accessRights"]; + const newCollaborators = {}; gids.forEach(gid => { - newAccessRights[gid] = this._resourceType === "study" ? this.self().getCollaboratorAccessRight() : this.self().getViewerAccessRight(); + newCollaborators[gid] = this._resourceType === "study" ? this.self().getCollaboratorAccessRight() : this.self().getViewerAccessRight(); }); - osparc.info.StudyUtils.patchStudyData(this._serializedDataCopy, "accessRights", newAccessRights) + osparc.info.StudyUtils.addCollaborators(this._serializedDataCopy, newCollaborators) .then(() => { this.fireDataEvent("updateAccessRights", this._serializedDataCopy); const text = this.tr("User(s) successfully added."); osparc.FlashMessenger.getInstance().logAs(text); this._reloadCollaboratorsList(); + this.__pushNotifications(gids); this.__checkShareePermissions(gids); }) .catch(err => { console.error(err); osparc.FlashMessenger.getInstance().logAs(this.tr("Something went adding user(s)"), "ERROR"); }); - - // push 'STUDY_SHARED'/'TEMPLATE_SHARED' notification - osparc.store.Store.getInstance().getPotentialCollaborators() - .then(potentialCollaborators => { - gids.forEach(gid => { - if (gid in potentialCollaborators && "id" in potentialCollaborators[gid]) { - // it's a user, not an organization - const collab = potentialCollaborators[gid]; - const uid = collab["id"]; - if (this._resourceType === "study") { - osparc.notification.Notifications.postNewStudy(uid, this._serializedDataCopy["uuid"]); - } else { - osparc.notification.Notifications.postNewTemplate(uid, this._serializedDataCopy["uuid"]); - } - } - }); - }); - }, - - __checkShareePermissions: function(gids) { - if (gids.length === 0) { - return; - } - - const promises = []; - gids.forEach(gid => { - const params = { - url: { - "studyId": this._serializedDataCopy["uuid"], - "gid": gid - } - }; - promises.push(osparc.data.Resources.fetch("studies", "checkShareePermissions", params)); - }); - Promise.all(promises) - .then(values => { - const noAccessible = values.filter(value => value["accessible"] === false); - if (noAccessible.length) { - const shareePermissions = new osparc.share.ShareePermissions(noAccessible); - const win = osparc.ui.window.Window.popUpInWindow(shareePermissions, this.tr("Sharee permissions"), 500, 500, "@FontAwesome5Solid/exclamation-triangle/14").set({ - clickAwayClose: false, - resizable: true, - showClose: true - }); - win.getChildControl("icon").set({ - textColor: "warning-yellow" - }); - } - }); }, _deleteMember: function(collaborator, item) { if (item) { item.setEnabled(false); } - const success = delete this._serializedDataCopy["accessRights"][collaborator["gid"]]; - if (!success) { - osparc.FlashMessenger.getInstance().logAs(this.tr("Something went wrong removing Member"), "ERROR"); - if (item) { - item.setEnabled(true); - } - return; - } - osparc.info.StudyUtils.patchStudyData(this._serializedDataCopy, "accessRights", this._serializedDataCopy["accessRights"]) + osparc.info.StudyUtils.removeCollaborator(this._serializedDataCopy, collaborator["gid"]) .then(() => { this.fireDataEvent("updateAccessRights", this._serializedDataCopy); osparc.FlashMessenger.getInstance().logAs(this.tr("Member successfully removed")); @@ -233,8 +177,8 @@ qx.Class.define("osparc.share.CollaboratorsStudy", { __make: function(collaboratorGId, newAccessRights, successMsg, failureMsg, item) { item.setEnabled(false); - this._serializedDataCopy["accessRights"][collaboratorGId] = newAccessRights; - osparc.info.StudyUtils.patchStudyData(this._serializedDataCopy, "accessRights", this._serializedDataCopy["accessRights"]) + + osparc.info.StudyUtils.updateCollaborator(this._serializedDataCopy, collaboratorGId, newAccessRights[collaboratorGId]) .then(() => { this.fireDataEvent("updateAccessRights", this._serializedDataCopy); osparc.FlashMessenger.getInstance().logAs(successMsg); @@ -244,7 +188,11 @@ qx.Class.define("osparc.share.CollaboratorsStudy", { console.error(err); osparc.FlashMessenger.getInstance().logAs(failureMsg, "ERROR"); }) - .finally(() => item.setEnabled(true)); + .finally(() => { + if (item) { + item.setEnabled(true); + } + }); }, _promoteToEditor: function(collaborator, item) { @@ -307,6 +255,57 @@ qx.Class.define("osparc.share.CollaboratorsStudy", { this.tr(`Something went wrong changing ${osparc.data.Roles.STUDY[3].label} to ${osparc.data.Roles.STUDY[2].label}`), item ); + }, + + __pushNotifications: function(gids) { + // push 'STUDY_SHARED'/'TEMPLATE_SHARED' notification + osparc.store.Store.getInstance().getPotentialCollaborators() + .then(potentialCollaborators => { + gids.forEach(gid => { + if (gid in potentialCollaborators && "id" in potentialCollaborators[gid]) { + // it's a user, not an organization + const collab = potentialCollaborators[gid]; + const uid = collab["id"]; + if (this._resourceType === "study") { + osparc.notification.Notifications.postNewStudy(uid, this._serializedDataCopy["uuid"]); + } else { + osparc.notification.Notifications.postNewTemplate(uid, this._serializedDataCopy["uuid"]); + } + } + }); + }); + }, + + __checkShareePermissions: function(gids) { + if (gids.length === 0) { + return; + } + + const promises = []; + gids.forEach(gid => { + const params = { + url: { + "studyId": this._serializedDataCopy["uuid"], + "gid": gid + } + }; + promises.push(osparc.data.Resources.fetch("studies", "checkShareePermissions", params)); + }); + Promise.all(promises) + .then(values => { + const noAccessible = values.filter(value => value["accessible"] === false); + if (noAccessible.length) { + const shareePermissions = new osparc.share.ShareePermissions(noAccessible); + const win = osparc.ui.window.Window.popUpInWindow(shareePermissions, this.tr("Sharee permissions"), 500, 500, "@FontAwesome5Solid/exclamation-triangle/14").set({ + clickAwayClose: false, + resizable: true, + showClose: true + }); + win.getChildControl("icon").set({ + textColor: "warning-yellow" + }); + } + }); } } }); diff --git a/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js b/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js index 31e01105365..6b6dc977995 100644 --- a/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js +++ b/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js @@ -67,18 +67,21 @@ qx.Class.define("osparc.study.SaveAsTemplate", { }, __publishTemplate: function() { + // AccessRights will be POSTed after the template is created + const accessRights = {} this.__studyDataClone["accessRights"] = {}; const selectedGroupIDs = this.__shareWith.getSelectedGroups(); selectedGroupIDs.forEach(gid => { - this.__studyDataClone["accessRights"][gid] = osparc.share.CollaboratorsStudy.getViewerAccessRight(); + accessRights[gid] = osparc.share.CollaboratorsStudy.getViewerAccessRight(); }); // Make publisher owner const myGroupId = osparc.auth.Data.getInstance().getGroupId(); - this.__studyDataClone["accessRights"][myGroupId] = osparc.share.CollaboratorsStudy.getOwnerAccessRight(); + accessRights[myGroupId] = osparc.share.CollaboratorsStudy.getOwnerAccessRight(); this.fireDataEvent("publishTemplate", { "studyData": this.__studyDataClone, - "copyData": this.__copyWData.getValue() + "copyData": this.__copyWData.getValue(), + "accessRights": accessRights }); },