diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js similarity index 68% rename from services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js rename to services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js index bd4582fd0fc..5322e8e8d67 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ContainerHeader.js @@ -20,7 +20,7 @@ * */ -qx.Class.define("osparc.dashboard.FolderHeader", { +qx.Class.define("osparc.dashboard.ContainerHeader", { extend: qx.ui.core.Widget, construct: function() { @@ -31,17 +31,21 @@ qx.Class.define("osparc.dashboard.FolderHeader", { })); }, - events: { - "changeCurrentFolderId": "qx.event.type.Data" - }, - properties: { + currentWorkspaceId: { + check: "Number", + nullable: true, + init: null, + event: "changeCurrentWorkspaceId", + apply: "__buildBreadcrumbs" + }, + currentFolderId: { check: "Number", nullable: true, init: null, event: "changeCurrentFolderId", - apply: "__applyCurrentFolderId" + apply: "__buildBreadcrumbs" } }, @@ -55,24 +59,10 @@ qx.Class.define("osparc.dashboard.FolderHeader", { })); this._addAt(control, 0, {flex: 1}); break; - case "permissions-info": { - control = new qx.ui.container.Composite(new qx.ui.layout.HBox().set({ - alignY: "middle" - })).set({ - paddingRight: 14 - }); - this._addAt(control, 1); - break; - } } return control || this.base(arguments, id); }, - __applyCurrentFolderId: function() { - this.__buildBreadcrumbs(); - this.__populatePermissions(); - }, - __buildBreadcrumbs: function() { const breadcrumbsLayout = this.getChildControl("breadcrumbs-layout"); breadcrumbsLayout.removeAll(); @@ -110,14 +100,38 @@ qx.Class.define("osparc.dashboard.FolderHeader", { return this.__createFolderButton(currentFolder); }, + __createRootButton: function(workspaceId) { + let rootButton = null; + if (workspaceId) { + if (workspaceId === -1) { + rootButton = new qx.ui.form.Button(this.tr("Shared Workspaces"), osparc.store.Workspaces.iconPath()); + } else { + const workspace = osparc.store.Workspaces.getWorkspace(workspaceId); + rootButton = new qx.ui.form.Button(workspace.getName(), osparc.store.Workspaces.iconPath()); + } + rootButton.addListener("execute", () => this.set({ + currentWorkspaceId: workspaceId, + currentFolderId: null, + })); + } else { + rootButton = new qx.ui.form.Button(this.tr("My Workspace"), "@FontAwesome5Solid/home/14"); + rootButton.addListener("execute", () => this.set({ + currentWorkspaceId: null, + currentFolderId: null, + })); + } + return rootButton; + }, + __createFolderButton: function(folder) { let folderButton = null; if (folder) { folderButton = new qx.ui.form.Button(folder.getName(), "@FontAwesome5Solid/folder/14"); + folderButton.addListener("execute", () => this.fireDataEvent("changeCurrentFolderId", folder ? folder.getFolderId() : null), this); } else { - folderButton = new qx.ui.form.Button(this.tr("Home"), "@FontAwesome5Solid/home/14"); + const workspaceId = this.getCurrentWorkspaceId(); + folderButton = this.__createRootButton(workspaceId); } - folderButton.addListener("execute", () => this.fireDataEvent("changeCurrentFolderId", folder ? folder.getFolderId() : null), this); folderButton.set({ backgroundColor: "transparent", textColor: "text", @@ -128,22 +142,6 @@ qx.Class.define("osparc.dashboard.FolderHeader", { __createArrow: function() { return new qx.ui.basic.Label("/"); - }, - - __populatePermissions: function() { - const permissionsLayout = this.getChildControl("permissions-info"); - permissionsLayout.removeAll(); - - if (this.getCurrentFolderId()) { - const currentFolder = osparc.store.Folders.getInstance().getFolder(this.getCurrentFolderId()); - const ar = currentFolder.getMyAccessRights(); - const permissions = ar["read"] + ar["write"] + ar["delete"]; - const roleTitle = new qx.ui.basic.Label().set({ - value: osparc.data.Roles.FOLDERS[permissions].label - }); - permissionsLayout.add(roleTitle); - permissionsLayout.add(osparc.data.Roles.createRolesFolderInfo(false)); - } } } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js index 82ccea53e3d..effe47c25c4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js @@ -167,7 +167,7 @@ qx.Class.define("osparc.dashboard.Dashboard", { const preResourcePromises = []; const store = osparc.store.Store.getInstance(); preResourcePromises.push(store.getAllGroupsAndMembers()); - preResourcePromises.push(osparc.service.Store.getServicesLatest(false)); + preResourcePromises.push(osparc.store.Services.getServicesLatest(false)); if (permissions.canDo("study.tag")) { preResourcePromises.push(osparc.data.Resources.get("tags")); } 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 b3c1bec4da6..d6a0d76769d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -79,18 +79,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { apply: "__applyDescription" }, - myAccessRights: { - check: "Object", - nullable: true, - apply: "__applyMyAccessRights" - }, - - accessRights: { - check: "Object", - nullable: true, - apply: "__applyAccessRights" - }, - lastModified: { check: "Date", nullable: true, @@ -103,7 +91,8 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { let control; switch (id) { case "icon": { - control = new osparc.dashboard.FolderWithSharedIcon().set({ + control = new qx.ui.basic.Image().set({ + source: "@FontAwesome5Solid/folder/26", anonymous: true, height: 40, padding: 5 @@ -155,9 +144,9 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { folder.bind("parentId", this, "parentFolderId"); folder.bind("name", this, "title"); folder.bind("description", this, "description"); - folder.bind("accessRights", this, "accessRights"); folder.bind("lastModified", this, "lastModified"); - folder.bind("myAccessRights", this, "myAccessRights"); + + this.__addMenuButton(); }, __applyTitle: function(value) { @@ -177,73 +166,51 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { } }, - __applyMyAccessRights: function(value) { - if (value && value["delete"]) { - const menuButton = this.getChildControl("menu-button"); - menuButton.setVisibility("visible"); + __addMenuButton: function() { + const menuButton = this.getChildControl("menu-button"); + menuButton.setVisibility("visible"); - const menu = new qx.ui.menu.Menu().set({ - position: "bottom-right" - }); + const menu = new qx.ui.menu.Menu().set({ + position: "bottom-right" + }); - 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()); + 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() }); - menu.add(editButton); - - const shareButton = new qx.ui.menu.Button(this.tr("Share..."), "@FontAwesome5Solid/share-alt/12"); - shareButton.addListener("execute", () => this.__openShareWith(), this); - menu.add(shareButton); - - menu.addSeparator(); - - const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12"); - deleteButton.addListener("execute", () => this.__deleteStudyRequested(), this); - menu.add(deleteButton); + 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()); + }); + menu.add(editButton); - menuButton.setMenu(menu); - } - }, + const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12"); + deleteButton.addListener("execute", () => this.__deleteStudyRequested(), this); + menu.add(deleteButton); - __applyAccessRights: function(value) { - if (value && Object.keys(value).length) { - const shareIcon = this.getChildControl("icon").getChildControl("shared-icon"); - // if it's not shared don't show the share icon - shareIcon.addListener("changeSource", e => { - const newSource = e.getData(); - shareIcon.set({ - visibility: newSource.includes(osparc.dashboard.CardBase.SHARE_ICON) ? "hidden" : "visible" - }); - }); - osparc.dashboard.CardBase.populateShareIcon(shareIcon, value); - } + menuButton.setMenu(menu); }, __updateTooltip: function() { @@ -260,18 +227,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { this.setValue(false); }, - __openShareWith: function() { - const disableShare = true; - if (disableShare) { - osparc.FlashMessenger.getInstance().logAs(this.tr("Not yet implemented"), "WARNING"); - } else { - const title = this.tr("Share Folder"); - const permissionsView = new osparc.share.CollaboratorsFolder(this.getFolder()); - osparc.ui.window.Window.popUpInWindow(permissionsView, title); - permissionsView.addListener("updateAccessRights", () => this.__applyAccessRights(this.getFolder().getAccessRights()), this); - } - }, - __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/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index 95986f1e293..40178ea468d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -42,10 +42,10 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { const mainLayoutWithSideSpacers = new qx.ui.container.Composite(new qx.ui.layout.HBox(spacing)) this._addToMainLayout(mainLayoutWithSideSpacers); - this.__leftLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)).set({ + this.__leftFilters = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)).set({ width: leftColumnWidth }); - mainLayoutWithSideSpacers.add(this.__leftLayout); + mainLayoutWithSideSpacers.add(this.__leftFilters); this.__centerLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); mainLayoutWithSideSpacers.add(this.__centerLayout); @@ -68,7 +68,6 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { } const compactVersion = w < this.__centerLayout.getMinWidth() + leftColumnWidth + emptyColumnMinWidth; - this.__leftLayout.setVisibility(compactVersion ? "excluded" : "visible"); rightColum.setVisibility(compactVersion ? "excluded" : "visible"); }; fitResourceCards(); @@ -83,7 +82,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { statics: { PAGINATED_STUDIES: 10, - MIN_GRID_CARDS_PER_ROW: 4, + MIN_GRID_CARDS_PER_ROW: 3, SIDE_SPACER_WIDTH: 180, checkLoggedIn: function() { @@ -186,7 +185,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, members: { - __leftLayout: null, + __leftFilters: null, __centerLayout: null, _resourceType: null, _resourcesList: null, @@ -254,6 +253,13 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("folderSelected", e => this._folderSelected(e.getData())); resourcesContainer.addListener("folderUpdated", e => this._folderUpdated(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); + }); + resourcesContainer.addListener("workspaceUpdated", e => this._workspaceUpdated(e.getData())); + resourcesContainer.addListener("deleteWorkspaceRequested", e => this._deleteWorkspaceRequested(e.getData())); this._addToLayout(resourcesContainer); }, @@ -349,17 +355,28 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, _addResourceFilter: function() { - const 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 }); resourceFilter.addListener("changeSharedWith", e => { + if (this._resourceType === "study") { + this.setCurrentWorkspaceId(null); + } const sharedWith = e.getData(); this._searchBarFilter.setSharedWithActiveFilter(sharedWith.id, sharedWith.label); }, this); + resourceFilter.addListener("changeWorkspace", e => { + const workspaceId = e.getData(); + this.setCurrentWorkspaceId(workspaceId); + if (this._resourceType === "study") { + this._searchBarFilter.resetSharedWithActiveFilter(); + } + }, this); + resourceFilter.addListener("changeSelectedTags", e => { const selectedTagIds = e.getData(); this._searchBarFilter.setTagsActiveFilter(selectedTagIds); @@ -375,7 +392,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourceFilter.filterChanged(filterData); }); - this.__leftLayout.add(resourceFilter); + this.__leftFilters.add(resourceFilter); }, /** @@ -460,6 +477,18 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, + _workspaceSelected: function(workspaceId) { + throw new Error("Abstract method called!"); + }, + + _workspaceUpdated: function(workspaceId) { + throw new Error("Abstract method called!"); + }, + + _deleteWorkspaceRequested: function(workspaceId) { + throw new Error("Abstract method called!"); + }, + _getOpenMenuButton: function(resourceData) { const openButton = new qx.ui.menu.Button(this.tr("Open")); openButton["openResourceButton"] = true; 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 01b4f7f861d..84b2e680f5f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -21,22 +21,30 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { construct: function() { this.base(arguments); - this._setLayout(new qx.ui.layout.VBox(20)); + this._setLayout(new qx.ui.layout.VBox(15)); this.set({ paddingBottom: 60 }); this.__foldersList = []; + this.__workspacesList = []; this.__resourcesList = []; + this.__groupedContainersList = []; + + const containerHeader = this.__containerHeader = new osparc.dashboard.ContainerHeader(); + this._add(containerHeader); + containerHeader.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded"); + + + const workspacesContainer = this.__workspacesContainer = new osparc.dashboard.ToggleButtonContainer(); + this._add(workspacesContainer); + workspacesContainer.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded"); + - const folders = this.__foldersLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); - const folderHeader = this.__folderHeader = new osparc.dashboard.FolderHeader(); - folders.add(folderHeader); const foldersContainer = this.__foldersContainer = new osparc.dashboard.ToggleButtonContainer(); - folders.add(foldersContainer); - this._add(folders); - folders.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded"); + this._add(foldersContainer); + foldersContainer.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded"); const nonGroupedContainer = this.__nonGroupedContainer = new osparc.dashboard.ToggleButtonContainer(); [ @@ -47,9 +55,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { }); this._add(nonGroupedContainer); - const groupedContainersLayout = this.__groupedContainersLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); - this._add(groupedContainersLayout); - this.__groupedContainersList = []; + const groupedContainers = this.__groupedContainers = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); + this._add(groupedContainers); }, properties: { @@ -80,6 +87,9 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "folderSelected": "qx.event.type.Data", "folderUpdated": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data", + "workspaceSelected": "qx.event.type.Data", + "workspaceUpdated": "qx.event.type.Data", + "deleteWorkspaceRequested": "qx.event.type.Data", }, statics: { @@ -104,13 +114,15 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { members: { __foldersList: null, + __workspacesList: null, __resourcesList: null, + __groupedContainersList: null, __foldersLayout: null, - __folderHeader: null, + __containerHeader: null, __foldersContainer: null, + __workspacesContainer: null, __nonGroupedContainer: null, - __groupedContainersList: null, - __groupedContainersLayout: null, + __groupedContainers: null, addNonResourceCard: function(card) { if (card instanceof qx.ui.form.ToggleButton) { @@ -151,8 +163,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { } }, - getFolderHeader: function() { - return this.__folderHeader; + getContainerHeader: function() { + return this.__containerHeader; }, getFlatList: function() { @@ -238,44 +250,41 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { return card; }, - __createFolderCard: function(folder) { - const card = new osparc.dashboard.FolderButtonItem(folder); - card.subscribeToFilterGroup("searchBarFilter"); - [ - "folderSelected", - "folderUpdated", - "deleteFolderRequested", - ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); - return card; - }, - setResourcesToList: function(resourcesList) { this.__resourcesList = resourcesList; }, __cleanAll: function() { + if (this.__workspacesContainer) { + this.__workspacesContainer.removeAll(); + } if (this.__nonGroupedContainer) { this.__nonGroupedContainer.removeAll(); this.__nonGroupedContainer = null; } - if (this.__groupedContainersLayout) { - this.__groupedContainersLayout.removeAll(); + if (this.__groupedContainers) { + this.__groupedContainers.removeAll(); } - this.__groupedContainersList.forEach(groupedContainer => groupedContainer.getContentContainer().removeAll()); + this.__groupedContainersList.forEach(groupedContainer => { + groupedContainer.getContentContainer().removeAll(); + }); this.__groupedContainersList = []; this._removeAll(); }, - reloadCards: function(listId) { + reloadCards: function(resourceType) { this.__cleanAll(); - this._add(this.__foldersLayout); + if (resourceType === "studies") { + this._add(this.__containerHeader); + this._add(this.__foldersContainer); + } if (this.getGroupBy()) { const noGroupContainer = this.__createGroupContainer("no-group", "No Group", "transparent"); - this.__groupedContainersLayout.add(noGroupContainer); - this._add(this.__groupedContainersLayout); + this.__groupedContainers.add(noGroupContainer); + this._add(this.__groupedContainers); } else { const flatList = this.__nonGroupedContainer = new osparc.dashboard.ToggleButtonContainer(); - osparc.utils.Utils.setIdToWidget(flatList, listId); + osparc.utils.Utils.setIdToWidget(flatList, resourceType + "List"); [ "changeSelection", "changeVisibility" @@ -309,6 +318,43 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { return newCards; }, + // WORKSPACES + setWorkspacesToList: function(workspacesList) { + this.__workspacesList = workspacesList; + }, + + reloadWorkspaces: function() { + this.__cleanAll(); + this._add(this.__containerHeader); + this._add(this.__workspacesContainer); + let workspacesCards = []; + this.__workspacesList.forEach(workspaceData => workspacesCards.push(this.__workspaceToCard(workspaceData))); + return workspacesCards; + }, + + addNewWorkspaceCard: function(newWorkspaceCard) { + this.__workspacesContainer.addAt(newWorkspaceCard, 0); + }, + + __workspaceToCard: function(workspaceData) { + const card = this.__createWorkspaceCard(workspaceData); + this.__workspacesContainer.add(card); + return card; + }, + + __createWorkspaceCard: function(workspace) { + const card = new osparc.dashboard.WorkspaceButtonItem(workspace); + card.subscribeToFilterGroup("searchBarFilter"); + [ + "workspaceSelected", + "workspaceUpdated", + "deleteWorkspaceRequested", + ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); + return card; + }, + // /WORKSPACES + + // FOLDERS setFoldersToList: function(foldersList) { this.__foldersList = foldersList; }, @@ -326,10 +372,28 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.__foldersContainer.addAt(newFolderCard, 0); }, + __folderToCard: function(folderData) { + const card = this.__createFolderCard(folderData); + this.__foldersContainer.add(card); + return card; + }, + + __createFolderCard: function(folder) { + const card = new osparc.dashboard.FolderButtonItem(folder); + card.subscribeToFilterGroup("searchBarFilter"); + [ + "folderSelected", + "folderUpdated", + "deleteFolderRequested", + ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); + return card; + }, + // /FOLDERS + __moveNoGroupToLast: function() { - const idx = this.__groupedContainersLayout.getChildren().findIndex(grpContainer => grpContainer === this.__getGroupContainer("no-group")); + const idx = this.__groupedContainers.getChildren().findIndex(grpContainer => grpContainer === this.__getGroupContainer("no-group")); if (idx > -1) { - this.__groupedContainersLayout.getChildren().push(this.__groupedContainersLayout.getChildren().splice(idx, 1)[0]); + this.__groupedContainers.getChildren().push(this.__groupedContainers.getChildren().splice(idx, 1)[0]); } }, @@ -347,8 +411,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { if (groupContainer === null) { groupContainer = this.__createGroupContainer(tag.id, tag.name, tag.color); groupContainer.setHeaderIcon("@FontAwesome5Solid/tag/24"); - this.__groupedContainersLayout.add(groupContainer); - this.__groupedContainersLayout.getChildren().sort((a, b) => a.getHeaderLabel().localeCompare(b.getHeaderLabel())); + this.__groupedContainers.add(groupContainer); + this.__groupedContainers.getChildren().sort((a, b) => a.getHeaderLabel().localeCompare(b.getHeaderLabel())); this.__moveNoGroupToLast(); } const card = this.__createCard(resourceData); @@ -417,12 +481,6 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.self().sortListByPriority(this.__nonGroupedContainer); } return cardsCreated; - }, - - __folderToCard: function(folderData) { - const card = this.__createFolderCard(folderData); - this.__foldersContainer.add(card); - return card; } } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 17d249b146b..7a32749e1b5 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -257,7 +257,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { if (selection.length) { const serviceVersion = selection[0].version; if (serviceVersion !== this.__resourceData["version"]) { - osparc.service.Store.getService(this.__resourceData["key"], serviceVersion) + osparc.store.Services.getService(this.__resourceData["key"], serviceVersion) .then(serviceData => { serviceData["resourceType"] = "service"; this.__resourceData = serviceData; 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 a325cec6123..0a47f4e12ec 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js @@ -26,6 +26,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { this.__resourceType = resourceType; this.__sharedWithButtons = []; + this.__workspaceButtons = []; this.__tagButtons = []; this.__serviceTypeButtons = []; @@ -35,6 +36,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { events: { "changeSharedWith": "qx.event.type.Data", + "changeWorkspace": "qx.event.type.Data", "changeSelectedTags": "qx.event.type.Data", "changeServiceType": "qx.event.type.Data" }, @@ -42,15 +44,19 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { members: { __resourceType: null, __sharedWithButtons: null, + __workspaceButtons: null, __tagButtons: null, __serviceTypeButtons: null, __buildLayout: function() { const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(40)); + layout.add(this.__createSharedWithFilterLayout()); + if (this.__resourceType !== "service") { layout.add(this.__createTagsFilterLayout()); } + if (this.__resourceType === "service") { layout.add(this.__createServiceTypeFilterLayout()); } @@ -74,11 +80,23 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { if (this.__resourceType === "study" && option.id === "shared-with-everyone") { return; } - const button = new qx.ui.toolbar.RadioButton(option.label, option.icon); - button.id = option.id; - button.set({ - appearance: "filter-toggle-button" + const button = new qx.ui.toolbar.RadioButton().set({ + appearance: "filter-toggle-button", + label: option.label, + icon: option.icon, }); + if (this.__resourceType === "study") { + if (option.id === "show-all") { + button.set({ + label: this.tr("My Workspace") + }); + } else { + button.set({ + marginLeft: 15 + }); + } + } + button.id = option.id; layout.add(button); radioGroup.add(button); @@ -93,10 +111,56 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { this.__sharedWithButtons.push(button); }); + if (this.__resourceType === "study") { + this.__addWorkspaceButtons(layout, radioGroup); + } + return layout; }, /* /SHARED WITH */ + /* WORKSPACES */ + __addWorkspaceButtons: function(layout, radioGroup) { + layout.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); + workspacesButton.addListener("execute", () => { + this.fireDataEvent("changeWorkspace", workspacesButton.workspaceId); + }); + + osparc.store.Workspaces.fetchWorkspaces() + .then(workspaces => { + workspaces.forEach(workspace => { + const workspaceButton = new qx.ui.toolbar.RadioButton(workspace.getName(), osparc.store.Workspaces.iconPath(22)); + workspaceButton.workspaceId = workspace.getWorkspaceId(); + this.__workspaceButtons.push(workspaceButton); + workspaceButton.set({ + appearance: "filter-toggle-button", + marginLeft: 15, + }); + layout.add(workspaceButton); + radioGroup.add(workspaceButton); + workspaceButton.addListener("execute", () => { + this.fireDataEvent("changeWorkspace", workspaceButton.workspaceId); + }, this); + }); + }) + .catch(console.error); + }, + + workspaceSelected: function(workspaceId) { + const foundButton = this.__workspaceButtons.find(workspaceButton => workspaceButton.workspaceId === workspaceId); + if (foundButton) { + foundButton.execute(); + } + }, + /* /WORKSPACES */ + /* TAGS */ __createTagsFilterLayout: function() { const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js index 4be366fc05d..99ba301e236 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js @@ -287,6 +287,11 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { }); }, + resetSharedWithActiveFilter: function() { + this.__removeChips("shared-with"); + this.__filter(); + }, + setSharedWithActiveFilter: function(optionId, optionLabel) { this.__removeChips("shared-with"); if (optionId === "show-all") { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js index 207b81956fc..81583c9b24f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js @@ -58,7 +58,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { }, __loadServices: function() { - osparc.service.Store.getServicesLatest() + osparc.store.Services.getServicesLatest() .then(servicesLatest => { const servicesList = []; Object.keys(servicesLatest).forEach(key => { @@ -100,7 +100,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { _reloadCards: function() { this._resourcesContainer.setResourcesToList(this._resourcesList); - const cards = this._resourcesContainer.reloadCards("servicesList"); + const cards = this._resourcesContainer.reloadCards("services"); cards.forEach(card => { card.setMultiSelectionMode(this.getMultiSelection()); card.addListener("execute", () => this.__itemClicked(card), 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 2e174d99229..e9b61fadca7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -49,6 +49,14 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, properties: { + currentWorkspaceId: { + check: "Number", + nullable: true, + init: null, + event: "changeCurrentWorkspaceId", + apply: "__applyCurrentWorkspaceId" + }, + currentFolderId: { check: "Number", nullable: true, @@ -94,6 +102,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, members: { + __workspacesList: null, __foldersList: null, // overridden @@ -141,13 +150,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { reloadResources: function() { if (osparc.data.Permissions.getInstance().canDo("studies.user.read")) { - this.__reloadResources(); + this.__reloadFoldersAndStudies(); } else { this.__resetStudiesList(); } }, - __reloadResources: function() { + __reloadFoldersAndStudies: function() { this.__reloadFolders(); this.__reloadStudies(); }, @@ -156,6 +165,15 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__reloadFilteredStudies(); }, + __reloadWorkspaces: function() { + osparc.store.Workspaces.fetchWorkspaces() + .then(workspaces => { + this.__workspacesList = workspaces; + workspaces.forEach(workspace => workspace["resourceType"] = "workspace"); + this.__reloadWorkspaceCards(); + }); + }, + __reloadFolders: function() { const folders = osparc.store.Folders.getInstance().getFolders(this.getCurrentFolderId()) this.__setFoldersToList(folders); @@ -186,7 +204,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (nStudies === 0) { const promises = [ osparc.store.Store.getInstance().getTemplates(), - osparc.service.Store.getServicesLatest(), + osparc.store.Services.getServicesLatest(), ]; if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { promises.push(osparc.store.Folders.getInstance().fetchFolders()); @@ -345,7 +363,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; this._resourcesContainer.setResourcesToList(this._resourcesList); - const cards = this._resourcesContainer.reloadCards("studiesList"); + const cards = this._resourcesContainer.reloadCards("studies"); this.__configureCards(cards); this.__addNewStudyButtons(); @@ -369,6 +387,66 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { osparc.filter.UIFilterController.dispatch("searchBarFilter"); }, + __applyCurrentWorkspaceId: function(workspaceId) { + if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { + if (workspaceId === -1) { + this._resourcesContainer.setResourcesToList([]); + this._resourcesList = []; + + this.__reloadWorkspaces(); + } else { + this._resourcesContainer.setResourcesToList([]); + this._resourcesList = []; + this.invalidateStudies(); + + this.__reloadFoldersAndStudies(); + } + } + }, + + // WORKSPACES + __reloadWorkspaceCards: function() { + this._resourcesContainer.setWorkspacesToList(this.__workspacesList); + this._resourcesContainer.reloadWorkspaces(); + + const newWorkspaceCard = new osparc.dashboard.WorkspaceButtonNew(); + newWorkspaceCard.setCardKey("new-workspace"); + newWorkspaceCard.subscribeToFilterGroup("searchBarFilter"); + newWorkspaceCard.addListener("createWorkspace", () => this.__reloadWorkspaces()); + newWorkspaceCard.addListener("updateWorkspace", () => this.__reloadWorkspaces()); + this._resourcesContainer.addNewWorkspaceCard(newWorkspaceCard); + }, + + _workspaceSelected: function(workspaceId) { + this.setCurrentWorkspaceId(workspaceId); + }, + + _workspaceUpdated: function() { + this.__reloadWorkspaceCards(); + }, + + _deleteWorkspaceRequested: function(workspaceId) { + osparc.store.Workspaces.deleteWorkspace(workspaceId) + .then(() => this.__reloadWorkspaceCards()) + .catch(err => console.error(err)); + }, + // /WORKSPACES + + // FOLDERS + __applyCurrentFolderId: function(currentFolderId) { + if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { + osparc.store.Folders.getInstance().fetchFolders(currentFolderId) + .then(() => { + this._resourcesContainer.setResourcesToList([]); + this._resourcesList = []; + this.invalidateStudies(); + + this.__reloadFoldersAndStudies(); + }) + .catch(console.error); + } + }, + __reloadFolderCards: function() { this._resourcesContainer.setFoldersToList(this.__foldersList); this._resourcesContainer.reloadFolders(); @@ -392,20 +470,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.setCurrentFolderId(folderId); }, - __applyCurrentFolderId: function(currentFolderId) { - if (osparc.utils.DisabledPlugins.isFoldersEnabled()) { - osparc.store.Folders.getInstance().fetchFolders(currentFolderId) - .then(() => { - this._resourcesContainer.setResourcesToList([]); - this._resourcesList = []; - this.invalidateStudies(); - - this.__reloadResources(); - }) - .catch(console.error); - } - }, - _folderUpdated: function() { this.__reloadFolders(); }, @@ -415,6 +479,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { .then(() => this.__reloadFolders()) .catch(err => console.error(err)); }, + // /FOLDERS __configureCards: function(cards) { cards.forEach(card => { @@ -671,7 +736,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // scale to latest compatible const latestVersion = versions[0]; const latestCompatible = osparc.service.Utils.getLatestCompatible(key, latestVersion); - osparc.service.Store.getService(latestCompatible["key"], latestCompatible["version"]) + osparc.store.Services.getService(latestCompatible["key"], latestCompatible["version"]) .then(latestMetadata => { const title = newButtonInfo.title + " " + osparc.service.Utils.extractVersionDisplay(latestMetadata); const desc = newButtonInfo.description; @@ -707,10 +772,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _createLayout: function() { this._createResourcesLayout(); - const folderHeader = this._resourcesContainer.getFolderHeader(); - if (folderHeader) { - this.bind("currentFolderId", folderHeader, "currentFolderId"); - folderHeader.addListener("changeCurrentFolderId", e => this.setCurrentFolderId(e.getData())); + const containerHeader = this._resourcesContainer.getContainerHeader(); + if (containerHeader) { + this.bind("currentWorkspaceId", containerHeader, "currentWorkspaceId"); + containerHeader.bind("currentWorkspaceId", this, "currentWorkspaceId"); + this.bind("currentFolderId", containerHeader, "currentFolderId"); + containerHeader.bind("currentFolderId", this, "currentFolderId"); } const list = this._resourcesContainer.getFlatList(); if (list) { @@ -795,7 +862,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (filterData.text) { this.__reloadFilteredResources(filterData.text); } else { - this.__reloadResources(); + this.__reloadFoldersAndStudies(); } sharedWithButton.filterChanged(filterData); }, this); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js index d9ba8b01296..6a5e516ce9e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js @@ -111,7 +111,7 @@ qx.Class.define("osparc.dashboard.TemplateBrowser", { _reloadCards: function() { this._resourcesContainer.setResourcesToList(this._resourcesList); - const cards = this._resourcesContainer.reloadCards("templatesList"); + const cards = this._resourcesContainer.reloadCards("templates"); cards.forEach(card => { card.setMultiSelectionMode(this.getMultiSelection()); card.addListener("tap", () => this.__itemClicked(card), this); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js new file mode 100644 index 00000000000..fbcf92b3bf7 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js @@ -0,0 +1,252 @@ +/* ************************************************************************ + + 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.WorkspaceButtonBase", { + extend: qx.ui.form.ToggleButton, + implement: [qx.ui.form.IModel, osparc.filter.IFilterable], + include: [qx.ui.form.MModelProperty, osparc.filter.MFilterable], + type: "abstract", + + construct: function() { + this.base(arguments); + + this.set({ + width: this.self().ITEM_WIDTH, + height: this.self().ITEM_HEIGHT, + padding: 0 + }); + + this._setLayout(new qx.ui.layout.Canvas()); + + this.getChildControl("main-layout"); + + [ + "pointerover", + "focus" + ].forEach(e => this.addListener(e, this._onPointerOver, this)); + + [ + "pointerout", + "focusout" + ].forEach(e => this.addListener(e, this._onPointerOut, this)); + }, + + properties: { + cardKey: { + check: "String", + nullable: true + }, + + resourceType: { + check: ["workspace"], + init: "workspace", + nullable: false + }, + + priority: { + check: "Number", + init: null, + nullable: false + } + }, + + statics: { + ITEM_WIDTH: 190, + ITEM_HEIGHT: 190, + PADDING: 10, + SPACING_IN: 5, + SPACING: 15, + HEADER_MAX_HEIGHT: 40, // two lines in Manrope + ICON_SIZE: 60, + POS: { + HEADER: 0, + BODY: 1, + FOOTER: 2 + }, + HPOS: { + SHARED: 0, + TITLE: 1, + MENU: 2, + }, + FPOS: { + MODIFIED: 0 + } + }, + + events: { + /** (Fired by {@link qx.ui.form.List}) */ + "action": "qx.event.type.Event" + }, + + members: { // eslint-disable-line qx-rules/no-refs-in-members + // overridden + _forwardStates: { + focused : true, + hovered : true, + selected : true, + dragover : true + }, + + // overridden + _createChildControlImpl: function(id) { + let layout; + let control; + switch (id) { + case "main-layout": { + control = new qx.ui.container.Composite(new qx.ui.layout.VBox(this.self().SPACING_IN)); + const header = this.getChildControl("header"); + const body = this.getChildControl("body"); + const footer = this.getChildControl("footer"); + control.addAt(header, this.self().POS.HEADER); + control.addAt(body, this.self().POS.BODY, { + flex: 1 + }); + control.addAt(footer, this.self().POS.FOOTER); + this._add(control, { + top: 0, + right: 0, + bottom: 0, + left: 0 + }); + break; + } + case "header": + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)).set({ + backgroundColor: "background-card-overlay", + anonymous: true, + maxWidth: this.self().ITEM_WIDTH, + maxHeight: this.self().HEADER_MAX_HEIGHT, + padding: this.self().PADDING, + alignY: "middle", + }); + break; + case "body": + control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({ + decorator: "main", + allowGrowY: true, + allowGrowX: true, + allowShrinkX: true, + padding: this.self().PADDING + }); + control.getContentElement().setStyles({ + "border-width": 0 + }); + break; + case "footer": { + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)).set({ + backgroundColor: "background-card-overlay", + anonymous: true, + maxWidth: this.self().ITEM_WIDTH, + maxHeight: this.self().ITEM_HEIGHT, + padding: this.self().PADDING, + alignY: "middle", + }); + break; + } + case "title": + control = new qx.ui.basic.Label().set({ + textColor: "contrasted-text-light", + font: "text-14", + allowGrowX: true, + maxHeight: this.self().HEADER_MAX_HEIGHT + }); + layout = this.getChildControl("header"); + layout.addAt(control, this.self().HPOS.TITLE, { + flex: 1 + }); + break; + case "icon": { + layout = this.getChildControl("body"); + const maxWidth = this.self().ITEM_WIDTH; + control = new osparc.ui.basic.Thumbnail(null, maxWidth, 124); + control.getChildControl("image").set({ + anonymous: true, + alignY: "middle", + alignX: "center", + allowGrowX: true, + allowGrowY: true + }); + layout.getContentElement().setStyles({ + "border-width": "0px" + }); + layout.add(control, {flex: 1}); + break; + } + } + return control || this.base(arguments, id); + }, + + // overridden + _applyIcon: function(value) { + if ( + value.includes("@FontAwesome5Solid/") || + value.includes("@MaterialIcons/") + ) { + value += this.self().ICON_SIZE; + const image = this.getChildControl("icon").getChildControl("image"); + image.set({ + source: value + }); + } else { + this.getContentElement().setStyles({ + "background-image": `url(${value})`, + "background-repeat": "no-repeat", + "background-size": "cover", // auto width, 85% height + "background-position": "center center", + "background-origin": "border-box" + }); + } + }, + + /** + * Event handler for the pointer over event. + */ + _onPointerOver: function() { + this.addState("hovered"); + }, + + /** + * Event handler for the pointer out event. + */ + _onPointerOut : function() { + this.removeState("hovered"); + }, + + _filter: function() { + this.exclude(); + }, + + _unfilter: function() { + this.show(); + }, + + _shouldApplyFilter: function(data) { + console.log("_shouldApplyFilter", data); + return false; + }, + + _shouldReactToFilter: function(data) { + console.log("_shouldReactToFilter", data); + return false; + } + }, + + destruct: function() { + this.removeListener("pointerover", this._onPointerOver, this); + this.removeListener("pointerout", this._onPointerOut, this); + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js new file mode 100644 index 00000000000..176d2775f46 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js @@ -0,0 +1,283 @@ +/* ************************************************************************ + + 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) + +************************************************************************ */ + +/** + * Widget used for displaying a Workspace in the Study Browser + * + */ + +qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { + extend: osparc.dashboard.WorkspaceButtonBase, + + /** + * @param workspace {osparc.data.model.Workspace} + */ + construct: function(workspace) { + this.base(arguments); + + this.set({ + appearance: "pb-listitem" + }); + + this.addListener("changeValue", e => this.__itemSelected(e.getData()), this); + + this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); + + this.set({ + workspace: workspace + }); + }, + + events: { + "workspaceSelected": "qx.event.type.Data", + "workspaceUpdated": "qx.event.type.Data", + "deleteWorkspaceRequested": "qx.event.type.Data" + }, + + properties: { + workspace: { + check: "osparc.data.model.Workspace", + nullable: false, + init: null, + apply: "__applyWorkspace" + }, + + workspaceId: { + check: "Number", + nullable: false + }, + + title: { + check: "String", + nullable: true, + apply: "__applyTitle" + }, + + description: { + check: "String", + nullable: true, + apply: "__applyDescription" + }, + + myAccessRights: { + check: "Object", + nullable: true, + apply: "__applyMyAccessRights" + }, + + accessRights: { + check: "Object", + nullable: true, + apply: "__applyAccessRights" + }, + + lastModified: { + check: "Date", + nullable: true, + apply: "__applyLastModified" + } + }, + + statics: { + MENU_BTN_DIMENSIONS: 24 + }, + + members: { + _createChildControlImpl: function(id) { + let control; + let layout; + switch (id) { + case "shared-icon": + control = new qx.ui.basic.Image().set({ + alignY: "middle", + allowGrowX: false, + allowShrinkX: false + }); + layout = this.getChildControl("header"); + layout.addAt(control, osparc.dashboard.WorkspaceButtonBase.HPOS.SHARED); + break; + case "menu-button": + control = new qx.ui.form.MenuButton().set({ + appearance: "form-button-outlined", + minWidth: this.self().MENU_BTN_DIMENSIONS, + minHeight: this.self().MENU_BTN_DIMENSIONS, + width: this.self().MENU_BTN_DIMENSIONS, + height: this.self().MENU_BTN_DIMENSIONS, + padding: [0, 8, 0, 8], + alignX: "center", + alignY: "middle", + icon: "@FontAwesome5Solid/ellipsis-v/14", + focusable: false + }); + // make it circular + control.getContentElement().setStyles({ + "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` + }); + layout = this.getChildControl("header"); + layout.addAt(control, osparc.dashboard.WorkspaceButtonBase.HPOS.MENU); + break; + case "modified-text": + control = new qx.ui.basic.Label().set({ + textColor: "contrasted-text-dark", + alignY: "middle", + rich: true, + anonymous: true, + font: "text-12", + allowGrowY: false + }); + layout = this.getChildControl("footer"); + layout.addAt(control, osparc.dashboard.WorkspaceButtonBase.FPOS.MODIFIED); + break; + } + return control || this.base(arguments, id); + }, + + __applyWorkspace: function(workspace) { + this.set({ + cardKey: "workspace-" + workspace.getWorkspaceId() + }); + workspace.bind("workspaceId", this, "workspaceId"); + workspace.bind("name", this, "title"); + workspace.bind("description", this, "description"); + workspace.bind("thumbnail", this, "icon", { + converter: thumbnail => thumbnail ? thumbnail : osparc.store.Workspaces.iconPath(-1) + }); + workspace.bind("accessRights", this, "accessRights"); + workspace.bind("lastModified", this, "lastModified"); + workspace.bind("myAccessRights", this, "myAccessRights"); + }, + + __applyTitle: function(value) { + const label = this.getChildControl("title"); + label.setValue(value); + this.__updateTooltip(); + }, + + __applyDescription: function() { + this.__updateTooltip(); + }, + + __applyMyAccessRights: function(value) { + if (value && value["delete"]) { + const menuButton = this.getChildControl("menu-button"); + menuButton.setVisibility("visible"); + + const menu = new qx.ui.menu.Menu().set({ + position: "bottom-right" + }); + + const editButton = new qx.ui.menu.Button(this.tr("Rename..."), "@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() + }); + 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 updateData = { + "name": newName, + "description": newDescription + }; + osparc.data.model.Workspace.putWorkspace(this.getWorkspaceId(), updateData) + .then(() => { + workspace.set({ + name: newName, + description: newDescription + }); + this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId()); + }) + .catch(err => console.error(err)); + win.close(); + }); + workspaceEditor.addListener("cancel", () => win.close()); + }); + menu.add(editButton); + + const shareButton = new qx.ui.menu.Button(this.tr("Share..."), "@FontAwesome5Solid/share-alt/12"); + shareButton.addListener("execute", () => this.__openShareWith(), this); + menu.add(shareButton); + + menu.addSeparator(); + + const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12"); + deleteButton.addListener("execute", () => this.__deleteWorkspaceRequested(), this); + menu.add(deleteButton); + + menuButton.setMenu(menu); + } + }, + + __applyAccessRights: function(value) { + if (value && Object.keys(value).length) { + const shareIcon = this.getChildControl("shared-icon"); + shareIcon.addListener("tap", e => { + e.stopPropagation(); + this.__openShareWith(); + }, this); + shareIcon.addListener("pointerdown", e => e.stopPropagation()); + osparc.dashboard.CardBase.populateShareIcon(shareIcon, value); + } + }, + + __applyLastModified: function(value) { + const label = this.getChildControl("modified-text"); + label.setValue(osparc.utils.Utils.formatDateAndTime(value)); + }, + + __updateTooltip: function() { + const toolTipText = this.getTitle() + (this.getDescription() ? "
" + this.getDescription() : ""); + this.set({ + toolTipText + }) + }, + + __itemSelected: function(newVal) { + if (newVal) { + this.fireDataEvent("workspaceSelected", this.getWorkspaceId()); + } + this.setValue(false); + }, + + __openShareWith: function() { + const permissionsView = new osparc.share.CollaboratorsWorkspace(this.getWorkspace()); + const title = this.tr("Share Workspace"); + osparc.ui.window.Window.popUpInWindow(permissionsView, title, 500, 400); + permissionsView.addListener("updateAccessRights", () => this.__applyAccessRights(this.getWorkspace().getAccessRights()), this); + }, + + __deleteWorkspaceRequested: function() { + let msg = this.tr("Are you sure you want to delete") + " " + this.getTitle() + "?"; + msg += "
" + this.tr("All the content of the workspace will be deleted."); + const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ + confirmText: this.tr("Delete"), + confirmAction: "delete" + }); + confirmationWin.center(); + confirmationWin.open(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + this.fireDataEvent("deleteWorkspaceRequested", this.getWorkspaceId()); + } + }, this); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js new file mode 100644 index 00000000000..b06dd548529 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js @@ -0,0 +1,78 @@ +/* ************************************************************************ + + 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) + +************************************************************************ */ + +/** + * Widget used for displaying a New Workspace in the Study Browser + * + */ + +qx.Class.define("osparc.dashboard.WorkspaceButtonNew", { + extend: osparc.dashboard.WorkspaceButtonBase, + + construct: function() { + this.base(arguments); + + this.set({ + appearance: "pb-new" + }); + + this.addListener("changeValue", e => this.__itemSelected(e.getData()), this); + + this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.NEW); + + const title = this.getChildControl("title"); + title.setValue(this.tr("New Workspace")); + + this.setIcon(osparc.dashboard.CardBase.NEW_ICON); + + this.getChildControl("footer").exclude(); + }, + + events: { + "createWorkspace": "qx.event.type.Data", + "updateWorkspace": "qx.event.type.Data" + }, + + members: { + __itemSelected: function(newVal) { + if (newVal) { + const workspaceEditor = new osparc.editor.WorkspaceEditor(true); + const title = this.tr("New Workspace"); + const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 200); + workspaceEditor.addListener("createWorkspace", () => { + const newWorkspaceData = { + name: workspaceEditor.getLabel(), + description: workspaceEditor.getDescription(), + thumbnail: workspaceEditor.getThumbnail(), + }; + osparc.store.Workspaces.postWorkspace(newWorkspaceData) + .then(newWorkspace => { + this.fireDataEvent("createWorkspace"); + const permissionsView = new osparc.share.CollaboratorsWorkspace(newWorkspace); + const title2 = qx.locale.Manager.tr("Share Workspace"); + osparc.ui.window.Window.popUpInWindow(permissionsView, title2, 500, 500); + permissionsView.addListener("updateAccessRights", () => this.fireDataEvent("updateWorkspace", newWorkspace.getWorkspaceId()), this); + }) + .catch(console.error) + .finally(() => win.close()); + }); + workspaceEditor.addListener("cancel", () => win.close()); + } + this.setValue(false); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderWithSharedIcon.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceWithSharedIcon.js similarity index 95% rename from services/static-webserver/client/source/class/osparc/dashboard/FolderWithSharedIcon.js rename to services/static-webserver/client/source/class/osparc/dashboard/WorkspaceWithSharedIcon.js index af6573fb16f..2e3b8a88d89 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderWithSharedIcon.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceWithSharedIcon.js @@ -15,7 +15,7 @@ ************************************************************************ */ -qx.Class.define("osparc.dashboard.FolderWithSharedIcon", { +qx.Class.define("osparc.dashboard.WorkspaceWithSharedIcon", { extend: qx.ui.core.Widget, construct: function() { 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 44ea7b20671..098d3239bfa 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -319,6 +319,42 @@ qx.Class.define("osparc.data.Resources", { } } }, + "workspaces": { + endpoints: { + getPage: { + method: "GET", + url: statics.API + "/workspaces?workspace_id={workspaceId}&offset={offset}&limit={limit}" + }, + getOne: { + method: "GET", + url: statics.API + "/workspaces/{workspaceId}" + }, + post: { + method: "POST", + url: statics.API + "/workspaces" + }, + update: { + method: "PUT", + url: statics.API + "/workspaces/{workspaceId}" + }, + delete: { + method: "DELETE", + url: statics.API + "/workspaces/{workspaceId}" + }, + postAccessRights: { + method: "POST", + url: statics.API + "/workspaces/{workspaceId}/groups/{groupId}" + }, + putAccessRights: { + method: "PUT", + url: statics.API + "/workspaces/{workspaceId}/groups/{groupId}" + }, + deleteAccessRights: { + method: "DELETE", + url: statics.API + "/workspaces/{workspaceId}/groups/{groupId}" + }, + } + }, "resourceUsage": { useCache: false, endpoints: { @@ -470,7 +506,7 @@ qx.Class.define("osparc.data.Resources", { * SERVICES V2 (web-api >=0.42.0) */ "servicesV2": { - useCache: false, // handled in osparc.service.Store + useCache: false, // handled in osparc.store.Services idField: ["key", "version"], endpoints: { get: { diff --git a/services/static-webserver/client/source/class/osparc/data/Roles.js b/services/static-webserver/client/source/class/osparc/data/Roles.js index 5d5a1d7c017..ccb66ffb7d7 100644 --- a/services/static-webserver/client/source/class/osparc/data/Roles.js +++ b/services/static-webserver/client/source/class/osparc/data/Roles.js @@ -135,7 +135,7 @@ qx.Class.define("osparc.data.Roles", { } }, - FOLDERS: { + WORKSPACE: { 1: { id: "read", label: qx.locale.Manager.tr("Viewer"), @@ -158,7 +158,7 @@ qx.Class.define("osparc.data.Roles", { label: qx.locale.Manager.tr("Owner"), longLabel: qx.locale.Manager.tr("Owner: Read/Write/Delete access"), canDo: [ - qx.locale.Manager.tr("- can rename folder"), + qx.locale.Manager.tr("- can rename workspace"), qx.locale.Manager.tr("- can share it"), qx.locale.Manager.tr("- can delete it") ] @@ -216,8 +216,8 @@ qx.Class.define("osparc.data.Roles", { return this.__createIntoFromRoles(osparc.data.Roles.SERVICES); }, - createRolesFolderInfo: function(showWording = true) { - return this.__createIntoFromRoles(osparc.data.Roles.FOLDERS, showWording); + createRolesWorkspaceInfo: function(showWording = true) { + return this.__createIntoFromRoles(osparc.data.Roles.WORKSPACE, showWording); } } }); 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 9b8f7bcc630..a3e1eb7ca90 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 @@ -16,7 +16,7 @@ ************************************************************************ */ /** - * Class that stores Service data. + * Class that stores Folder data. */ qx.Class.define("osparc.data.model.Folder", { diff --git a/services/static-webserver/client/source/class/osparc/data/model/Node.js b/services/static-webserver/client/source/class/osparc/data/model/Node.js index a6cf28b8af3..6373d8835ca 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Node.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Node.js @@ -382,7 +382,7 @@ qx.Class.define("osparc.data.model.Node", { __applyNewMetaData: function(newV, oldV) { if (oldV !== null) { - const metadata = osparc.service.Store.getMetadata(this.getKey(), this.getVersion()); + const metadata = osparc.store.Services.getMetadata(this.getKey(), this.getVersion()); if (metadata) { this.__metaData = metadata; } 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 6c16d1a9851..8fd40a67a28 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 @@ -299,7 +299,7 @@ qx.Class.define("osparc.data.model.Study", { let nCompNodes = 0; let overallProgress = 0; Object.values(nodes).forEach(node => { - const metadata = osparc.service.Store.getMetadata(node["key"], node["version"]); + const metadata = osparc.store.Services.getMetadata(node["key"], node["version"]); if (metadata && osparc.data.model.Node.isComputational(metadata)) { const progress = "progress" in node ? node["progress"] : 0; overallProgress += progress; diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js index 1110d55b736..3120a22f7f1 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js @@ -295,7 +295,7 @@ qx.Class.define("osparc.data.model.Workbench", { }; try { - const metadata = await osparc.service.Store.getService(key, version); + const metadata = await osparc.store.Services.getService(key, version); const resp = await osparc.data.Resources.fetch("studies", "addNode", params); const nodeId = resp["node_id"]; @@ -679,7 +679,7 @@ qx.Class.define("osparc.data.model.Workbench", { const metadataPromises = []; nodeIds.forEach(nodeId => { const nodeData = workbenchData[nodeId]; - metadataPromises.push(osparc.service.Store.getService(nodeData.key, nodeData.version)); + metadataPromises.push(osparc.store.Services.getService(nodeData.key, nodeData.version)); }); return Promise.all(metadataPromises) 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 new file mode 100644 index 00000000000..ea4c324e9aa --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/data/model/Workspace.js @@ -0,0 +1,121 @@ +/* ************************************************************************ + + 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) + +************************************************************************ */ + +/** + * Class that stores Workspace data. + */ + +qx.Class.define("osparc.data.model.Workspace", { + extend: qx.core.Object, + + /** + * @param workspaceData {Object} Object containing the serialized Workspace Data + */ + construct: function(workspaceData) { + this.base(arguments); + + this.set({ + workspaceId: workspaceData.workspaceId, + name: workspaceData.name, + description: workspaceData.description, + thumbnail: workspaceData.thumbnail, + myAccessRights: workspaceData.myAccessRights, + accessRights: workspaceData.accessRights, + createdAt: new Date(workspaceData.createdAt), + lastModified: new Date(workspaceData.lastModified), + }); + }, + + properties: { + workspaceId: { + check: "Number", + nullable: false, + init: null, + event: "changeId" + }, + + name: { + check: "String", + nullable: false, + init: null, + event: "changeName" + }, + + description: { + check: "String", + nullable: true, + init: null, + event: "changeDescription" + }, + + thumbnail: { + check: "String", + nullable: true, + init: null, + event: "changeThumbnail" + }, + + myAccessRights: { + check: "Object", + nullable: false, + init: null, + event: "changeMyAccessRights" + }, + + accessRights: { + check: "Object", + nullable: false, + init: null, + event: "changeAccessRights" + }, + + createdAt: { + check: "Date", + nullable: true, + init: null, + event: "changeCreatedAt" + }, + + lastModified: { + check: "Date", + nullable: true, + init: null, + event: "changeLastModified" + } + }, + + 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)); + } + }, + + members: { + serialize: function() { + const jsonObject = {}; + const propertyKeys = this.self().getProperties(); + propertyKeys.forEach(key => { + jsonObject[key] = this.get(key); + }); + return jsonObject; + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js index d6655889ed7..6a212375312 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js @@ -115,7 +115,7 @@ qx.Class.define("osparc.desktop.organizations.ServicesList", { } const gid = orgModel.getGid(); - osparc.service.Store.getServicesLatest() + osparc.store.Services.getServicesLatest() .then(servicesLatest => { const orgServices = []; Object.keys(servicesLatest).forEach(key => { diff --git a/services/static-webserver/client/source/class/osparc/editor/ThumbnailSuggestions.js b/services/static-webserver/client/source/class/osparc/editor/ThumbnailSuggestions.js index ff496032568..ba361f12f46 100644 --- a/services/static-webserver/client/source/class/osparc/editor/ThumbnailSuggestions.js +++ b/services/static-webserver/client/source/class/osparc/editor/ThumbnailSuggestions.js @@ -138,7 +138,7 @@ qx.Class.define("osparc.editor.ThumbnailSuggestions", { } const promises = []; - queryParams.forEach(qP => promises.push(osparc.service.Store.getService(qP.key, qP.version))); + queryParams.forEach(qP => promises.push(osparc.store.Services.getService(qP.key, qP.version))); return Promise.all(promises) .then(values => { const suggestions = new Set([]); diff --git a/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js b/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js new file mode 100644 index 00000000000..3a654307006 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js @@ -0,0 +1,162 @@ +/* ************************************************************************ + + 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.editor.WorkspaceEditor", { + extend: qx.ui.core.Widget, + + construct: function(newWorkspace = true) { + this.base(arguments); + + this._setLayout(new qx.ui.layout.VBox(8)); + + const manager = this.__validator = new qx.ui.form.validation.Manager(); + const title = this.getChildControl("title"); + title.setRequired(true); + manager.add(title); + this.getChildControl("description"); + this.getChildControl("thumbnail"); + newWorkspace ? this.getChildControl("create") : this.getChildControl("save"); + + this.addListener("appear", this.__onAppear, this); + }, + + properties: { + label: { + check: "String", + init: "", + nullable: false, + event: "changeLabel" + }, + + description: { + check: "String", + init: "", + nullable: false, + event: "changeDescription" + }, + + thumbnail: { + check: "String", + init: "", + nullable: false, + event: "changeThumbnail" + }, + + accessRights: { + check: "Object", + init: {}, + nullable: false, + event: "changeAccessRights", + apply: "applyAccessRights" + } + }, + + events: { + "createWorkspace": "qx.event.type.Event", + "updateWorkspace": "qx.event.type.Event", + "cancel": "qx.event.type.Event" + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "title": { + control = new qx.ui.form.TextField().set({ + font: "text-14", + backgroundColor: "background-main", + placeholder: this.tr("Title"), + }); + this.bind("label", control, "value"); + control.bind("value", this, "label"); + this._add(control); + break; + } + case "description": { + control = new qx.ui.form.TextArea().set({ + font: "text-14", + placeholder: this.tr("Description"), + autoSize: true, + minHeight: 70, + }); + this.bind("description", control, "value"); + control.bind("value", this, "description"); + this._add(control); + break; + } + case "thumbnail": { + control = new qx.ui.form.TextField().set({ + font: "text-14", + placeholder: this.tr("Thumbnail"), + }); + this.bind("thumbnail", control, "value"); + control.bind("value", this, "thumbnail"); + this._add(control); + break; + } + case "create": { + const buttons = this.getChildControl("buttonsLayout"); + control = new osparc.ui.form.FetchButton(this.tr("Create")).set({ + appearance: "form-button" + }); + control.addListener("execute", () => { + if (this.__validator.validate()) { + control.setFetching(true); + this.fireEvent("createWorkspace"); + } + }, this); + buttons.addAt(control, 1); + break; + } + case "save": { + const buttons = this.getChildControl("buttonsLayout"); + control = new osparc.ui.form.FetchButton(this.tr("Save")).set({ + appearance: "form-button" + }); + control.addListener("execute", () => { + if (this.__validator.validate()) { + control.setFetching(true); + this.fireEvent("updateWorkspace"); + } + }, this); + buttons.addAt(control, 1); + break; + } + case "buttonsLayout": { + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(8).set({ + alignX: "right" + })); + const cancelButton = new qx.ui.form.Button(this.tr("Cancel")).set({ + appearance: "form-button-text" + }); + cancelButton.addListener("execute", () => this.fireEvent("cancel"), this); + control.addAt(cancelButton, 0); + this._add(control); + break; + } + } + + return control || this.base(arguments, id); + }, + + __onAppear: function() { + const title = this.getChildControl("title"); + title.focus(); + title.activate(); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/info/MergedLarge.js b/services/static-webserver/client/source/class/osparc/info/MergedLarge.js index cefe7e01c9a..d85ffef353d 100644 --- a/services/static-webserver/client/source/class/osparc/info/MergedLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/MergedLarge.js @@ -325,7 +325,7 @@ qx.Class.define("osparc.info.MergedLarge", { }; promise = osparc.data.Resources.get("nodesInStudyResources", params); } else { - promise = osparc.service.Store.getResources(this.getNode().getKey(), this.getNode().getVersion()) + promise = osparc.store.Services.getResources(this.getNode().getKey(), this.getNode().getVersion()) } promise .then(serviceResources => { diff --git a/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js b/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js index efdccc3ad7d..9eb0bddf435 100644 --- a/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js @@ -360,7 +360,7 @@ qx.Class.define("osparc.info.ServiceLarge", { }; promise = osparc.data.Resources.get("nodesInStudyResources", params); } else { - promise = osparc.service.Store.getResources(this.getService()["key"], this.getService()["version"]) + promise = osparc.store.Services.getResources(this.getService()["key"], this.getService()["version"]) } promise .then(serviceResources => { @@ -474,7 +474,7 @@ qx.Class.define("osparc.info.ServiceLarge", { __patchService: function(key, value) { const serviceDataCopy = osparc.utils.Utils.deepCloneObject(this.getService()); - osparc.service.Store.patchServiceData(serviceDataCopy, key, value) + osparc.store.Services.patchServiceData(serviceDataCopy, key, value) .then(() => { this.setService(serviceDataCopy); this.fireDataEvent("updateService", this.getService()); 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 eb1f70913c5..abbee546dae 100644 --- a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js +++ b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js @@ -370,7 +370,7 @@ qx.Class.define("osparc.info.StudyUtils", { openAccessRights: function(studyData) { const permissionsView = new osparc.share.CollaboratorsStudy(studyData); const title = qx.locale.Manager.tr("Share with Editors and Organizations"); - osparc.ui.window.Window.popUpInWindow(permissionsView, title, 500, 400); + osparc.ui.window.Window.popUpInWindow(permissionsView, title, 500, 500); return permissionsView; }, diff --git a/services/static-webserver/client/source/class/osparc/metadata/ClassifiersEditor.js b/services/static-webserver/client/source/class/osparc/metadata/ClassifiersEditor.js index ee9c7328b46..3f66e39d7ec 100644 --- a/services/static-webserver/client/source/class/osparc/metadata/ClassifiersEditor.js +++ b/services/static-webserver/client/source/class/osparc/metadata/ClassifiersEditor.js @@ -158,7 +158,7 @@ qx.Class.define("osparc.metadata.ClassifiersEditor", { }); } else { const serviceDataCopy = osparc.utils.Utils.deepCloneObject(this.__resourceData); - osparc.service.Store.patchServiceData(serviceDataCopy, "classifiers", newClassifiers) + osparc.store.Services.patchServiceData(serviceDataCopy, "classifiers", newClassifiers) .then(() => { osparc.FlashMessenger.getInstance().logAs(this.tr("Classifiers successfully edited")); saveBtn.setFetching(false); diff --git a/services/static-webserver/client/source/class/osparc/metadata/QualityEditor.js b/services/static-webserver/client/source/class/osparc/metadata/QualityEditor.js index 5ac435dcea7..1b1000e5d68 100644 --- a/services/static-webserver/client/source/class/osparc/metadata/QualityEditor.js +++ b/services/static-webserver/client/source/class/osparc/metadata/QualityEditor.js @@ -457,7 +457,7 @@ qx.Class.define("osparc.metadata.QualityEditor", { btn.setFetching(true); if (osparc.utils.Resources.isService(this.__resourceData)) { const serviceDataCopy = osparc.utils.Utils.deepCloneObject(this.__resourceData); - osparc.service.Store.patchServiceData(serviceDataCopy, "quality", newQuality) + osparc.store.Services.patchServiceData(serviceDataCopy, "quality", newQuality) .then(() => { this.__initResourceData(serviceDataCopy); this.fireDataEvent("updateQuality", serviceDataCopy); diff --git a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js index 03802472180..9f1dbe17600 100644 --- a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js +++ b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js @@ -45,7 +45,7 @@ qx.Class.define("osparc.metadata.ServicesInStudy", { const servicesInStudy = osparc.study.Utils.extractServices(this._studyData["workbench"]); if (servicesInStudy.length) { const promises = []; - servicesInStudy.forEach(srv => promises.push(osparc.service.Store.getService(srv.key, srv.version))); + servicesInStudy.forEach(srv => promises.push(osparc.store.Services.getService(srv.key, srv.version))); Promise.all(promises) .then(() => this._populateLayout()); } else { @@ -137,7 +137,7 @@ qx.Class.define("osparc.metadata.ServicesInStudy", { const infoButton = new qx.ui.form.Button(null, "@MaterialIcons/info_outline/14"); infoButton.addListener("execute", () => { - const metadata = osparc.service.Store.getMetadata(node["key"], node["version"]); + const metadata = osparc.store.Services.getMetadata(node["key"], node["version"]); if (metadata === null) { osparc.FlashMessenger.logAs(this.tr("Service information could not be retrieved"), "WARNING"); return; @@ -167,7 +167,7 @@ qx.Class.define("osparc.metadata.ServicesInStudy", { }); this.__grid.setRowHeight(i, 24); - const nodeMetadata = osparc.service.Store.getMetadata(node["key"], node["version"]); + const nodeMetadata = osparc.store.Services.getMetadata(node["key"], node["version"]); if (nodeMetadata === null) { osparc.FlashMessenger.logAs(this.tr("Some service information could not be retrieved"), "WARNING"); break; diff --git a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyBootOpts.js b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyBootOpts.js index 5f3802d1267..72ad2d4530e 100644 --- a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyBootOpts.js +++ b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyBootOpts.js @@ -25,7 +25,7 @@ qx.Class.define("osparc.metadata.ServicesInStudyBootOpts", { if ("workbench" in studyData) { for (const nodeId in studyData["workbench"]) { const node = studyData["workbench"][nodeId]; - const metadata = osparc.service.Store.getMetadata(node["key"], node["version"]); + const metadata = osparc.store.Services.getMetadata(node["key"], node["version"]); if (metadata && osparc.data.model.Node.hasBootModes(metadata)) { return true; } @@ -74,7 +74,7 @@ qx.Class.define("osparc.metadata.ServicesInStudyBootOpts", { for (const nodeId in workbench) { i++; const node = workbench[nodeId]; - const nodeMetadata = osparc.service.Store.getMetadata(node["key"], node["version"]); + const nodeMetadata = osparc.store.Services.getMetadata(node["key"], node["version"]); if (nodeMetadata === null) { osparc.FlashMessenger.logAs(this.tr("Some service information could not be retrieved"), "WARNING"); break; diff --git a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyUpdate.js b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyUpdate.js index 3ab2f4b87a1..bbd9685bfec 100644 --- a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyUpdate.js +++ b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyUpdate.js @@ -205,7 +205,7 @@ qx.Class.define("osparc.metadata.ServicesInStudyUpdate", { for (const nodeId in workbench) { i++; const node = workbench[nodeId]; - const metadata = osparc.service.Store.getMetadata(node["key"], node["version"]); + const metadata = osparc.store.Services.getMetadata(node["key"], node["version"]); const currentVersionLabel = new qx.ui.basic.Label(osparc.service.Utils.extractVersionDisplay(metadata)).set({ font: "text-14" }); @@ -221,7 +221,7 @@ qx.Class.define("osparc.metadata.ServicesInStudyUpdate", { const latestCompatible = osparc.service.Utils.getLatestCompatible(node["key"], node["version"]); if (latestCompatible) { // updatable - osparc.service.Store.getService(latestCompatible["key"], latestCompatible["version"]) + osparc.store.Services.getService(latestCompatible["key"], latestCompatible["version"]) .then(latestMetadata => { let label = osparc.service.Utils.extractVersionDisplay(latestMetadata) if (node["key"] !== latestMetadata["key"]) { diff --git a/services/static-webserver/client/source/class/osparc/pricing/ServicesList.js b/services/static-webserver/client/source/class/osparc/pricing/ServicesList.js index f3b5c60949b..eb215da3c76 100644 --- a/services/static-webserver/client/source/class/osparc/pricing/ServicesList.js +++ b/services/static-webserver/client/source/class/osparc/pricing/ServicesList.js @@ -83,7 +83,7 @@ qx.Class.define("osparc.pricing.ServicesList", { services.forEach(service => { const key = service["serviceKey"]; const version = service["serviceVersion"]; - metadataPromises.push(osparc.service.Store.getService(key, version)); + metadataPromises.push(osparc.store.Services.getService(key, version)); }); Promise.all(metadataPromises) .catch(err => console.error(err)) @@ -92,7 +92,7 @@ qx.Class.define("osparc.pricing.ServicesList", { services.forEach(service => { const key = service["serviceKey"]; const version = service["serviceVersion"]; - const serviceMetadata = osparc.service.Store.getMetadata(key, version); + const serviceMetadata = osparc.store.Services.getMetadata(key, version); if (serviceMetadata) { sList.push(new osparc.data.model.Service(serviceMetadata)); } diff --git a/services/static-webserver/client/source/class/osparc/service/ServiceListItem.js b/services/static-webserver/client/source/class/osparc/service/ServiceListItem.js index 543e3d0b11a..059dd840170 100644 --- a/services/static-webserver/client/source/class/osparc/service/ServiceListItem.js +++ b/services/static-webserver/client/source/class/osparc/service/ServiceListItem.js @@ -158,7 +158,7 @@ qx.Class.define("osparc.service.ServiceListItem", { if (version === this.self().LATEST) { version = this.__versionsBox.getChildrenContainer().getSelectables()[1].version; } - osparc.service.Store.getService(key, version) + osparc.store.Services.getService(key, version) .then(serviceMetadata => { const serviceDetails = new osparc.info.ServiceLarge(serviceMetadata); const title = this.tr("Service information"); diff --git a/services/static-webserver/client/source/class/osparc/service/Utils.js b/services/static-webserver/client/source/class/osparc/service/Utils.js index 38ce2b31905..b47f3a5b5fa 100644 --- a/services/static-webserver/client/source/class/osparc/service/Utils.js +++ b/services/static-webserver/client/source/class/osparc/service/Utils.js @@ -65,8 +65,6 @@ qx.Class.define("osparc.service.Utils", { } }, - servicesCached: {}, - getTypes: function() { return Object.keys(this.TYPES); }, @@ -133,7 +131,7 @@ qx.Class.define("osparc.service.Utils", { }, getVersions: function(key, filterDeprecated = true) { - const services = osparc.service.Store.servicesCached; + const services = osparc.store.Services.servicesCached; let versions = []; if (key in services) { const serviceVersions = services[key]; @@ -152,7 +150,7 @@ qx.Class.define("osparc.service.Utils", { }, getLatest: function(key) { - const services = osparc.service.Store.servicesCached; + const services = osparc.store.Services.servicesCached; if (key in services) { const versions = this.getVersions(key, true); if (versions.length) { @@ -163,7 +161,7 @@ qx.Class.define("osparc.service.Utils", { }, getLatestCompatible: function(key, version) { - const services = osparc.service.Store.servicesCached; + const services = osparc.store.Services.servicesCached; if (key in services && version in services[key]) { const serviceMD = services[key][version]; if (serviceMD["compatibility"] && serviceMD["compatibility"]["canUpdateTo"]) { @@ -183,7 +181,7 @@ qx.Class.define("osparc.service.Utils", { }, getVersionDisplay: function(key, version) { - const services = osparc.service.Store.servicesCached; + const services = osparc.store.Services.servicesCached; if (key in services && version in services[key]) { return this.extractVersionDisplay(services[key][version]); } @@ -195,7 +193,7 @@ qx.Class.define("osparc.service.Utils", { }, getReleasedDate: function(key, version) { - const services = osparc.service.Store.servicesCached; + const services = osparc.store.Services.servicesCached; if ( key in services && version in services[key] && @@ -262,7 +260,8 @@ qx.Class.define("osparc.service.Utils", { getParametersMetadata: function() { const parametersMetadata = []; - for (const key in this.servicesCached) { + const services = osparc.store.Services.servicesCached; + for (const key in services) { if (key.includes("simcore/services/frontend/parameter/")) { const latest = this.self().getLatest(key); if (latest) { 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 47163863b82..c315c7d26d3 100644 --- a/services/static-webserver/client/source/class/osparc/share/Collaborators.js +++ b/services/static-webserver/client/source/class/osparc/share/Collaborators.js @@ -199,8 +199,8 @@ qx.Class.define("osparc.share.Collaborators", { case "service": fullOptions = osparc.service.Utils.canIWrite(this._serializedDataCopy["accessRights"]); break; - case "folder": - fullOptions = osparc.share.CollaboratorsFolder.canIDelete(this._serializedDataCopy["myAccessRights"]); + case "workspace": + fullOptions = osparc.share.CollaboratorsWorkspace.canIDelete(this._serializedDataCopy["myAccessRights"]); break; } return fullOptions; @@ -212,8 +212,8 @@ qx.Class.define("osparc.share.Collaborators", { case "service": rolesLayout = osparc.data.Roles.createRolesServicesInfo(); break; - case "folder": - rolesLayout = osparc.data.Roles.createRolesFolderInfo(); + case "workspace": + rolesLayout = osparc.data.Roles.createRolesWorkspaceInfo(); break; default: rolesLayout = osparc.data.Roles.createRolesStudyInfo(); diff --git a/services/static-webserver/client/source/class/osparc/share/CollaboratorsService.js b/services/static-webserver/client/source/class/osparc/share/CollaboratorsService.js index 7379a512b9b..4f540ebb211 100644 --- a/services/static-webserver/client/source/class/osparc/share/CollaboratorsService.js +++ b/services/static-webserver/client/source/class/osparc/share/CollaboratorsService.js @@ -90,7 +90,7 @@ qx.Class.define("osparc.share.CollaboratorsService", { gids.forEach(gid => { newAccessRights[gid] = this.self().getCollaboratorAccessRight(); }); - osparc.service.Store.patchServiceData(this._serializedDataCopy, "accessRights", newAccessRights) + osparc.store.Services.patchServiceData(this._serializedDataCopy, "accessRights", newAccessRights) .then(() => { this.fireDataEvent("updateAccessRights", this._serializedDataCopy); let text = this.tr("Editor(s) successfully added."); @@ -119,7 +119,7 @@ qx.Class.define("osparc.share.CollaboratorsService", { return; } - osparc.service.Store.patchServiceData(this._serializedDataCopy, "accessRights", this._serializedDataCopy["accessRights"]) + osparc.store.Services.patchServiceData(this._serializedDataCopy, "accessRights", this._serializedDataCopy["accessRights"]) .then(() => { this.fireDataEvent("updateAccessRights", this._serializedDataCopy); osparc.FlashMessenger.getInstance().logAs(this.tr("Member successfully removed")); @@ -139,7 +139,7 @@ qx.Class.define("osparc.share.CollaboratorsService", { __make: function(collaboratorGId, newAccessRights, successMsg, failureMsg, item) { item.setEnabled(false); this._serializedDataCopy["accessRights"][collaboratorGId] = newAccessRights; - osparc.service.Store.patchServiceData(this._serializedDataCopy, "accessRights", this._serializedDataCopy["accessRights"]) + osparc.store.Services.patchServiceData(this._serializedDataCopy, "accessRights", this._serializedDataCopy["accessRights"]) .then(() => { this.fireDataEvent("updateAccessRights", this._serializedDataCopy); osparc.FlashMessenger.getInstance().logAs(successMsg); diff --git a/services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js b/services/static-webserver/client/source/class/osparc/share/CollaboratorsWorkspace.js similarity index 69% rename from services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js rename to services/static-webserver/client/source/class/osparc/share/CollaboratorsWorkspace.js index d68df6a9dde..202d86b5e5d 100644 --- a/services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js +++ b/services/static-webserver/client/source/class/osparc/share/CollaboratorsWorkspace.js @@ -16,18 +16,18 @@ ************************************************************************ */ -qx.Class.define("osparc.share.CollaboratorsFolder", { +qx.Class.define("osparc.share.CollaboratorsWorkspace", { extend: osparc.share.Collaborators, /** - * @param folder {osparc.data.model.Folder} + * @param workspace {osparc.data.model.Workspace} */ - construct: function(folder) { - this.__folder = folder; - this._resourceType = "folder"; + construct: function(workspace) { + this.__workspace = workspace; + this._resourceType = "workspace"; - const folderDataCopy = folder.serialize(); - this.base(arguments, folderDataCopy, []); + const workspaceDataCopy = workspace.serialize(); + this.base(arguments, workspaceDataCopy, []); }, statics: { @@ -61,7 +61,7 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { }, members: { - __folder: null, + __workspace: null, _addEditors: function(gids) { if (gids.length === 0) { @@ -70,9 +70,9 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { const newCollaborators = {}; gids.forEach(gid => newCollaborators[gid] = this.self().getCollaboratorAccessRight()); - osparc.store.Folders.getInstance().addCollaborators(this.__folder.getFolderId(), newCollaborators) + osparc.store.Workspaces.addCollaborators(this.__workspace.getWorkspaceId(), newCollaborators) .then(() => { - this.fireDataEvent("updateAccessRights", this.__folder.serialize()); + this.fireDataEvent("updateAccessRights", this.__workspace.serialize()); const text = this.tr("User(s) successfully added."); osparc.FlashMessenger.getInstance().logAs(text); this._reloadCollaboratorsList(); @@ -88,9 +88,9 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { item.setEnabled(false); } - osparc.store.Folders.getInstance().removeCollaborator(this.__folder.getFolderId(), collaborator["gid"]) + osparc.store.Workspaces.removeCollaborator(this.__workspace.getWorkspaceId(), collaborator["gid"]) .then(() => { - this.fireDataEvent("updateAccessRights", this.__folder.serialize()); + this.fireDataEvent("updateAccessRights", this.__workspace.serialize()); osparc.FlashMessenger.getInstance().logAs(this.tr("Member successfully removed")); this._reloadCollaboratorsList(); }) @@ -108,9 +108,9 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { __make: function(collaboratorGId, newAccessRights, successMsg, failureMsg, item) { item.setEnabled(false); - osparc.store.Folders.getInstance().updateCollaborator(this.__folder.getFolderId(), collaboratorGId, newAccessRights) + osparc.store.Workspaces.updateCollaborator(this.__workspace.getWorkspaceId(), collaboratorGId, newAccessRights) .then(() => { - this.fireDataEvent("updateAccessRights", this.__folder.serialize()); + this.fireDataEvent("updateAccessRights", this.__workspace.serialize()); osparc.FlashMessenger.getInstance().logAs(successMsg); this._reloadCollaboratorsList(); }) @@ -129,8 +129,8 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { this.__make( collaborator["gid"], this.self().getCollaboratorAccessRight(), - this.tr(`${osparc.data.Roles.FOLDERS[1].label} successfully changed ${osparc.data.Roles.FOLDERS[2].label}`), - this.tr(`Something went wrong changing ${osparc.data.Roles.FOLDERS[1].label} to ${osparc.data.Roles.FOLDERS[2].label}`), + this.tr(`${osparc.data.Roles.WORKSPACE[1].label} successfully changed ${osparc.data.Roles.WORKSPACE[2].label}`), + this.tr(`Something went wrong changing ${osparc.data.Roles.WORKSPACE[1].label} to ${osparc.data.Roles.WORKSPACE[2].label}`), item ); }, @@ -139,8 +139,8 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { this.__make( collaborator["gid"], this.self().getOwnerAccessRight(), - this.tr(`${osparc.data.Roles.FOLDERS[2].label} successfully changed to ${osparc.data.Roles.FOLDERS[3].label}`), - this.tr(`Something went wrong changing ${osparc.data.Roles.FOLDERS[2].label} to ${osparc.data.Roles.FOLDERS[3].label}`), + this.tr(`${osparc.data.Roles.WORKSPACE[2].label} successfully changed to ${osparc.data.Roles.WORKSPACE[3].label}`), + this.tr(`Something went wrong changing ${osparc.data.Roles.WORKSPACE[2].label} to ${osparc.data.Roles.WORKSPACE[3].label}`), item ); }, @@ -151,8 +151,8 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { this.__make( gid, this.self().getViewerAccessRight(), - this.tr(`${osparc.data.Roles.FOLDERS[2].label} successfully changed to ${osparc.data.Roles.FOLDERS[1].label}`), - this.tr(`Something went wrong changing ${osparc.data.Roles.FOLDERS[2].label} to ${osparc.data.Roles.FOLDERS[1].label}`), + this.tr(`${osparc.data.Roles.WORKSPACE[2].label} successfully changed to ${osparc.data.Roles.WORKSPACE[1].label}`), + this.tr(`Something went wrong changing ${osparc.data.Roles.WORKSPACE[2].label} to ${osparc.data.Roles.WORKSPACE[1].label}`), itm ); }; @@ -160,7 +160,7 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { const groupData = await osparc.store.Store.getInstance().getGroup(groupId); const isOrganization = (groupData && !("id" in groupData)); if (isOrganization) { - const msg = this.tr(`Demoting to ${osparc.data.Roles.FOLDERS[1].label} will remove write access to all the members of the Organization. Are you sure?`); + const msg = this.tr(`Demoting to ${osparc.data.Roles.WORKSPACE[1].label} will remove write access to all the members of the Organization. Are you sure?`); const win = new osparc.ui.window.Confirmation(msg).set({ confirmAction: "delete", confirmText: this.tr("Yes") @@ -181,8 +181,8 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { this.__make( collaborator["gid"], this.self().getCollaboratorAccessRight(), - this.tr(`${osparc.data.Roles.FOLDERS[3].label} successfully changed to ${osparc.data.Roles.FOLDERS[2].label}`), - this.tr(`Something went wrong changing ${osparc.data.Roles.FOLDERS[3].label} to ${osparc.data.Roles.FOLDERS[2].label}`), + this.tr(`${osparc.data.Roles.WORKSPACE[3].label} successfully changed to ${osparc.data.Roles.WORKSPACE[2].label}`), + this.tr(`Something went wrong changing ${osparc.data.Roles.WORKSPACE[3].label} to ${osparc.data.Roles.WORKSPACE[2].label}`), item ); } diff --git a/services/static-webserver/client/source/class/osparc/share/ShareePermissions.js b/services/static-webserver/client/source/class/osparc/share/ShareePermissions.js index 4df66b06ca6..c0c4a4c6b89 100644 --- a/services/static-webserver/client/source/class/osparc/share/ShareePermissions.js +++ b/services/static-webserver/client/source/class/osparc/share/ShareePermissions.js @@ -51,7 +51,7 @@ qx.Class.define("osparc.share.ShareePermissions", { const label = new qx.ui.basic.Label(); hBox.add(infoButton); hBox.add(label); - osparc.service.Store.getService(inaccessibleService.key, inaccessibleService.version) + osparc.store.Services.getService(inaccessibleService.key, inaccessibleService.version) .then(metadata => { label.setValue(metadata["name"] + " : " + metadata["version"]) infoButton.addListener("execute", () => { diff --git a/services/static-webserver/client/source/class/osparc/service/Store.js b/services/static-webserver/client/source/class/osparc/store/Services.js similarity index 99% rename from services/static-webserver/client/source/class/osparc/service/Store.js rename to services/static-webserver/client/source/class/osparc/store/Services.js index 8158eb836bc..1fb2c1b0959 100644 --- a/services/static-webserver/client/source/class/osparc/service/Store.js +++ b/services/static-webserver/client/source/class/osparc/store/Services.js @@ -15,7 +15,7 @@ ************************************************************************ */ -qx.Class.define("osparc.service.Store", { +qx.Class.define("osparc.store.Services", { type: "static", statics: { diff --git a/services/static-webserver/client/source/class/osparc/store/Store.js b/services/static-webserver/client/source/class/osparc/store/Store.js index c6c25606acc..1c03b85ea44 100644 --- a/services/static-webserver/client/source/class/osparc/store/Store.js +++ b/services/static-webserver/client/source/class/osparc/store/Store.js @@ -74,6 +74,10 @@ qx.Class.define("osparc.store.Store", { check: "Array", init: [] }, + workspaces: { + check: "Array", + init: [] + }, studyComments: { check: "Array", init: [] diff --git a/services/static-webserver/client/source/class/osparc/store/Workspaces.js b/services/static-webserver/client/source/class/osparc/store/Workspaces.js new file mode 100644 index 00000000000..6d0664abb0a --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/store/Workspaces.js @@ -0,0 +1,317 @@ +/* ************************************************************************ + + 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.store.Workspaces", { + type: "static", + + statics: { + workspacesCached: [], + + iconPath: function(iconsSize = 18) { + const source = "@MaterialIcons/folder_shared/"; + if (iconsSize === -1) { + return source; + } + 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", + }], + + fetchWorkspaces: function() { + if (osparc.auth.Data.getInstance().isGuest()) { + return new Promise(resolve => { + resolve([]); + }); + } + + /* + return osparc.data.Resources.getInstance().getAllPages("workspaces", params) + .then(workspacesData => { + const workspaces = []; + workspacesData.forEach(workspaceData => { + const workspace = new osparc.data.model.Workspace(workspaceData); + this.__addToCache(workspace); + workspaces.push(workspace); + }); + return workspaces; + }); + */ + + 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 + }; + return osparc.data.Resources.getInstance().fetch("workspaces", "post", params) + .then(workspaceData => { + const newWorkspace = new osparc.data.model.Workspace(workspaceData); + 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 + } + }; + osparc.data.Resources.getInstance().fetch("workspaces", "delete", params) + .then(() => { + if (this.__deleteFromCache(workspaceId)) { + resolve(); + } else { + reject(); + } + }) + .catch(err => reject(err)); + */ + }); + }, + + putWorkspace: function(workspaceId, updateData) { + return new Promise((resolve, reject) => { + const params = { + "url": { + workspaceId + }, + data: updateData + }; + osparc.data.Resources.getInstance().fetch("workspaces", "update", params) + .then(() => { + const workspace = this.getWorkspace(workspaceId); + Object.keys(updateData).forEach(propKey => { + const upKey = qx.lang.String.firstUp(propKey); + const setter = "set" + upKey; + if (workspace && setter in workspace) { + workspace[setter](updateData[propKey]); + } + }); + workspace.setLastModified(new Date()); + this.__deleteFromCache(workspaceId); + this.__addToCache(workspace); + resolve(); + }) + .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); + workspace.set({ + accessRights: newAccessRights, + lastModified: new Date() + }) + resolve(); + } else { + reject(); + } + }); + }, + + 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(); + } + }); + }, + + 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(); + }); + }, + + getWorkspaces: function(parentId = null) { + return this.workspacesCached.filter(f => f.getParentId() === parentId); + }, + + getWorkspace: function(workspaceId = null) { + return this.workspacesCached.find(f => f.getWorkspaceId() === workspaceId); + }, + + __addToCache: function(workspace) { + const found = this.workspacesCached.find(f => f.getWorkspaceId() === workspace.getWorkspaceId()); + if (!found) { + this.workspacesCached.unshift(workspace); + } + }, + + __deleteFromCache: function(workspaceId) { + const idx = this.workspacesCached.findIndex(f => f.getWorkspaceId() === workspaceId); + if (idx > -1) { + this.workspacesCached.splice(idx, 1); + return true; + } + return false; + } + } +}); 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 a1f4fc76f31..3855cf01f96 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -35,7 +35,7 @@ qx.Class.define("osparc.study.Utils", { }, getInaccessibleServices: function(workbench) { - const allServices = osparc.service.Store.servicesCached; + const allServices = osparc.store.Services.servicesCached; const unaccessibleServices = []; const wbServices = new Set(this.extractServices(workbench)); wbServices.forEach(srv => { @@ -70,7 +70,7 @@ qx.Class.define("osparc.study.Utils", { }, isWorkbenchRetired: function(workbench) { - const allServices = osparc.service.Store.servicesCached; + const allServices = osparc.store.Services.servicesCached; const services = new Set(this.extractServices(workbench)); const isRetired = Array.from(services).some(srv => { if (srv.key in allServices && srv.version in allServices[srv.key]) { @@ -88,7 +88,7 @@ qx.Class.define("osparc.study.Utils", { }, isWorkbenchDeprecated: function(workbench) { - const allServices = osparc.service.Store.servicesCached; + const allServices = osparc.store.Services.servicesCached; const services = new Set(this.extractServices(workbench)); const isRetired = Array.from(services).some(srv => { if (srv.key in allServices && srv.version in allServices[srv.key]) { @@ -107,7 +107,7 @@ qx.Class.define("osparc.study.Utils", { createStudyFromService: function(key, version, existingStudies, newStudyLabel) { return new Promise((resolve, reject) => { - osparc.service.Store.getService(key, version) + osparc.store.Services.getService(key, version) .then(metadata => { const newUuid = osparc.utils.Utils.uuidV4(); const minStudyData = osparc.data.model.Study.createMyNewStudyObject(); diff --git a/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js b/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js index dfba86b6b8c..6dd098e5bb3 100644 --- a/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js +++ b/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js @@ -83,8 +83,8 @@ qx.Class.define("osparc.ui.list.CollaboratorListItem", { return osparc.data.Roles.STUDY[i]; } else if (resource === "service") { return osparc.data.Roles.SERVICES[i]; - } else if (resource === "folder") { - return osparc.data.Roles.FOLDERS[i]; + } else if (resource === "workspace") { + return osparc.data.Roles.WORKSPACE[i]; } return undefined; }, diff --git a/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js b/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js index 1684b780fc6..a1b4db10cb1 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js +++ b/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js @@ -197,7 +197,7 @@ qx.Class.define("osparc.workbench.ServiceCatalog", { __populateList: function() { this.__servicesLatest = []; - osparc.service.Store.getServicesLatest() + osparc.store.Services.getServicesLatest() .then(servicesLatest => { Object.keys(servicesLatest).forEach(key => { this.__servicesLatest.push(servicesLatest[key]); @@ -293,7 +293,7 @@ qx.Class.define("osparc.workbench.ServiceCatalog", { if (version == this.self(arguments).LATEST.toString()) { version = this.__versionsBox.getChildrenContainer().getSelectables()[1].version; } - const serviceMetadata = await osparc.service.Store.getService(key, version); + const serviceMetadata = await osparc.store.Services.getService(key, version); return serviceMetadata; },