diff --git a/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js b/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js index 1c18a4a3aed..1cd8440fc45 100644 --- a/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js +++ b/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js @@ -99,7 +99,7 @@ qx.Class.define("osparc.auth.ui.LoginView", { this.fireEvent("toRegister"); } else if (createAccountAction === "REQUEST_ACCOUNT_FORM") { this.fireEvent("toRequestAccount"); - } else if (createAccountAction === "REQUEST_ACCOUNT_FORM") { + } else if (createAccountAction === "REQUEST_ACCOUNT_INSTRUCTIONS") { osparc.store.Support.openInvitationRequiredDialog(); } createAccountBtn.setEnabled(true); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index d6a0d76769d..9db4130e02b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -45,6 +45,8 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { events: { "folderSelected": "qx.event.type.Data", "folderUpdated": "qx.event.type.Data", + "moveFolderToFolderRequested": "qx.event.type.Data", + "moveFolderToWorkspaceRequested": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data" }, @@ -73,12 +75,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { apply: "__applyTitle" }, - description: { - check: "String", - nullable: true, - apply: "__applyDescription" - }, - lastModified: { check: "Date", nullable: true, @@ -143,7 +139,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { folder.bind("folderId", this, "folderId"); folder.bind("parentId", this, "parentFolderId"); folder.bind("name", this, "title"); - folder.bind("description", this, "description"); folder.bind("lastModified", this, "lastModified"); this.__addMenuButton(); @@ -152,11 +147,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { __applyTitle: function(value) { const label = this.getChildControl("title"); label.setValue(value); - this.__updateTooltip(); - }, - - __applyDescription: function() { - this.__updateTooltip(); }, __applyLastModified: function(value) { @@ -175,37 +165,19 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { }); const editButton = new qx.ui.menu.Button(this.tr("Rename..."), "@FontAwesome5Solid/pencil-alt/12"); - editButton.addListener("execute", () => { - const folder = this.getFolder(); - const newFolder = false; - const folderEditor = new osparc.editor.FolderEditor(newFolder).set({ - label: folder.getName(), - description: folder.getDescription() - }); - const title = this.tr("Edit Folder"); - const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 200); - folderEditor.addListener("updateFolder", () => { - const newName = folderEditor.getLabel(); - const newDescription = folderEditor.getDescription(); - const updateData = { - "name": newName, - "description": newDescription - }; - osparc.data.model.Folder.putFolder(this.getFolderId(), updateData) - .then(() => { - folder.set({ - name: newName, - description: newDescription - }); - this.fireDataEvent("folderUpdated", folder.getFolderId()); - }) - .catch(err => console.error(err)); - win.close(); - }); - folderEditor.addListener("cancel", () => win.close()); - }); + editButton.addListener("execute", () => this.__editFolder(), this); menu.add(editButton); + const moveToFolderButton = new qx.ui.menu.Button(this.tr("Move to Folder..."), "@FontAwesome5Solid/folder/12"); + moveToFolderButton.addListener("execute", () => this.fireDataEvent("moveFolderToFolderRequested", this.getFolderId()), this); + menu.add(moveToFolderButton); + + const moveToWorkspaceButton = new qx.ui.menu.Button(this.tr("Move to Workspace..."), ""); + moveToWorkspaceButton.addListener("execute", () => this.fireDataEvent("moveFolderToWorkspaceRequested", this.getFolderId()), this); + menu.add(moveToWorkspaceButton); + + menu.addSeparator(); + const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12"); deleteButton.addListener("execute", () => this.__deleteStudyRequested(), this); menu.add(deleteButton); @@ -213,13 +185,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { menuButton.setMenu(menu); }, - __updateTooltip: function() { - const toolTipText = this.getTitle() + (this.getDescription() ? "
" + this.getDescription() : ""); - this.set({ - toolTipText - }) - }, - __itemSelected: function(newVal) { if (newVal) { this.fireDataEvent("folderSelected", this.getFolderId()); @@ -227,6 +192,32 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { this.setValue(false); }, + __editFolder: function() { + const folder = this.getFolder(); + const newFolder = false; + const folderEditor = new osparc.editor.FolderEditor(newFolder).set({ + label: folder.getName(), + }); + const title = this.tr("Edit Folder"); + const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 150); + folderEditor.addListener("updateFolder", () => { + const newName = folderEditor.getLabel(); + const updateData = { + "name": newName, + }; + osparc.data.model.Folder.putFolder(this.getFolderId(), updateData) + .then(() => { + folder.set({ + name: newName, + }); + this.fireDataEvent("folderUpdated", folder.getFolderId()); + }) + .catch(err => console.error(err)); + win.close(); + }); + folderEditor.addListener("cancel", () => win.close()); + }, + __deleteStudyRequested: function() { const msg = this.tr("Are you sure you want to delete") + " " + this.getTitle() + "?"; const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js index e0b0eb1aef6..1f0e862c726 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js @@ -80,13 +80,11 @@ qx.Class.define("osparc.dashboard.FolderButtonNew", { const newFolder = true; const folderEditor = new osparc.editor.FolderEditor(newFolder); const title = this.tr("New Folder"); - const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 200); + const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 150); folderEditor.addListener("createFolder", () => { const name = folderEditor.getLabel(); - const description = folderEditor.getDescription(); this.fireDataEvent("createFolder", { name, - description }); win.close(); }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 2a597b08b92..059a01c49f2 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -18,8 +18,11 @@ qx.Class.define("osparc.dashboard.FoldersTree", { extend: qx.ui.tree.VirtualTree, - construct: function() { - const rootFolder = this.self().createNewEntry(null); + construct: function(currentWorkspaceId) { + this.__currentWorkspaceId = currentWorkspaceId; + const workspace = osparc.store.Workspaces.getWorkspace(currentWorkspaceId); + const rootLabel = workspace ? workspace.getName() : "My Workspace"; + const rootFolder = this.self().createNewEntry(rootLabel, null); const root = qx.data.marshal.Json.createModel(rootFolder, true); this.__fetchChildren(root); @@ -41,10 +44,10 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, statics: { - createNewEntry: function(folder) { + createNewEntry: function(label, folderId) { return { - folderId: folder ? folder.getFolderId() : null, - label: folder ? folder.getName() : "Home", + label, + folderId, children: [ this.self().getLoadingData() ], @@ -77,6 +80,8 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, members: { + __currentWorkspaceId:null, + __initTree: function() { const that = this; this.setDelegate({ @@ -116,11 +121,11 @@ qx.Class.define("osparc.dashboard.FoldersTree", { parentModel.setLoaded(true); const folderId = parentModel.getFolderId ? parentModel.getFolderId() : parentModel.getModel(); - osparc.store.Folders.getInstance().fetchFolders(folderId) + osparc.store.Folders.getInstance().fetchFolders(folderId, this.__currentWorkspaceId) .then(folders => { this.self().removeLoadingChild(parentModel); folders.forEach(folder => { - const folderData = this.self().createNewEntry(folder); + const folderData = this.self().createNewEntry(folder.getName(), folder.getFolderId()); const folderModel = qx.data.marshal.Json.createModel(folderData, true); parentModel.getChildren().append(folderModel); }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js similarity index 85% rename from services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js rename to services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js index f01d64f42b4..e01bc834776 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToFolder.js @@ -15,14 +15,14 @@ ************************************************************************ */ -qx.Class.define("osparc.dashboard.MoveStudyToFolder", { +qx.Class.define("osparc.dashboard.MoveResourceToFolder", { extend: qx.ui.core.Widget, - construct: function(studyData, currentFolderId) { + construct: function(currentFolderId, currentWorkspaceId) { this.base(arguments); - this.__studyData = studyData; this.__currentFolderId = currentFolderId; + this.__currentWorkspaceId = currentWorkspaceId; this._setLayout(new qx.ui.layout.VBox(10)); @@ -35,7 +35,10 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { foldersTree.addListener("selectionChanged", e => { const folderId = e.getData(); moveButton.setEnabled(this.__currentFolderId !== folderId); - moveButton.addListenerOnce("execute", () => this.fireDataEvent("moveToFolder", folderId)); + this.__selectedFolderId = folderId; + }); + moveButton.addListener("execute", () => { + this.fireDataEvent("moveToFolder", this.__selectedFolderId); }); }, @@ -45,7 +48,6 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { }, members: { - __studyData: null, __currentFolderId: null, _createChildControlImpl: function(id) { @@ -59,7 +61,7 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { break; } case "folders-tree": - control = new osparc.dashboard.FoldersTree(); + control = new osparc.dashboard.FoldersTree(this.__currentWorkspaceId); this._add(control); break; case "buttons-layout": diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js new file mode 100644 index 00000000000..56e9d5a674f --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceToWorkspace.js @@ -0,0 +1,93 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.MoveResourceToWorkspace", { + extend: qx.ui.core.Widget, + + construct: function(currentWorkspaceId) { + this.base(arguments); + + this.__currentWorkspaceId = currentWorkspaceId; + + this._setLayout(new qx.ui.layout.VBox(10)); + + this.getChildControl("current-workspace"); + const workspacesTree = this.getChildControl("workspaces-tree"); + this.getChildControl("cancel-btn"); + const moveButton = this.getChildControl("move-btn"); + + moveButton.setEnabled(false) + workspacesTree.addListener("selectionChanged", e => { + const workspaceId = e.getData(); + moveButton.setEnabled(this.__currentWorkspaceId !== workspaceId); + this.__selectedWorkspaceId = workspaceId; + }); + moveButton.addListener("execute", () => { + this.fireDataEvent("moveToWorkspace", this.__selectedWorkspaceId); + }, this); + }, + + events: { + "cancel": "qx.event.type.Event", + "moveToWorkspace": "qx.event.type.Data" + }, + + members: { + __currentWorkspaceId: null, + + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "current-workspace": { + const workspace = osparc.store.Workspaces.getWorkspace(this.__currentWorkspaceId); + const currentWorkspaceName = workspace ? workspace.getName() : "My Workspace"; + control = new qx.ui.basic.Label(this.tr("Current location: ") + currentWorkspaceName); + this._add(control); + break; + } + case "workspaces-tree": + control = new osparc.dashboard.WorkspacesTree(); + this._add(control); + break; + case "buttons-layout": + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ + alignX: "right" + })); + this._add(control); + break; + case "cancel-btn": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Cancel")).set({ + appearance: "form-button-text" + }); + control.addListener("execute", () => this.fireEvent("cancel"), this); + buttons.add(control); + break; + } + case "move-btn": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Move")).set({ + appearance: "form-button" + }); + buttons.add(control); + break; + } + } + return control || this.base(arguments, id); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index 40178ea468d..59bde762e2e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -186,6 +186,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { members: { __leftFilters: null, + _resourceFilter: null, __centerLayout: null, _resourceType: null, _resourcesList: null, @@ -252,11 +253,13 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("emptyStudyClicked", e => this._deleteResourceRequested(e.getData())); resourcesContainer.addListener("folderSelected", e => this._folderSelected(e.getData())); resourcesContainer.addListener("folderUpdated", e => this._folderUpdated(e.getData())); + resourcesContainer.addListener("moveFolderToFolderRequested", e => this._moveFolderToFolderRequested(e.getData())); + resourcesContainer.addListener("moveFolderToWorkspaceRequested", e => this._moveFolderToWorkspaceRequested(e.getData())); resourcesContainer.addListener("deleteFolderRequested", e => this._deleteFolderRequested(e.getData())); resourcesContainer.addListener("workspaceSelected", e => { const workspaceId = e.getData(); this._workspaceSelected(workspaceId); - this.__resourceFilter.workspaceSelected(workspaceId); + this._resourceFilter.workspaceSelected(workspaceId); }); resourcesContainer.addListener("workspaceUpdated", e => this._workspaceUpdated(e.getData())); resourcesContainer.addListener("deleteWorkspaceRequested", e => this._deleteWorkspaceRequested(e.getData())); @@ -355,7 +358,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, _addResourceFilter: function() { - const resourceFilter = this.__resourceFilter = new osparc.dashboard.ResourceFilter(this._resourceType).set({ + const resourceFilter = this._resourceFilter = new osparc.dashboard.ResourceFilter(this._resourceType).set({ marginTop: osparc.dashboard.SearchBarFilter.HEIGHT + 10, // aligned with toolbar buttons: search bar + spacing maxWidth: this.self().SIDE_SPACER_WIDTH, width: this.self().SIDE_SPACER_WIDTH @@ -473,6 +476,14 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, + _moveFolderToFolderRequested: function(folderId) { + throw new Error("Abstract method called!"); + }, + + _moveFolderToWorkspaceRequested: function(folderId) { + throw new Error("Abstract method called!"); + }, + _deleteFolderRequested: function(folderId) { throw new Error("Abstract method called!"); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index 84b2e680f5f..4f7363299fe 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -86,6 +86,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "changeVisibility": "qx.event.type.Data", "folderSelected": "qx.event.type.Data", "folderUpdated": "qx.event.type.Data", + "moveFolderToFolderRequested": "qx.event.type.Data", + "moveFolderToWorkspaceRequested": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data", "workspaceSelected": "qx.event.type.Data", "workspaceUpdated": "qx.event.type.Data", @@ -384,6 +386,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { [ "folderSelected", "folderUpdated", + "moveFolderToFolderRequested", + "moveFolderToWorkspaceRequested", "deleteFolderRequested", ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); return card; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js index 0a47f4e12ec..4a93a2b0d00 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js @@ -43,6 +43,8 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { members: { __resourceType: null, + __contextLayout: null, + __contextRadioGroup: null, __sharedWithButtons: null, __workspaceButtons: null, __tagButtons: null, @@ -70,9 +72,9 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { /* SHARED WITH */ __createSharedWithFilterLayout: function() { - const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); + const layout = this.__contextLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); - const radioGroup = new qx.ui.form.RadioGroup(); + const radioGroup = this.__contextRadioGroup = new qx.ui.form.RadioGroup(); radioGroup.setAllowEmptySelection(false); const options = osparc.dashboard.SearchBarFilter.getSharedWithOptions(this.__resourceType); @@ -90,7 +92,15 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { button.set({ label: this.tr("My Workspace") }); - } else { + } else if (option.id === "shared-with-me") { + button.set({ + label: this.tr("Shared") + " " + osparc.product.Utils.getStudyAlias({ + firstUpperCase: true, + plural: true, + }) + }); + } + if (option.id !== "show-all") { button.set({ marginLeft: 15 }); @@ -112,7 +122,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { }); if (this.__resourceType === "study") { - this.__addWorkspaceButtons(layout, radioGroup); + this.__addWorkspaceButtons(); } return layout; @@ -120,19 +130,30 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { /* /SHARED WITH */ /* WORKSPACES */ - __addWorkspaceButtons: function(layout, radioGroup) { - layout.add(new qx.ui.core.Spacer()); + __addWorkspaceButtons: function() { + this.__contextLayout.add(new qx.ui.core.Spacer()); const workspacesButton = new qx.ui.toolbar.RadioButton(this.tr("Shared Workspaces"), osparc.store.Workspaces.iconPath(22)); workspacesButton.workspaceId = -1; workspacesButton.set({ appearance: "filter-toggle-button" }); - layout.add(workspacesButton); - radioGroup.add(workspacesButton); + this.__contextLayout.add(workspacesButton); + this.__contextRadioGroup.add(workspacesButton); workspacesButton.addListener("execute", () => { this.fireDataEvent("changeWorkspace", workspacesButton.workspaceId); }); + this.reloadWorkspaceButtons(); + }, + + reloadWorkspaceButtons: function() { + // remove first the workspaces + for (let i=this.__workspaceButtons.length-1; i >= 0; i--) { + const workspaceButton = this.__workspaceButtons[i]; + this.__contextLayout.remove(workspaceButton); + this.__contextRadioGroup.remove(workspaceButton); + } + this.__workspaceButtons = []; osparc.store.Workspaces.fetchWorkspaces() .then(workspaces => { workspaces.forEach(workspace => { @@ -143,8 +164,8 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { appearance: "filter-toggle-button", marginLeft: 15, }); - layout.add(workspaceButton); - radioGroup.add(workspaceButton); + this.__contextLayout.add(workspaceButton); + this.__contextRadioGroup.add(workspaceButton); workspaceButton.addListener("execute", () => { this.fireDataEvent("changeWorkspace", workspaceButton.workspaceId); }, this); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index e9b61fadca7..1e288034fbc 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -102,19 +102,14 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, members: { + __currentRequest: null, __workspacesList: null, __foldersList: null, // overridden initResources: function() { this._resourcesList = []; - const promises = [ - this.__getActiveStudy() - ]; - if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { - promises.push(osparc.store.Folders.getInstance().fetchFolders()); - } - Promise.all(promises) + this.__getActiveStudy() .then(() => { this.getChildControl("resources-layout"); this.__attachEventHandlers(); @@ -175,70 +170,83 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __reloadFolders: function() { - const folders = osparc.store.Folders.getInstance().getFolders(this.getCurrentFolderId()) - this.__setFoldersToList(folders); + if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { + const folderId = this.getCurrentFolderId(); + const workspaceId = this.getCurrentWorkspaceId(); + if (workspaceId === -1) { + return; + } + osparc.store.Folders.getInstance().fetchFolders(folderId, workspaceId) + .then(folders => { + this.__setFoldersToList(folders); + }) + .catch(console.error); + } }, __reloadStudies: function() { if (this._loadingResourcesBtn.isFetching()) { return; } + const workspaceId = this.getCurrentWorkspaceId(); + if (workspaceId === -1) { + return; + } + osparc.data.Resources.get("tasks") .then(tasks => { if (tasks && tasks.length) { this.__tasksReceived(tasks); } }); + + // Show "Contact Us" message if services.length === 0 + // Most probably is a product-stranger user (it can also be that the catalog is down) + osparc.store.Services.getServicesLatest() + .then(services => { + if (Object.keys(services).length === 0) { + const noAccessText = new qx.ui.basic.Label().set({ + selectable: true, + rich: true, + font: "text-18", + paddingTop: 20 + }); + let msg = this.tr("It seems you don't have access to this product."); + msg += "
"; + msg += "
"; + msg += this.tr("Please contact us:"); + msg += "
"; + const supportEmail = osparc.store.VendorInfo.getInstance().getSupportEmail(); + noAccessText.setValue(msg + supportEmail); + this._addToLayout(noAccessText); + } + }); + this._loadingResourcesBtn.setFetching(true); this._loadingResourcesBtn.setVisibility("visible"); - const request = this.__getNextRequest(); - request + if (this.__currentRequest) { + // cancel currentRequest + } + const currentRequest = this.__currentRequest = this.__getNextStudiesRequest(); + currentRequest .then(resp => { const studies = resp["data"]; this._resourcesContainer.getFlatList().nextRequest = resp["_links"]["next"]; this.__addStudiesToList(studies); - // Show "Contact Us" message if studies.length === 0 && templates.length === 0 && services.length === 0 - // Most probably is a product-stranger user (it can also be that the catalog is down) - const nStudies = "_meta" in resp ? resp["_meta"]["total"] : 0; - if (nStudies === 0) { - const promises = [ - osparc.store.Store.getInstance().getTemplates(), - osparc.store.Services.getServicesLatest(), - ]; - if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { - promises.push(osparc.store.Folders.getInstance().fetchFolders()); - } - Promise.all(promises).then(values => { - const templates = values[0]; - const services = values[1]; - if (templates.length === 0 && Object.keys(services).length === 0) { - const noAccessText = new qx.ui.basic.Label().set({ - selectable: true, - rich: true, - font: "text-18", - paddingTop: 20 - }); - let msg = this.tr("It seems you don't have access to this product."); - msg += "
"; - msg += "
"; - msg += this.tr("Please contact us:"); - msg += "
"; - const supportEmail = osparc.store.VendorInfo.getInstance().getSupportEmail(); - noAccessText.setValue(msg + supportEmail); - this._addAt(noAccessText, 2); - } - }); - } - - // Show Quick Start if studies.length === 0 + // Show Quick Start if there are no studies in the root folder of the personal workspace const quickStart = osparc.product.quickStart.Utils.getQuickStart(); if (quickStart) { const dontShow = osparc.utils.Utils.localCache.getLocalStorageItem(quickStart.localStorageStr); if (dontShow === "true") { return; } - if (nStudies === 0) { + const nStudies = "_meta" in resp ? resp["_meta"]["total"] : 0; + if ( + nStudies === 0 && + this.getWorkspaceId() === null && + this.getCurrentFolderId() === null + ) { const tutorialWindow = quickStart.tutorial(); tutorialWindow.center(); tutorialWindow.open(); @@ -247,6 +255,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }) .catch(err => console.error(err)) .finally(() => { + this.__currentRequest = null; this._loadingResourcesBtn.setFetching(false); this._loadingResourcesBtn.setVisibility(this._resourcesContainer.getFlatList().nextRequest === null ? "excluded" : "visible"); this._moreResourcesRequired(); @@ -364,7 +373,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourcesContainer.setResourcesToList(this._resourcesList); const cards = this._resourcesContainer.reloadCards("studies"); - this.__configureCards(cards); + this.__configureStudyCards(cards); this.__addNewStudyButtons(); @@ -382,7 +391,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _reloadNewCards: function() { this._resourcesContainer.setResourcesToList(this._resourcesList); const cards = this._resourcesContainer.reloadNewCards(); - this.__configureCards(cards); + this.__configureStudyCards(cards); osparc.filter.UIFilterController.dispatch("searchBarFilter"); }, @@ -412,8 +421,15 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const newWorkspaceCard = new osparc.dashboard.WorkspaceButtonNew(); newWorkspaceCard.setCardKey("new-workspace"); newWorkspaceCard.subscribeToFilterGroup("searchBarFilter"); - newWorkspaceCard.addListener("createWorkspace", () => this.__reloadWorkspaces()); - newWorkspaceCard.addListener("updateWorkspace", () => this.__reloadWorkspaces()); + [ + "createWorkspace", + "updateWorkspace" + ].forEach(e => { + newWorkspaceCard.addListener(e, () => { + this._resourceFilter.reloadWorkspaceButtons(); + this.__reloadWorkspaces(); + }); + }); this._resourcesContainer.addNewWorkspaceCard(newWorkspaceCard); }, @@ -427,15 +443,23 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _deleteWorkspaceRequested: function(workspaceId) { osparc.store.Workspaces.deleteWorkspace(workspaceId) - .then(() => this.__reloadWorkspaceCards()) + .then(() => { + this._resourceFilter.reloadWorkspaceButtons(); + this.__reloadWorkspaces(); + }) .catch(err => console.error(err)); }, // /WORKSPACES // FOLDERS - __applyCurrentFolderId: function(currentFolderId) { + __applyCurrentFolderId: function() { if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { - osparc.store.Folders.getInstance().fetchFolders(currentFolderId) + const folderId = this.getCurrentFolderId(); + const workspaceId = this.getCurrentWorkspaceId(); + if (workspaceId === -1) { + return; + } + osparc.store.Folders.getInstance().fetchFolders(folderId, workspaceId) .then(() => { this._resourcesContainer.setResourcesToList([]); this._resourcesList = []; @@ -458,14 +482,21 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { newFolderCard.subscribeToFilterGroup("searchBarFilter"); newFolderCard.addListener("createFolder", e => { const data = e.getData(); - osparc.store.Folders.getInstance().postFolder(data.name, data.description, currentFolder ? currentFolder.getFolderId() : null) - .then(() => this.__reloadFolders()) - .catch(err => console.error(err)); - }) + this.__createFolder(data); + }, this); this._resourcesContainer.addNewFolderCard(newFolderCard); } }, + __createFolder: function(data) { + const currentFolder = osparc.store.Folders.getInstance().getFolder(this.getCurrentFolderId()) + const parentFolderId = currentFolder ? currentFolder.getFolderId() : null; + const currentWorkspaceId = this.getCurrentWorkspaceId(); + osparc.store.Folders.getInstance().postFolder(data.name, parentFolderId, currentWorkspaceId) + .then(() => this.__reloadFolders()) + .catch(err => console.error(err)); + }, + _folderSelected: function(folderId) { this.setCurrentFolderId(folderId); }, @@ -474,6 +505,48 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__reloadFolders(); }, + _moveFolderToFolderRequested: function(folderId) { + const moveFolderToFolder = new osparc.dashboard.MoveResourceToFolder(this.getCurrentFolderId(), this.getCurrentWorkspaceId()); + const title = "Move to Folder"; + const win = osparc.ui.window.Window.popUpInWindow(moveFolderToFolder, title, 350, 280); + moveFolderToFolder.addListener("moveToFolder", e => { + win.close(); + const destFolderId = e.getData(); + const params = { + url: { + folderId, + destFolderId, + } + }; + osparc.data.Resources.fetch("folders", "moveToFolder", params) + .then(() => { + this.__reloadFolders() + }) + .catch(err => console.error(err)); + }); + }, + + _moveFolderToWorkspaceRequested: function(folderId) { + const moveFolderToWorkspace = new osparc.dashboard.MoveResourceToWorkspace(this.getCurrentWorkspaceId()); + const title = "Move to Workspace"; + const win = osparc.ui.window.Window.popUpInWindow(moveFolderToWorkspace, title, 350, 280); + moveFolderToWorkspace.addListener("moveToWorkspace", e => { + win.close(); + const destWorkspaceId = e.getData(); + const params = { + url: { + folderId, + workspaceId: destWorkspaceId, + } + }; + osparc.data.Resources.fetch("folders", "moveToWorkspace", params) + .then(() => { + this.__reloadFolders() + }) + .catch(err => console.error(err)); + }); + }, + _deleteFolderRequested: function(folderId) { osparc.store.Folders.getInstance().deleteFolder(folderId) .then(() => this.__reloadFolders()) @@ -481,7 +554,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, // /FOLDERS - __configureCards: function(cards) { + __configureStudyCards: function(cards) { cards.forEach(card => { card.setMultiSelectionMode(this.getMultiSelection()); card.addListener("tap", e => { @@ -559,7 +632,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return null; }, - __getNextRequest: function() { + __getNextStudiesRequest: function() { const params = { url: { offset: 0, @@ -576,14 +649,14 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { resolveWResponse: true }; - const currentFolderId = this.getCurrentFolderId(); - params.url.folderId = currentFolderId; + params.url.workspaceId = this.getCurrentWorkspaceId(); + params.url.folderId = this.getCurrentFolderId(); if (params.url.orderBy) { - return osparc.data.Resources.fetch("studies", "getPageFolderSortBy", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageSortBy", params, undefined, options); } else if (params.url.search) { - return osparc.data.Resources.fetch("studies", "getPageFolderSearch", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageSearch", params, undefined, options); } - return osparc.data.Resources.fetch("studies", "getPageFolder", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPage", params, undefined, options); }, __getTextFilteredNextRequest: function(text) { @@ -603,9 +676,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { resolveWResponse: true }; - const currentFolderId = this.getCurrentFolderId(); - params.url.folderId = currentFolderId; - return osparc.data.Resources.fetch("studies", "getPageFolderSearch", params, undefined, options); + params.url.workspaceId = this.getCurrentWorkspaceId(); + params.url.folderId = this.getCurrentFolderId(); + return osparc.data.Resources.fetch("studies", "getPageSearch", params, undefined, options); }, __getSortedByNextRequest: function() { @@ -625,9 +698,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { resolveWResponse: true }; - const currentFolderId = this.getCurrentFolderId(); - params.url.folderId = currentFolderId; - return osparc.data.Resources.fetch("studies", "getPageFolderSortBy", params, undefined, options); + params.url.workspaceId = this.getCurrentWorkspaceId(); + params.url.folderId = this.getCurrentFolderId(); + return osparc.data.Resources.fetch("studies", "getPageSortBy", params, undefined, options); }, invalidateStudies: function() { @@ -774,8 +847,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const containerHeader = this._resourcesContainer.getContainerHeader(); if (containerHeader) { - this.bind("currentWorkspaceId", containerHeader, "currentWorkspaceId"); - containerHeader.bind("currentWorkspaceId", this, "currentWorkspaceId"); + this.bind("currentWorkspaceId", containerHeader, "currentWorkspaceId", { + onUpdate: () => containerHeader.setCurrentFolderId(null) + }); + containerHeader.bind("currentWorkspaceId", this, "currentWorkspaceId", { + onUpdate: () => this.setCurrentFolderId(null) + }); this.bind("currentFolderId", containerHeader, "currentFolderId"); containerHeader.bind("currentFolderId", this, "currentFolderId"); } @@ -981,10 +1058,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __newStudyBtnClicked: function(button) { button.setValue(false); - const minStudyData = osparc.data.model.Study.createMyNewStudyObject(); + const minStudyData = osparc.data.model.Study.createMinStudyObject(); const title = osparc.utils.Utils.getUniqueStudyName(minStudyData.name, this._resourcesList); minStudyData["name"] = title; - minStudyData["description"] = ""; + minStudyData["workspaceId"] = this.getCurrentWorkspaceId(); + minStudyData["folderId"] = this.getCurrentFolderId(); this._showLoadingPage(this.tr("Creating ") + (minStudyData.name || osparc.product.Utils.getStudyAlias())); const params = { data: minStudyData @@ -1004,7 +1082,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const title = osparc.utils.Utils.getUniqueStudyName(newStudyName, this._resourcesList); templateCopyData.name = title; this._showLoadingPage(this.tr("Creating ") + (newStudyName || osparc.product.Utils.getStudyAlias())); - osparc.study.Utils.createStudyFromTemplate(templateCopyData, this._loadingPage) + const contextProps = { + workspaceId: this.getCurrentWorkspaceId(), + folderId: this.getCurrentFolderId(), + }; + osparc.study.Utils.createStudyFromTemplate(templateCopyData, this._loadingPage, contextProps) .then(studyId => this.__startStudyAfterCreating(studyId)) .catch(err => { this._hideLoadingPage(); @@ -1016,7 +1098,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __newStudyFromServiceBtnClicked: function(button, key, version, newStudyLabel) { button.setValue(false); this._showLoadingPage(this.tr("Creating ") + osparc.product.Utils.getStudyAlias()); - osparc.study.Utils.createStudyFromService(key, version, this._resourcesList, newStudyLabel) + const contextProps = { + workspaceId: this.getCurrentWorkspaceId(), + folderId: this.getCurrentFolderId(), + }; + osparc.study.Utils.createStudyFromService(key, version, this._resourcesList, newStudyLabel, contextProps) .then(studyId => this.__startStudyAfterCreating(studyId)) .catch(err => { this._hideLoadingPage(); @@ -1026,9 +1112,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __startStudyAfterCreating: function(studyId) { - if (this.getCurrentFolderId()) { - this.__moveStudyToFolder(studyId, this.getCurrentFolderId()); - } const openCB = () => this._hideLoadingPage(); const cancelCB = () => { this._hideLoadingPage(); @@ -1043,16 +1126,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._startStudyById(studyId, openCB, cancelCB, isStudyCreation); }, - __moveStudyToFolder: function(studyId, folderId) { - const params = { - url: { - studyId, - folderId - } - }; - return osparc.data.Resources.fetch("studies", "moveToFolder", params); - }, - _updateStudyData: function(studyData) { studyData["resourceType"] = "study"; const studies = this._resourcesList; @@ -1106,7 +1179,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { menu.addSeparator(); - if (writeAccess) { + // Access Rights are set at workspace level) + if (writeAccess && this.getCurrentWorkspaceId() === null) { const shareButton = this._getShareMenuButton(card); if (shareButton) { menu.add(shareButton); @@ -1129,10 +1203,17 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } if (writeAccess && osparc.utils.DisabledPlugins.isFoldersEnabled()) { - const moveToFolderButton = this.__getMoveToFolderMenuButton(studyData); + menu.addSeparator(); + + const moveToFolderButton = this.__getMoveStudyToFolderMenuButton(studyData); if (moveToFolderButton) { menu.add(moveToFolderButton); } + + const moveToWorkspaceButton = this.__getMoveStudyToWorkspaceMenuButton(studyData); + if (moveToWorkspaceButton) { + menu.add(moveToWorkspaceButton); + } } if (deleteAccess) { @@ -1217,35 +1298,68 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return studyBillingSettingsButton; }, - __getMoveToFolderMenuButton: function(studyData) { - const text = osparc.utils.Utils.capitalize(this.tr("Move to Folder...")); + __getMoveStudyToFolderMenuButton: function(studyData) { + const text = this.tr("Move to Folder..."); const moveToFolderButton = new qx.ui.menu.Button(text, "@FontAwesome5Solid/folder/12"); moveToFolderButton["moveToFolderButton"] = true; moveToFolderButton.addListener("tap", () => { - if (Object.keys(studyData["accessRights"]).length > 1) { - osparc.FlashMessenger.getInstance().logAs(this.tr("Shared projects can't be moved yet"), "WARNING"); - } else { - const title = this.tr("Move") + " " + studyData["name"]; - const moveStudyToFolder = new osparc.dashboard.MoveStudyToFolder(studyData, this.getCurrentFolderId()); - const win = osparc.ui.window.Window.popUpInWindow(moveStudyToFolder, title, 350, 280); - moveStudyToFolder.addListener("moveToFolder", e => { - win.close(); - const folderId = e.getData(); - this.__moveStudyToFolder(studyData["uuid"], folderId) - .then(() => { - this.__removeFromStudyList(studyData["uuid"]); - }) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); - }, this); - moveStudyToFolder.addListener("cancel", () => win.close()); - } + const title = this.tr("Move") + " " + studyData["name"]; + const moveStudyToFolder = new osparc.dashboard.MoveResourceToFolder(this.getCurrentFolderId(), this.getCurrentWorkspaceId()); + const win = osparc.ui.window.Window.popUpInWindow(moveStudyToFolder, title, 350, 280); + moveStudyToFolder.addListener("moveToFolder", e => { + win.close(); + const destFolderId = e.getData(); + const params = { + url: { + studyId: studyData["uuid"], + folderId: destFolderId, + } + }; + osparc.data.Resources.fetch("studies", "moveToFolder", params) + .then(() => { + this.__removeFromStudyList(studyData["uuid"]); + }) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); + }, this); + moveStudyToFolder.addListener("cancel", () => win.close()); }, this); return moveToFolderButton; }, + __getMoveStudyToWorkspaceMenuButton: function(studyData) { + const text = this.tr("Move to Workspace..."); + const moveToWorkspaceButton = new qx.ui.menu.Button(text, osparc.store.Workspaces.iconPath(14)); + moveToWorkspaceButton["moveToWorkspaceButton"] = true; + moveToWorkspaceButton.addListener("tap", () => { + const title = this.tr("Move") + " " + studyData["name"]; + const moveStudyToWorkspace = new osparc.dashboard.MoveResourceToWorkspace(this.getCurrentFolderId()); + const win = osparc.ui.window.Window.popUpInWindow(moveStudyToWorkspace, title, 350, 280); + moveStudyToWorkspace.addListener("moveToWorkspace", e => { + win.close(); + const destWorkspaceId = e.getData(); + const params = { + url: { + studyId: studyData["uuid"], + workspaceId: destWorkspaceId, + } + }; + osparc.data.Resources.fetch("studies", "moveToWorkspace", params) + .then(() => { + this.__removeFromStudyList(studyData["uuid"]); + }) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); + }, this); + moveStudyToWorkspace.addListener("cancel", () => win.close()); + }, this); + return moveToWorkspaceButton; + }, + __getDuplicateMenuButton: function(studyData) { const duplicateButton = new qx.ui.menu.Button(this.tr("Duplicate"), "@FontAwesome5Solid/copy/12"); duplicateButton["duplicateButton"] = true; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js index fbcf92b3bf7..844004de698 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js @@ -192,14 +192,18 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonBase", { // overridden _applyIcon: function(value) { + const image = this.getChildControl("icon").getChildControl("image"); if ( value.includes("@FontAwesome5Solid/") || value.includes("@MaterialIcons/") ) { + this.getContentElement().setStyles({ + "background-image": "none", + }); value += this.self().ICON_SIZE; - const image = this.getChildControl("icon").getChildControl("image"); image.set({ - source: value + source: value, + visibility: "visible", }); } else { this.getContentElement().setStyles({ @@ -209,6 +213,9 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonBase", { "background-position": "center center", "background-origin": "border-box" }); + image.set({ + visibility: "excluded", + }); } }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js index 176d2775f46..2b49a333b8a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js @@ -85,10 +85,10 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { apply: "__applyAccessRights" }, - lastModified: { + modifiedAt: { check: "Date", nullable: true, - apply: "__applyLastModified" + apply: "__applyModifiedAt" } }, @@ -157,7 +157,7 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { converter: thumbnail => thumbnail ? thumbnail : osparc.store.Workspaces.iconPath(-1) }); workspace.bind("accessRights", this, "accessRights"); - workspace.bind("lastModified", this, "lastModified"); + workspace.bind("modifiedAt", this, "modifiedAt"); workspace.bind("myAccessRights", this, "myAccessRights"); }, @@ -180,29 +180,28 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { position: "bottom-right" }); - const editButton = new qx.ui.menu.Button(this.tr("Rename..."), "@FontAwesome5Solid/pencil-alt/12"); + const editButton = new qx.ui.menu.Button(this.tr("Edit..."), "@FontAwesome5Solid/pencil-alt/12"); editButton.addListener("execute", () => { const workspace = this.getWorkspace(); const newWorkspace = false; const workspaceEditor = new osparc.editor.WorkspaceEditor(newWorkspace).set({ label: workspace.getName(), - description: workspace.getDescription() + description: workspace.getDescription(), + thumbnail: workspace.getThumbnail(), }); const title = this.tr("Edit Workspace"); const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 200); workspaceEditor.addListener("updateWorkspace", () => { const newName = workspaceEditor.getLabel(); const newDescription = workspaceEditor.getDescription(); + const newThumbnail = workspaceEditor.getThumbnail(); const updateData = { "name": newName, - "description": newDescription + "description": newDescription, + "thumbnail": newThumbnail, }; - osparc.data.model.Workspace.putWorkspace(this.getWorkspaceId(), updateData) + osparc.store.Workspaces.putWorkspace(this.getWorkspaceId(), updateData) .then(() => { - workspace.set({ - name: newName, - description: newDescription - }); this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId()); }) .catch(err => console.error(err)); @@ -238,7 +237,7 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { } }, - __applyLastModified: function(value) { + __applyModifiedAt: function(value) { const label = this.getChildControl("modified-text"); label.setValue(osparc.utils.Utils.formatDateAndTime(value)); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceTreeItem.js new file mode 100644 index 00000000000..f6959bb446b --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceTreeItem.js @@ -0,0 +1,41 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.WorkspaceTreeItem", { + extend: qx.ui.tree.VirtualTreeItem, + + members: { + _addWidgets: function() { + this.addSpacer(); + // this.addOpenButton(); + const openButton = this.getChildControl("open"); + openButton.addListener("changeOpen", () => { + console.log("changeOpen", this); + }, this); + openButton.addListener("changeVisibility", e => { + // console.log("changeVisibility", this.getLabel() + e.getData(), this); + openButton.show(); + }, this); + this._add(openButton); + this.addIcon(); + const label = this.getChildControl("label"); + this._add(label, { + flex: 1 + }); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js new file mode 100644 index 00000000000..0c0c2ce078e --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesTree.js @@ -0,0 +1,127 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.WorkspacesTree", { + extend: qx.ui.tree.VirtualTree, + + construct: function() { + const rootLabel = "Root"; + const rootWorkspace = this.self().createNewEntry(rootLabel, null); + const root = qx.data.marshal.Json.createModel(rootWorkspace, true); + this.__fetchChildren(root); + + this.base(arguments, root, "label", "children"); + + this.set({ + openMode: "dbltap", + decorator: "no-border", + font: "text-14", + showLeafs: true, + paddingLeft: -10, + hideRoot: true + }); + + this.__initTree(); + }, + + events: { + "selectionChanged": "qx.event.type.Event" // tap + }, + + statics: { + createNewEntry: function(label, workspaceId) { + return { + label, + workspaceId, + children: [ + this.self().getLoadingData() + ], + loaded: false, + }; + }, + + getLoadingData: function() { + return { + workspaceId: -1, + label: "Loading...", + children: [], + icon: "@FontAwesome5Solid/circle-notch/12", + loaded: false, + }; + }, + + addLoadingChild: function(parentModel) { + const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); + parentModel.getChildren().append(loadingModel); + }, + + removeLoadingChild: function(parent) { + for (let i = parent.getChildren().getLength() - 1; i >= 0; i--) { + if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { + parent.getChildren().splice(i, 1); + } + } + } + }, + + members: { + __currentWorkspaceId:null, + + __initTree: function() { + this.setDelegate({ + createItem: () => new osparc.dashboard.WorkspaceTreeItem(), + bindItem: (c, item, id) => { + c.bindDefaultProperties(item, id); + c.bindProperty("workspaceId", "model", null, item, id); + }, + configureItem: item => { + item.addListener("tap", () => this.fireDataEvent("selectionChanged", item.getModel()), this); + }, + sorter: (a, b) => { + const aLabel = a.getLabel(); + if (aLabel === -1) { + return 1; + } + const bLabel = b.getLabel(); + if (bLabel === -1) { + return -1; + } + return aLabel - bLabel; + } + }); + }, + + __fetchChildren: function(parentModel) { + parentModel.setLoaded(true); + + const myWorkspaceData = this.self().createNewEntry("My Workspace", null); + const myWorkspaceModel = qx.data.marshal.Json.createModel(myWorkspaceData, true); + parentModel.getChildren().append(myWorkspaceModel); + + osparc.store.Workspaces.fetchWorkspaces() + .then(workspaces => { + this.self().removeLoadingChild(parentModel); + workspaces.forEach(workspace => { + const workspaceData = this.self().createNewEntry(workspace.getName(), workspace.getWorkspaceId()); + const workspaceModel = qx.data.marshal.Json.createModel(workspaceData, true); + parentModel.getChildren().append(workspaceModel); + }); + }) + .catch(console.error); + } + } +}); 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 098d3239bfa..4516598e2dd 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -119,19 +119,19 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/projects?type=user" }, - getPageFolder: { + getPage: { method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}" + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&workspace_id={workspaceId}&folder_id={folderId}" }, - getPageFolderSearch: { + getPageSearch: { useCache: false, method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}&search={text}" + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&workspace_id={workspaceId}&folder_id={folderId}&search={text}" }, - getPageFolderSortBy: { + getPageSortBy: { useCache: false, method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}&order_by={orderBy}" + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&workspace_id={workspaceId}&folder_id={folderId}&order_by={orderBy}" }, getOne: { useCache: false, @@ -276,7 +276,11 @@ qx.Class.define("osparc.data.Resources", { moveToFolder: { method: "PUT", url: statics.API + "/projects/{studyId}/folders/{folderId}" - } + }, + moveToWorkspace: { + method: "PUT", + url: statics.API + "/projects/{studyId}/workspaces/{workspaceId}" + }, } }, "studyComments": { @@ -299,7 +303,7 @@ qx.Class.define("osparc.data.Resources", { endpoints: { getPage: { method: "GET", - url: statics.API + "/folders?folder_id={folderId}&offset={offset}&limit={limit}" + url: statics.API + "/folders?workspace_id={workspaceId}&folder_id={folderId}&offset={offset}&limit={limit}" }, getOne: { method: "GET", @@ -316,14 +320,22 @@ qx.Class.define("osparc.data.Resources", { delete: { method: "DELETE", url: statics.API + "/folders/{folderId}" - } + }, + moveToFolder: { + method: "PUT", + url: statics.API + "/folders/{folderId}/folders/{destFolderId}" + }, + moveToWorkspace: { + method: "PUT", + url: statics.API + "/folders/{folderId}/folders/{workspaceId}" + }, } }, "workspaces": { endpoints: { getPage: { method: "GET", - url: statics.API + "/workspaces?workspace_id={workspaceId}&offset={offset}&limit={limit}" + url: statics.API + "/workspaces?&offset={offset}&limit={limit}" }, getOne: { method: "GET", diff --git a/services/static-webserver/client/source/class/osparc/data/model/Folder.js b/services/static-webserver/client/source/class/osparc/data/model/Folder.js index a3e1eb7ca90..d9d2b517212 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Folder.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Folder.js @@ -32,9 +32,7 @@ qx.Class.define("osparc.data.model.Folder", { folderId: folderData.folderId, parentId: folderData.parentFolderId, name: folderData.name, - description: folderData.description, myAccessRights: folderData.myAccessRights, - accessRights: folderData.accessRights, owner: folderData.owner, createdAt: new Date(folderData.createdAt), lastModified: new Date(folderData.modifiedAt), @@ -63,13 +61,6 @@ qx.Class.define("osparc.data.model.Folder", { event: "changeName" }, - description: { - check: "String", - nullable: true, - init: null, - event: "changeDescription" - }, - myAccessRights: { check: "Object", nullable: false, @@ -77,13 +68,6 @@ qx.Class.define("osparc.data.model.Folder", { event: "changeMyAccessRights" }, - accessRights: { - check: "Object", - nullable: false, - init: null, - event: "changeAccessRights" - }, - owner: { check: "Number", nullable: true, 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 8fd40a67a28..0df98435304 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 @@ -214,7 +214,7 @@ qx.Class.define("osparc.data.model.Study", { "workbench" ], - createMyNewStudyObject: function() { + createMinStudyObject: function() { let myNewStudyObject = {}; const props = qx.util.PropertyUtil.getProperties(osparc.data.model.Study); for (let key in props) { diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workspace.js b/services/static-webserver/client/source/class/osparc/data/model/Workspace.js index ea4c324e9aa..56023d1eb4e 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Workspace.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Workspace.js @@ -36,7 +36,7 @@ qx.Class.define("osparc.data.model.Workspace", { myAccessRights: workspaceData.myAccessRights, accessRights: workspaceData.accessRights, createdAt: new Date(workspaceData.createdAt), - lastModified: new Date(workspaceData.lastModified), + modifiedAt: new Date(workspaceData.modifiedAt), }); }, @@ -90,19 +90,15 @@ qx.Class.define("osparc.data.model.Workspace", { event: "changeCreatedAt" }, - lastModified: { + modifiedAt: { check: "Date", nullable: true, init: null, - event: "changeLastModified" + event: "changeModifiedAt" } }, statics: { - putWorkspace: function(workspaceId, propKey, value) { - return osparc.store.Workspaces.putWorkspace(workspaceId, propKey, value); - }, - getProperties: function() { return Object.keys(qx.util.PropertyUtil.getProperties(osparc.data.model.Workspace)); } diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js index 611f1bd77bc..935c9cfc35b 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js @@ -25,7 +25,7 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { const studiesLabel = osparc.product.Utils.getStudyAlias({plural: true}); const msg = this.tr("\ - An organization is a group of users who can share ") + studiesLabel + this.tr(".
\ + An organization is a group of users who can share ") + studiesLabel + this.tr(" and other resources.
\ Here you can see the list of organizations you belong to, create new organizations, \ or manage the membership by setting up the access rights of each member in the organization \ if you are a manager or administrator."); diff --git a/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js b/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js index 5080b96b9be..1948026e352 100644 --- a/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js +++ b/services/static-webserver/client/source/class/osparc/editor/FolderEditor.js @@ -27,32 +27,17 @@ qx.Class.define("osparc.editor.FolderEditor", { const title = this.getChildControl("title"); title.setRequired(true); manager.add(title); - this.getChildControl("description"); newFolder ? this.getChildControl("create") : this.getChildControl("save"); this.addListener("appear", this.__onAppear, this); }, properties: { - gid: { - check: "Number", - init: 0, - nullable: false, - event: "changeGid" - }, - label: { check: "String", init: "", nullable: false, event: "changeLabel" - }, - - description: { - check: "String", - init: "", - nullable: false, - event: "changeDescription" } }, @@ -78,19 +63,6 @@ qx.Class.define("osparc.editor.FolderEditor", { this._add(control); break; } - case "description": { - control = new qx.ui.form.TextArea().set({ - font: "text-14", - placeholder: this.tr("Description"), - autoSize: true, - minHeight: 70, - maxHeight: 140 - }); - this.bind("description", control, "value"); - control.bind("value", this, "description"); - this._add(control); - break; - } case "create": { const buttons = this.getChildControl("buttonsLayout"); control = new osparc.ui.form.FetchButton(this.tr("Create")).set({ diff --git a/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js b/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js index 90ccbd2171a..31d3aef3512 100644 --- a/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js +++ b/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js @@ -26,7 +26,13 @@ qx.Class.define("osparc.filter.CollaboratorToggleButton", { let label = null; if (collaborator["first_name"]) { // user - label = `${collaborator["first_name"]} ${"last_name" in collaborator && collaborator["last_name"] != null ? collaborator["last_name"] : ""}`; + label = collaborator["first_name"]; + if (collaborator["last_name"]) { + label += ` ${collaborator["last_name"]}`; + } + if (collaborator["login"]) { + label += ` (${collaborator["login"]})`; + } } else if ("login" in collaborator) { label = collaborator["login"]; } else { @@ -35,7 +41,7 @@ qx.Class.define("osparc.filter.CollaboratorToggleButton", { } this.setLabel(label); - if ("login" in collaborator) { + if (collaborator["login"]) { this.setToolTipText(collaborator["login"]); } diff --git a/services/static-webserver/client/source/class/osparc/share/Collaborators.js b/services/static-webserver/client/source/class/osparc/share/Collaborators.js index c315c7d26d3..9c282f858ed 100644 --- a/services/static-webserver/client/source/class/osparc/share/Collaborators.js +++ b/services/static-webserver/client/source/class/osparc/share/Collaborators.js @@ -189,7 +189,11 @@ qx.Class.define("osparc.share.Collaborators", { return control || this.base(arguments, id); }, - __amIOwner: function() { + __canIChangePermissions: function() { + if (this._resourceType === "study" && this._serializedDataCopy["workspaceId"]) { + // Access Rights are set at workspace level + return false; + } let fullOptions = false; switch (this._resourceType) { case "study": @@ -223,7 +227,7 @@ qx.Class.define("osparc.share.Collaborators", { }, __buildLayout: function() { - if (this.__amIOwner()) { + if (this.__canIChangePermissions()) { this.__addCollaborators = this._createChildControlImpl("add-collaborator"); } this._createChildControlImpl("collaborators-list"); @@ -382,7 +386,7 @@ qx.Class.define("osparc.share.Collaborators", { ]; const accessRights = this._serializedDataCopy["accessRights"]; const collaboratorsList = []; - const showOptions = this.__amIOwner(); + const showOptions = this.__canIChangePermissions(); Object.keys(accessRights).forEach(gid => { if (Object.prototype.hasOwnProperty.call(this.__collaborators, gid)) { const collab = this.__collaborators[gid]; diff --git a/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js b/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js index b8c8752c6eb..148d8df6f14 100644 --- a/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js +++ b/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js @@ -18,7 +18,7 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { showMaximize: false, autoDestroy: true, modal: true, - width: 262, + width: 350, maxHeight: 500, clickAwayClose: true }); @@ -43,7 +43,10 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { members: { __resourceData: null, __showOrganizations: null, + __introLabel: null, + __textFilter: null, __collabButtonsContainer: null, + __orgsButton: null, __shareButton: null, __selectedCollaborators: null, __visibleCollaborators: null, @@ -53,7 +56,16 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { }, __renderLayout: function() { - const filter = new osparc.filter.TextFilter("name", "collaboratorsManager").set({ + const introText = this.tr("In order to start Sharing with other members, you first need to belong to an Organization."); + const introLabel = this.__introLabel = new qx.ui.basic.Label(introText).set({ + rich: true, + wrap: true, + visibility: "excluded", + padding: 8 + }); + this.add(introLabel); + + const filter = this.__textFilter = new osparc.filter.TextFilter("name", "collaboratorsManager").set({ allowStretchX: true, margin: [0, 10, 5, 10] }); @@ -70,8 +82,15 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { const buttons = new qx.ui.container.Composite(new qx.ui.layout.HBox().set({ alignX: "right" })); + const orgsButton = this.__orgsButton = new qx.ui.form.Button(this.tr("Check Organizations...")).set({ + appearance: "form-button", + visibility: "excluded", + }); + orgsButton.addListener("execute", () => osparc.desktop.organizations.OrganizationsWindow.openWindow(), this); + buttons.add(orgsButton); const shareButton = this.__shareButton = new osparc.ui.form.FetchButton(this.tr("Share")).set({ - appearance: "form-button" + appearance: "form-button", + enabled: false, }); shareButton.addListener("execute", () => this.__shareClicked(), this); buttons.add(shareButton); @@ -95,6 +114,15 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { osparc.store.Store.getInstance().getPotentialCollaborators(false, includeProductEveryone) .then(potentialCollaborators => { this.__visibleCollaborators = potentialCollaborators; + const anyCollaborator = Object.keys(potentialCollaborators).length; + // tell the user that belonging to an organization is required to start sharing + this.__introLabel.setVisibility(anyCollaborator ? "excluded" : "visible"); + this.__orgsButton.setVisibility(anyCollaborator ? "excluded" : "visible"); + + // or start sharing + this.__textFilter.setVisibility(anyCollaborator ? "visible" : "excluded"); + this.__collabButtonsContainer.setVisibility(anyCollaborator ? "visible" : "excluded"); + this.__shareButton.setVisibility(anyCollaborator ? "visible" : "excluded"); this.__addEditors(); }); }, @@ -108,6 +136,7 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { } else { this.__selectedCollaborators.remove(collaborator.gid); } + this.__shareButton.setEnabled(Boolean(this.__selectedCollaborators.length)); }, this); collaboratorButton.subscribeToFilterGroup("collaboratorsManager"); return collaboratorButton; @@ -155,6 +184,9 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { }); }, + __openOrganization: function() { + }, + __shareClicked: function() { this.__collabButtonsContainer.setEnabled(false); this.__shareButton.setFetching(true); diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 5b3459852b9..351e090f6f2 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -28,7 +28,7 @@ qx.Class.define("osparc.store.Folders", { members: { foldersCached: null, - fetchFolders: function(folderId = null) { + fetchFolders: function(folderId = null, workspaceId = null) { if (osparc.auth.Data.getInstance().isGuest()) { return new Promise(resolve => { resolve([]); @@ -37,7 +37,8 @@ qx.Class.define("osparc.store.Folders", { const params = { "url": { - folderId + workspaceId, + folderId, } }; return osparc.data.Resources.getInstance().getAllPages("folders", params) @@ -52,11 +53,11 @@ qx.Class.define("osparc.store.Folders", { }); }, - postFolder: function(name, description, parentId = null) { + postFolder: function(name, parentFolderId = null, workspaceId = null) { const newFolderData = { - parentFolderId: parentId, - name: name, - description: description || "", + name, + parentFolderId, + workspaceId, }; const params = { data: newFolderData diff --git a/services/static-webserver/client/source/class/osparc/store/Workspaces.js b/services/static-webserver/client/source/class/osparc/store/Workspaces.js index 6d0664abb0a..839670ffb24 100644 --- a/services/static-webserver/client/source/class/osparc/store/Workspaces.js +++ b/services/static-webserver/client/source/class/osparc/store/Workspaces.js @@ -29,94 +29,13 @@ qx.Class.define("osparc.store.Workspaces", { return source+iconsSize; }, - FAKE_WORKSPACES: [{ - workspaceId: 1, - name: "Workspace 1", - description: "Workspace 1 desc", - thumbnail: "https://images.ctfassets.net/hrltx12pl8hq/01rJn4TormMsGQs1ZRIpzX/16a1cae2440420d0fd0a7a9a006f2dcb/Artboard_Copy_231.jpg?fit=fill&w=600&h=600", - myAccessRights: { - read: true, - write: true, - delete: true, - }, - accessRights: { - 3: { - read: true, - write: true, - delete: true, - }, - 5: { - read: true, - write: true, - delete: false, - }, - 9: { - read: true, - write: false, - delete: false, - }, - }, - createdAt: "2024-03-04 15:59:51.579217", - lastModified: "2024-03-05 15:18:21.515403", - }, { - workspaceId: 2, - name: "Workspace 2", - description: "Workspace 2 desc", - thumbnail: "", - myAccessRights: { - read: true, - write: true, - delete: false, - }, - accessRights: { - 3: { - read: true, - write: true, - delete: false, - }, - 5: { - read: true, - write: true, - delete: true, - }, - 9: { - read: true, - write: false, - delete: false, - }, - }, - createdAt: "2024-03-05 15:18:21.515403", - lastModified: "2024-04-24 12:03:05.15249", - }, { - workspaceId: 3, - name: "Workspace 3", - description: "Workspace 3 desc", - thumbnail: "https://media.springernature.com/lw703/springer-static/image/art%3A10.1038%2F528452a/MediaObjects/41586_2015_Article_BF528452a_Figg_HTML.jpg", - myAccessRights: { - read: true, - write: false, - delete: false, - }, - accessRights: { - 3: { - read: true, - write: false, - delete: false, - }, - 5: { - read: true, - write: true, - delete: false, - }, - 9: { - read: true, - write: true, - delete: true, - }, - }, - createdAt: "2024-04-24 12:03:05.15249", - lastModified: "2024-06-21 13:00:40.33769", - }], + createNewWorkspaceData: function(name, description = "", thumbnail = "") { + return { + name, + description, + thumbnail, + }; + }, fetchWorkspaces: function() { if (osparc.auth.Data.getInstance().isGuest()) { @@ -125,40 +44,17 @@ qx.Class.define("osparc.store.Workspaces", { }); } - /* - return osparc.data.Resources.getInstance().getAllPages("workspaces", params) + return osparc.data.Resources.getInstance().getAllPages("workspaces") .then(workspacesData => { - const workspaces = []; workspacesData.forEach(workspaceData => { const workspace = new osparc.data.model.Workspace(workspaceData); this.__addToCache(workspace); - workspaces.push(workspace); }); - return workspaces; + return this.workspacesCached; }); - */ - - return new Promise(resolve => { - if (this.workspacesCached.length === 0) { - this.self().FAKE_WORKSPACES.forEach(workspaceData => { - const workspace = new osparc.data.model.Workspace(workspaceData); - this.__addToCache(workspace); - }); - } - resolve(this.workspacesCached); - }); - }, - - createNewWorkspaceData: function(name, description = "", thumbnail = "") { - return { - name, - description, - thumbnail, - }; }, postWorkspace: function(newWorkspaceData) { - /* const params = { data: newWorkspaceData }; @@ -168,30 +64,10 @@ qx.Class.define("osparc.store.Workspaces", { this.__addToCache(newWorkspace); return newWorkspace; }); - */ - const workspaceData = newWorkspaceData; - workspaceData["workspaceId"] = Math.floor(Math.random() * 100) + 100; - workspaceData["myAccessRights"] = osparc.share.CollaboratorsWorkspace.getOwnerAccessRight(); - const myGroupId = osparc.auth.Data.getInstance().getGroupId(); - workspaceData["accessRights"] = {}; - workspaceData["accessRights"][myGroupId] = osparc.share.CollaboratorsWorkspace.getOwnerAccessRight(); - workspaceData["createdAt"] = new Date().toISOString(); - workspaceData["lastModified"] = new Date().toISOString(); - return new Promise(resolve => { - const workspace = new osparc.data.model.Workspace(workspaceData); - this.__addToCache(workspace); - resolve(workspace); - }); }, deleteWorkspace: function(workspaceId) { return new Promise((resolve, reject) => { - if (this.__deleteFromCache(workspaceId)) { - resolve(); - } else { - reject(); - } - /* const params = { "url": { workspaceId @@ -206,7 +82,6 @@ qx.Class.define("osparc.store.Workspaces", { } }) .catch(err => reject(err)); - */ }); }, @@ -228,66 +103,81 @@ qx.Class.define("osparc.store.Workspaces", { workspace[setter](updateData[propKey]); } }); - workspace.setLastModified(new Date()); - this.__deleteFromCache(workspaceId); - this.__addToCache(workspace); - resolve(); + workspace.set({ + modifiedAt: new Date() + }); + resolve(workspace); }) .catch(err => reject(err)); }); }, addCollaborators: function(workspaceId, newCollaborators) { - return new Promise((resolve, reject) => { - const workspace = this.getWorkspace(workspaceId); - if (workspace) { - const accessRights = workspace.getAccessRights(); - const newAccessRights = Object.assign(accessRights, newCollaborators); + const promises = []; + Object.keys(newCollaborators).forEach(groupId => { + const params = { + url: { + workspaceId, + groupId, + }, + data: newCollaborators[groupId] + }; + promises.push(osparc.data.Resources.fetch("workspaces", "postAccessRights", params)); + }); + return Promise.all(promises) + .then(() => { + const workspace = this.getWorkspace(workspaceId); + const newAccessRights = workspace.getAccessRights(); + Object.keys(newCollaborators).forEach(gid => { + newAccessRights[gid] = newCollaborators[gid]; + }); workspace.set({ accessRights: newAccessRights, - lastModified: new Date() - }) - resolve(); - } else { - reject(); - } - }); + modifiedAt: new Date() + }); + }) + .catch(console.error); }, - removeCollaborator: function(workspaceId, gid) { - return new Promise((resolve, reject) => { - const workspace = this.getWorkspace(workspaceId); - if (workspace) { - const accessRights = workspace.getAccessRights(); - delete accessRights[gid]; - workspace.set({ - accessRights: accessRights, - lastModified: new Date() - }) - resolve(); - } else { - reject(); + removeCollaborator: function(workspaceId, groupId) { + const params = { + url: { + workspaceId, + groupId, } - }); + }; + return osparc.data.Resources.fetch("workspaces", "deleteAccessRights", params) + .then(() => { + const workspace = this.getWorkspace(workspaceId); + const newAccessRights = workspace.getAccessRights(); + delete newAccessRights[groupId]; + workspace.set({ + accessRights: newAccessRights, + modifiedAt: new Date() + }); + }) + .catch(console.error); }, - updateCollaborator: function(workspaceId, gid, newPermissions) { - return new Promise((resolve, reject) => { - const workspace = this.getWorkspace(workspaceId); - if (workspace) { - const accessRights = workspace.getAccessRights(); - if (gid in accessRights) { - accessRights[gid] = newPermissions; - workspace.set({ - accessRights: accessRights, - lastModified: new Date() - }) - resolve(); - return; - } - } - reject(); - }); + updateCollaborator: function(workspaceId, groupId, newPermissions) { + const params = { + url: { + workspaceId, + groupId, + }, + data: newPermissions + }; + return osparc.data.Resources.fetch("workspaces", "putAccessRights", params) + .then(() => { + const workspace = this.getWorkspace(workspaceId); + const newAccessRights = workspace.getAccessRights(); + newAccessRights[groupId] = newPermissions; + workspace.set({ + accessRights: workspace.newAccessRights, + modifiedAt: new Date() + }); + }) + .catch(console.error); }, getWorkspaces: function(parentId = null) { diff --git a/services/static-webserver/client/source/class/osparc/study/Utils.js b/services/static-webserver/client/source/class/osparc/study/Utils.js index c41b7218813..b88ed539257 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -105,12 +105,13 @@ qx.Class.define("osparc.study.Utils", { return isRetired; }, - createStudyFromService: function(key, version, existingStudies, newStudyLabel) { + createStudyFromService: function(key, version, existingStudies, newStudyLabel, contextProps = {}) { return new Promise((resolve, reject) => { osparc.store.Services.getService(key, version) .then(metadata => { const newUuid = osparc.utils.Utils.uuidV4(); - const minStudyData = osparc.data.model.Study.createMyNewStudyObject(); + // context props, otherwise Study will be created in the root folder of my personal workspace + const minStudyData = Object.assign(osparc.data.model.Study.createMinStudyObject(), contextProps); if (newStudyLabel === undefined) { newStudyLabel = metadata["name"]; } @@ -181,7 +182,7 @@ qx.Class.define("osparc.study.Utils", { }); }, - createStudyFromTemplate: function(templateData, loadingPage) { + createStudyFromTemplate: function(templateData, loadingPage, contextProps = {}) { return new Promise((resolve, reject) => { const inaccessibleServices = this.getInaccessibleServices(templateData["workbench"]); if (inaccessibleServices.length) { @@ -191,7 +192,8 @@ qx.Class.define("osparc.study.Utils", { }); return; } - const minStudyData = osparc.data.model.Study.createMyNewStudyObject(); + // context props, otherwise Study will be created in the root folder of my personal workspace + const minStudyData = Object.assign(osparc.data.model.Study.createMinStudyObject(), contextProps); minStudyData["name"] = templateData["name"]; minStudyData["description"] = templateData["description"]; minStudyData["thumbnail"] = templateData["thumbnail"];