diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js
index acb3fe35386..3bcd200c2ee 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js
@@ -395,6 +395,14 @@ qx.Class.define("osparc.dashboard.CardBase", {
return this.getResourceType() === resourceType;
},
+ isItemNotClickable: function() {
+ const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ return (
+ this.getBlocked() === true || // It could be blocked by IN_USE or UNKNOWN_SERVICE
+ (this.isResourceType("study") && (studyBrowserContext === "trash")) // It could a trashed study
+ );
+ },
+
__applyResourceData: function(resourceData) {
let uuid = null;
let owner = null;
@@ -776,9 +784,9 @@ qx.Class.define("osparc.dashboard.CardBase", {
if (moveToButton) {
moveToButton.setEnabled(osparc.study.Utils.canMoveTo(resourceData));
}
- const deleteButton = menuButtons.find(menuBtn => "deleteButton" in menuBtn);
- if (deleteButton) {
- deleteButton.setEnabled(osparc.study.Utils.canBeDeleted(resourceData));
+ const trashButton = menuButtons.find(menuBtn => "trashButton" in menuBtn);
+ if (trashButton) {
+ trashButton.setEnabled(osparc.study.Utils.canBeDeleted(resourceData));
}
}
},
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ContextBreadcrumbs.js b/services/static-webserver/client/source/class/osparc/dashboard/ContextBreadcrumbs.js
index dba858ffad8..4358bc68848 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ContextBreadcrumbs.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ContextBreadcrumbs.js
@@ -24,6 +24,8 @@ qx.Class.define("osparc.dashboard.ContextBreadcrumbs", {
this._setLayout(new qx.ui.layout.HBox(5).set({
alignY: "middle"
}));
+
+ osparc.store.Store.getInstance().addListener("changeStudyBrowserContext", () => this.__rebuild(), this);
},
events: {
@@ -31,14 +33,6 @@ qx.Class.define("osparc.dashboard.ContextBreadcrumbs", {
},
properties: {
- currentContext: {
- check: ["studiesAndFolders", "workspaces", "search"],
- nullable: false,
- init: "studiesAndFolders",
- event: "changeCurrentContext",
- apply: "__rebuild"
- },
-
currentWorkspaceId: {
check: "Number",
nullable: true,
@@ -60,7 +54,8 @@ qx.Class.define("osparc.dashboard.ContextBreadcrumbs", {
__rebuild: function() {
this._removeAll();
- if (this.getCurrentContext() !== "studiesAndFolders") {
+ const currentContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ if (currentContext !== "studiesAndFolders") {
return;
}
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 2932a774c92..ac919b73579 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js
@@ -46,6 +46,8 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", {
"folderSelected": "qx.event.type.Data",
"folderUpdated": "qx.event.type.Data",
"moveFolderToRequested": "qx.event.type.Data",
+ "trashFolderRequested": "qx.event.type.Data",
+ "untrashFolderRequested": "qx.event.type.Data",
"deleteFolderRequested": "qx.event.type.Data",
"changeContext": "qx.event.type.Data",
},
@@ -85,7 +87,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", {
check: "Date",
nullable: true,
apply: "__applyLastModified"
- }
+ },
},
members: {
@@ -219,6 +221,16 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", {
menu.addSeparator();
+ const trashButton = new qx.ui.menu.Button(this.tr("Trash"), "@FontAwesome5Solid/trash/12");
+ trashButton.addListener("execute", () => this.__trashFolderRequested(), this);
+ menu.add(trashButton);
+ } else if (studyBrowserContext === "trash") {
+ const restoreButton = new qx.ui.menu.Button(this.tr("Restore"), "@MaterialIcons/restore_from_trash/16");
+ restoreButton.addListener("execute", () => this.fireDataEvent("untrashFolderRequested", this.getFolder()), this);
+ menu.add(restoreButton);
+
+ menu.addSeparator();
+
const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12");
osparc.utils.Utils.setIdToWidget(deleteButton, "deleteFolderMenuItem");
deleteButton.addListener("execute", () => this.__deleteFolderRequested(), this);
@@ -229,7 +241,9 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", {
},
__itemSelected: function(newVal) {
- if (newVal) {
+ const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ // do not allow selecting workspace
+ if (studyBrowserContext !== "trash" && newVal) {
this.fireDataEvent("folderSelected", this.getFolderId());
}
this.setValue(false);
@@ -262,6 +276,24 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", {
folderEditor.addListener("cancel", () => win.close());
},
+ __trashFolderRequested: function() {
+ const trashDays = osparc.store.StaticInfo.getInstance().getTrashRetentionDays();
+ let msg = this.tr("Are you sure you want to move the Folder and all its content to the trash?");
+ msg += "
" + this.tr("It will be permanently deleted after ") + trashDays + " days.";
+ const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
+ caption: this.tr("Move to Trash"),
+ confirmText: this.tr("Move to Trash"),
+ confirmAction: "delete"
+ });
+ confirmationWin.center();
+ confirmationWin.open();
+ confirmationWin.addListener("close", () => {
+ if (confirmationWin.getConfirmed()) {
+ this.fireDataEvent("trashFolderRequested", this.getFolderId());
+ }
+ }, this);
+ },
+
__deleteFolderRequested: 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/GridButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js
index 828a0c74ba7..e9019262342 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js
@@ -188,8 +188,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", {
},
__itemSelected: function() {
- // It could be blocked by IN_USE or UNKNOWN_SERVICE
- if (this.getBlocked() === true) {
+ if (this.isItemNotClickable()) {
this.setValue(false);
return;
}
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js
index 71f59b970df..9c433550185 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js
@@ -274,8 +274,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", {
},
__itemSelected: function() {
- // It could be blocked by IN_USE or UNKNOWN_SERVICE
- if (this.getBlocked() === true) {
+ if (this.isItemNotClickable()) {
this.setValue(false);
return;
}
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 ca565c756f3..c007ca05f7e 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js
@@ -244,7 +244,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
this._addToLayout(searchBarFilter);
},
- _createResourcesLayout: function() {
+ _createResourcesLayout: function(flatListId) {
const toolbar = this._toolbar = new qx.ui.toolbar.ToolBar().set({
backgroundColor: "transparent",
spacing: 10,
@@ -255,7 +255,13 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
this.__viewModeLayout = new qx.ui.toolbar.Part();
- const resourcesContainer = this._resourcesContainer = new osparc.dashboard.ResourceContainerManager();
+ const resourcesContainer = this._resourcesContainer = new osparc.dashboard.ResourceContainerManager(this._resourceType);
+ if (flatListId) {
+ const list = this._resourcesContainer.getFlatList();
+ if (list) {
+ osparc.utils.Utils.setIdToWidget(list, flatListId);
+ }
+ }
if (this._resourceType === "study") {
const viewMode = osparc.utils.Utils.localCache.getLocalStorageItem("studiesViewMode");
if (viewMode) {
@@ -270,6 +276,8 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
resourcesContainer.addListener("emptyStudyClicked", e => this._deleteResourceRequested(e.getData()));
resourcesContainer.addListener("folderUpdated", e => this._folderUpdated(e.getData()));
resourcesContainer.addListener("moveFolderToRequested", e => this._moveFolderToRequested(e.getData()));
+ resourcesContainer.addListener("trashFolderRequested", e => this._trashFolderRequested(e.getData()));
+ resourcesContainer.addListener("untrashFolderRequested", e => this._untrashFolderRequested(e.getData()));
resourcesContainer.addListener("deleteFolderRequested", e => this._deleteFolderRequested(e.getData()));
resourcesContainer.addListener("folderSelected", e => {
const folderId = e.getData();
@@ -288,6 +296,8 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
this._changeContext(context, workspaceId, folderId);
}, this);
resourcesContainer.addListener("workspaceUpdated", e => this._workspaceUpdated(e.getData()));
+ resourcesContainer.addListener("trashWorkspaceRequested", e => this._trashWorkspaceRequested(e.getData()));
+ resourcesContainer.addListener("untrashWorkspaceRequested", e => this._untrashWorkspaceRequested(e.getData()));
resourcesContainer.addListener("deleteWorkspaceRequested", e => this._deleteWorkspaceRequested(e.getData()));
this._addToLayout(resourcesContainer);
@@ -502,6 +512,14 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
throw new Error("Abstract method called!");
},
+ _trashFolderRequested: function(folderId) {
+ throw new Error("Abstract method called!");
+ },
+
+ _untrashFolderRequested: function(folder) {
+ throw new Error("Abstract method called!");
+ },
+
_deleteFolderRequested: function(folderId) {
throw new Error("Abstract method called!");
},
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js
index ba1485f024f..b33d5015231 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js
@@ -18,7 +18,7 @@
qx.Class.define("osparc.dashboard.ResourceContainerManager", {
extend: qx.ui.core.Widget,
- construct: function() {
+ construct: function(resourceType) {
this.base(arguments);
this._setLayout(new qx.ui.layout.VBox(15));
@@ -27,19 +27,20 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
paddingBottom: 60
});
- this.__foldersList = [];
this.__workspacesList = [];
+ this.__foldersList = [];
this.__resourcesList = [];
this.__groupedContainersList = [];
+ if (resourceType === "study") {
+ const workspacesContainer = this.__workspacesContainer = new osparc.dashboard.ToggleButtonContainer();
+ this._add(workspacesContainer);
+ workspacesContainer.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded");
- const workspacesContainer = this.__workspacesContainer = new osparc.dashboard.ToggleButtonContainer();
- workspacesContainer.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded");
-
-
- const foldersContainer = this.__foldersContainer = new osparc.dashboard.ToggleButtonContainer();
- this._add(foldersContainer);
- foldersContainer.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded");
+ const foldersContainer = this.__foldersContainer = new osparc.dashboard.ToggleButtonContainer();
+ this._add(foldersContainer);
+ foldersContainer.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded");
+ }
const nonGroupedContainer = this.__nonGroupedContainer = this.__createFlatList();
this._add(nonGroupedContainer);
@@ -75,9 +76,13 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
"folderSelected": "qx.event.type.Data",
"folderUpdated": "qx.event.type.Data",
"moveFolderToRequested": "qx.event.type.Data",
+ "trashFolderRequested": "qx.event.type.Data",
+ "untrashFolderRequested": "qx.event.type.Data",
"deleteFolderRequested": "qx.event.type.Data",
"workspaceSelected": "qx.event.type.Data",
"workspaceUpdated": "qx.event.type.Data",
+ "trashWorkspaceRequested": "qx.event.type.Data",
+ "untrashWorkspaceRequested": "qx.event.type.Data",
"deleteWorkspaceRequested": "qx.event.type.Data",
"changeContext": "qx.event.type.Data",
},
@@ -260,9 +265,13 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
},
__cleanAll: function() {
- if (this.__workspacesContainer) {
- this.__workspacesContainer.removeAll();
+ if (this._getChildren().includes(this.__nonGroupedContainer)) {
+ this._remove(this.__nonGroupedContainer);
+ }
+ if (this._getChildren().includes(this.__groupedContainers)) {
+ this._remove(this.__groupedContainers);
}
+
if (this.__nonGroupedContainer) {
this.__nonGroupedContainer.removeAll();
this.__nonGroupedContainer = null;
@@ -274,7 +283,6 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
groupedContainer.getContentContainer().removeAll();
});
this.__groupedContainersList = [];
- this._removeAll();
},
__addFoldersContainer: function() {
@@ -299,9 +307,6 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
__rebuildLayout: function(resourceType) {
this.__cleanAll();
- if (osparc.utils.DisabledPlugins.isFoldersEnabled()) {
- this.__addFoldersContainer();
- }
if (this.getGroupBy()) {
const noGroupContainer = this.__createGroupContainer("no-group", "No Group", "transparent");
this.__groupedContainers.add(noGroupContainer);
@@ -361,8 +366,9 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
},
reloadWorkspaces: function() {
- this.__cleanAll();
- this._add(this.__workspacesContainer);
+ if (this.__workspacesContainer) {
+ this.__workspacesContainer.removeAll();
+ }
let workspacesCards = [];
this.__workspacesList.forEach(workspaceData => workspacesCards.push(this.__workspaceToCard(workspaceData)));
return workspacesCards;
@@ -383,6 +389,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
[
"workspaceSelected",
"workspaceUpdated",
+ "trashWorkspaceRequested",
+ "untrashWorkspaceRequested",
"deleteWorkspaceRequested",
].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData())));
return card;
@@ -419,6 +427,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
"folderSelected",
"folderUpdated",
"moveFolderToRequested",
+ "trashFolderRequested",
+ "untrashFolderRequested",
"deleteFolderRequested",
"changeContext",
].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData())));
@@ -494,7 +504,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
} else {
groupContainer.exclude();
}
- this._add(groupContainer);
+ this.__groupedContainers.add(groupContainer);
this.__moveNoGroupToLast();
}
const card = this.__createCard(resourceData);
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 0c452e3e33a..dcda538841c 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js
@@ -34,6 +34,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", {
},
events: {
+ "trashContext": "qx.event.type.Event",
"changeSharedWith": "qx.event.type.Data",
"changeSelectedTags": "qx.event.type.Data",
"changeServiceType": "qx.event.type.Data"
@@ -42,6 +43,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", {
members: {
__resourceType: null,
__workspacesAndFoldersTree: null,
+ __trashButton: null,
__sharedWithButtons: null,
__tagButtons: null,
__serviceTypeButtons: null,
@@ -49,6 +51,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", {
__buildLayout: function() {
if (this.__resourceType === "study" && osparc.utils.DisabledPlugins.isFoldersEnabled()) {
this._add(this.__createWorkspacesAndFoldersTree());
+ this._add(this.__createTrashBin());
} else {
this._add(this.__createSharedWithFilterLayout());
}
@@ -62,6 +65,16 @@ qx.Class.define("osparc.dashboard.ResourceFilter", {
}
},
+ contextChanged: function(context, workspaceId, folderId) {
+ this.__workspacesAndFoldersTree.set({
+ currentWorkspaceId: workspaceId,
+ currentFolderId: folderId,
+ });
+ this.__workspacesAndFoldersTree.contextChanged(context);
+
+ this.__trashButton.setValue(context === "trash");
+ },
+
/* WORKSPACES AND FOLDERS */
__createWorkspacesAndFoldersTree: function() {
const workspacesAndFoldersTree = this.__workspacesAndFoldersTree = new osparc.dashboard.WorkspacesAndFoldersTree();
@@ -85,6 +98,76 @@ qx.Class.define("osparc.dashboard.ResourceFilter", {
},
/* /WORKSPACES AND FOLDERS */
+ /* TRASH BIN */
+ __createTrashBin: function() {
+ const trashButton = this.__trashButton = new qx.ui.toolbar.RadioButton().set({
+ value: false,
+ appearance: "filter-toggle-button",
+ label: this.tr("Trash"),
+ icon: "@FontAwesome5Solid/trash/18",
+ });
+ trashButton.addListener("changeValue", e => {
+ const trashEnabled = e.getData();
+ if (trashEnabled) {
+ this.fireEvent("trashContext");
+ }
+ });
+ this.evaluateTrashEmpty();
+ return trashButton;
+ },
+
+ evaluateTrashEmpty: function() {
+ const studiesParams = {
+ url: {
+ offset: 0,
+ limit: 1, // just one
+ orderBy: JSON.stringify({
+ field: "last_change_date",
+ direction: "desc"
+ }),
+ }
+ };
+ const foldersParams = {
+ url: {
+ offset: 0,
+ limit: 1, // just one
+ orderBy: JSON.stringify({
+ field: "modified_at",
+ direction: "desc"
+ }),
+ }
+ };
+ const workspacesParams = {
+ url: {
+ offset: 0,
+ limit: 1, // just one
+ orderBy: JSON.stringify({
+ field: "modified_at",
+ direction: "desc"
+ }),
+ }
+ };
+ Promise.all([
+ osparc.data.Resources.fetch("studies", "getPageTrashed", studiesParams),
+ osparc.data.Resources.fetch("folders", "getPageTrashed", foldersParams),
+ osparc.data.Resources.fetch("workspaces", "getPageTrashed", workspacesParams),
+ ])
+ .then(values => {
+ const nTrashedStudies = values[0].length;
+ const nTrashedFolders = values[1].length;
+ const nTrashedWorkspaces = values[2].length;
+ this.setTrashEmpty((nTrashedStudies+nTrashedFolders+nTrashedWorkspaces) === 0);
+ })
+ .catch(err => console.error(err));
+ },
+
+ setTrashEmpty: function(isEmpty) {
+ this.__trashButton.set({
+ textColor: isEmpty ? "text" : "danger-red"
+ });
+ },
+ /* /TRASH BIN */
+
/* SHARED WITH */
__createSharedWithFilterLayout: function() {
const sharedWithLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
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 15278743e54..5fbaa4ebaf7 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js
@@ -129,11 +129,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", {
// LAYOUT //
_createLayout: function() {
this._createSearchBar();
- this._createResourcesLayout();
- const list = this._resourcesContainer.getFlatList();
- if (list) {
- osparc.utils.Utils.setIdToWidget(list, "servicesList");
- }
+ this._createResourcesLayout("servicesList");
this.__addNewServiceButtons();
this._toolbar.add(new qx.ui.core.Spacer(), {
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 882848dd295..c7ef8f916f2 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js
@@ -42,6 +42,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
construct: function() {
this._resourceType = "study";
this.base(arguments);
+
+ const store = osparc.store.Store.getInstance();
+ this.bind("currentContext", store, "studyBrowserContext");
},
events: {
@@ -50,7 +53,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
properties: {
currentContext: {
- check: ["studiesAndFolders", "workspaces", "search"],
+ check: ["studiesAndFolders", "workspaces", "search", "trash"],
nullable: false,
init: "studiesAndFolders",
event: "changeCurrentContext"
@@ -96,6 +99,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
__workspacesList: null,
__foldersList: null,
__loadingFolders: null,
+ __loadingWorkspaces: null,
// overridden
initResources: function() {
@@ -160,10 +164,48 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
},
__reloadWorkspaces: function() {
+ if (
+ !osparc.auth.Manager.getInstance().isLoggedIn() ||
+ !osparc.utils.DisabledPlugins.isFoldersEnabled() ||
+ this.getCurrentContext() === "studiesAndFolders" ||
+ this.getCurrentContext() === "search" || // not yet implemented for workspaces
+ this.__loadingWorkspaces
+ ) {
+ return;
+ }
+
+ let request = null;
+ switch (this.getCurrentContext()) {
+ case "search": {
+ const filterData = this._searchBarFilter.getFilterData();
+ const text = filterData.text ? encodeURIComponent(filterData.text) : "";
+ request = osparc.store.Workspaces.getInstance().searchWorkspaces(text);
+ break;
+ }
+ case "workspaces": {
+ request = osparc.store.Workspaces.getInstance().fetchWorkspaces();
+ break;
+ }
+ case "trash":
+ request = osparc.store.Workspaces.getInstance().fetchAllTrashedWorkspaces();
+ break;
+ }
+
+ this.__loadingWorkspaces = true;
this.__setWorkspacesToList([]);
- osparc.store.Workspaces.getInstance().fetchWorkspaces()
+ request
.then(workspaces => {
this.__setWorkspacesToList(workspaces);
+ if (this.getCurrentContext() === "trash") {
+ if (workspaces.length) {
+ this.__header.getChildControl("empty-trash-button").show();
+ }
+ }
+ })
+ .catch(console.error)
+ .finally(() => {
+ this.__addNewWorkspaceButton();
+ this.__loadingWorkspaces = null;
});
},
@@ -177,7 +219,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
return;
}
- this.__loadingFolders = true;
let request = null;
switch (this.getCurrentContext()) {
case "search": {
@@ -192,14 +233,27 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
request = osparc.store.Folders.getInstance().fetchFolders(folderId, workspaceId, this.getOrderBy());
break;
}
+ case "trash":
+ request = osparc.store.Folders.getInstance().fetchAllTrashedFolders(this.getOrderBy());
+ break;
}
+
+ this.__loadingFolders = true;
this.__setFoldersToList([]);
request
.then(folders => {
this.__setFoldersToList(folders);
+ if (this.getCurrentContext() === "trash") {
+ if (folders.length) {
+ this.__header.getChildControl("empty-trash-button").show();
+ }
+ }
})
.catch(console.error)
- .finally(() => this.__loadingFolders = null);
+ .finally(() => {
+ this.__addNewFolderButton();
+ this.__loadingFolders = null;
+ });
},
__reloadStudies: function() {
@@ -257,6 +311,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
this._resourcesContainer.getFlatList().nextRequest = resp["_links"]["next"];
}
+ if (this.getCurrentContext() === "trash") {
+ if (this._resourcesList.length) {
+ this.__header.getChildControl("empty-trash-button").show();
+ }
+ }
+
// Show Quick Start if there are no studies in the root folder of the personal workspace
const quickStartInfo = osparc.product.quickStart.Utils.getQuickStart();
if (quickStartInfo) {
@@ -267,6 +327,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
const nStudies = "_meta" in resp ? resp["_meta"]["total"] : 0;
if (
nStudies === 0 &&
+ this.getCurrentContext() === "studiesAndFolders" &&
this.getCurrentWorkspaceId() === null &&
this.getCurrentFolderId() === null
) {
@@ -354,9 +415,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
const cards = this._resourcesContainer.reloadCards("studies");
this.__configureStudyCards(cards);
- // they were removed in the above reloadCards
- this.__reloadFolders();
-
this.__addNewStudyButtons();
const loadMoreBtn = this.__createLoadMoreButton();
@@ -382,6 +440,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
__reloadWorkspaceCards: function() {
this._resourcesContainer.setWorkspacesToList(this.__workspacesList);
this._resourcesContainer.reloadWorkspaces();
+ },
+
+ __addNewWorkspaceButton: function() {
+ if (this.getCurrentContext() !== "workspaces") {
+ return;
+ }
const newWorkspaceCard = new osparc.dashboard.WorkspaceButtonNew();
newWorkspaceCard.setCardKey("new-workspace");
@@ -401,13 +465,44 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
},
_workspaceUpdated: function() {
- this.__reloadWorkspaceCards();
+ this.__reloadWorkspaces();
+ },
+
+ _trashWorkspaceRequested: function(workspaceId) {
+ osparc.store.Workspaces.getInstance().trashWorkspace(workspaceId)
+ .then(() => {
+ this.__reloadWorkspaces();
+ const msg = this.tr("Successfully moved to Trash");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.setTrashEmpty(false);
+ })
+ .catch(err => {
+ console.error(err);
+ osparc.FlashMessenger.getInstance().logAs(err, "ERROR");
+ });
+ },
+
+ _untrashWorkspaceRequested: function(workspace) {
+ osparc.store.Workspaces.getInstance().untrashWorkspace(workspace)
+ .then(() => {
+ this.__reloadWorkspaces();
+ const msg = this.tr("Successfully restored");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.evaluateTrashEmpty();
+ })
+ .catch(err => {
+ console.error(err);
+ osparc.FlashMessenger.getInstance().logAs(err, "ERROR");
+ });
},
_deleteWorkspaceRequested: function(workspaceId) {
osparc.store.Workspaces.getInstance().deleteWorkspace(workspaceId)
.then(() => {
this.__reloadWorkspaces();
+ const msg = this.tr("Successfully deleted");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.evaluateTrashEmpty();
})
.catch(err => {
console.error(err);
@@ -420,8 +515,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
__reloadFolderCards: function() {
this._resourcesContainer.setFoldersToList(this.__foldersList);
this._resourcesContainer.reloadFolders();
-
- this.__addNewFolderButton();
},
__addNewFolderButton: function() {
@@ -544,9 +637,42 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
.catch(err => console.error(err));
},
+ _trashFolderRequested: function(folderId) {
+ osparc.store.Folders.getInstance().trashFolder(folderId, this.getCurrentWorkspaceId())
+ .then(() => {
+ this.__reloadFolders();
+ const msg = this.tr("Successfully moved to Trash");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.setTrashEmpty(false);
+ })
+ .catch(err => {
+ console.error(err);
+ osparc.FlashMessenger.getInstance().logAs(err, "ERROR");
+ })
+ },
+
+ _untrashFolderRequested: function(folder) {
+ osparc.store.Folders.getInstance().untrashFolder(folder)
+ .then(() => {
+ this.__reloadFolders();
+ const msg = this.tr("Successfully restored");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.evaluateTrashEmpty();
+ })
+ .catch(err => {
+ console.error(err);
+ osparc.FlashMessenger.getInstance().logAs(err, "ERROR");
+ })
+ },
+
_deleteFolderRequested: function(folderId) {
osparc.store.Folders.getInstance().deleteFolder(folderId, this.getCurrentWorkspaceId())
- .then(() => this.__reloadFolders())
+ .then(() => {
+ this.__reloadFolders();
+ const msg = this.tr("Successfully deleted");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.evaluateTrashEmpty();
+ })
.catch(err => console.error(err));
},
// /FOLDERS
@@ -555,7 +681,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
cards.forEach(card => {
card.setMultiSelectionMode(this.getMultiSelection());
card.addListener("tap", e => {
- if (card.getBlocked() === true) {
+ if (card.isItemNotClickable()) {
card.setValue(false);
} else {
this.__itemClicked(card, e.getNativeEvent().shiftKey);
@@ -634,6 +760,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
// loose equality: will do a Number to String conversion if necessary
sameContext &= key in reqParams && reqParams[key] == value;
});
+ // both ways
+ Object.entries(reqParams).forEach(([key, value]) => {
+ // loose equality: will do a Number to String conversion if necessary
+ sameContext &= key in currentParams && currentParams[key] == value;
+ });
return !sameContext;
},
@@ -711,6 +842,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
let request = null;
switch (this.getCurrentContext()) {
+ case "trash":
+ request = osparc.data.Resources.fetch("studies", "getPageTrashed", params, options);
+ break;
case "search":
request = osparc.data.Resources.fetch("studies", "getPageSearch", params, options);
break;
@@ -864,15 +998,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
if (osparc.utils.DisabledPlugins.isFoldersEnabled()) {
const header = this.__header = new osparc.dashboard.StudyBrowserHeader();
+ this.__header.addListener("emptyTrashRequested", () => this.__emptyTrash(), this);
this._addToLayout(header);
}
- this._createResourcesLayout();
-
- const list = this._resourcesContainer.getFlatList();
- if (list) {
- osparc.utils.Utils.setIdToWidget(list, "studiesList");
- }
+ this._createResourcesLayout("studiesList");
const importStudyButton = this.__createImportButton();
const isDisabled = osparc.utils.DisabledPlugins.isImportDisabled();
@@ -882,10 +1012,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
const selectStudiesButton = this.__createSelectButton();
this._toolbar.add(selectStudiesButton);
- const studiesMoveButton = this.__createMoveStudiesButton(false);
+ const studiesMoveButton = this.__createMoveStudiesButton();
this._toolbar.add(studiesMoveButton);
- const studiesDeleteButton = this.__createDeleteButton(false);
+ const studiesTrashButton = this.__createTrashStudiesButton();
+ this._toolbar.add(studiesTrashButton);
+
+ const studiesDeleteButton = this.__createDeleteStudiesButton();
this._toolbar.add(studiesDeleteButton);
this._toolbar.add(new qx.ui.core.Spacer(), {
@@ -924,8 +1057,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
label: selection.length > 1 ? this.tr("Move selected")+" ("+selection.length+")" : this.tr("Move")
});
+ studiesTrashButton.set({
+ visibility: selection.length && currentContext === "studiesAndFolders" ? "visible" : "excluded",
+ label: selection.length > 1 ? this.tr("Trash selected")+" ("+selection.length+")" : this.tr("Trash")
+ });
+
studiesDeleteButton.set({
- visibility: selection.length ? "visible" : "excluded",
+ visibility: selection.length && currentContext === "trash" ? "visible" : "excluded",
label: selection.length > 1 ? this.tr("Delete selected")+" ("+selection.length+")" : this.tr("Delete")
});
});
@@ -956,6 +1094,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
}
}, this);
+ this._resourceFilter.addListener("trashContext", () => {
+ this._changeContext("trash");
+ });
+
this._searchBarFilter.addListener("filterChanged", e => {
const filterData = e.getData();
if (filterData.text) {
@@ -987,41 +1129,56 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
currentWorkspaceId: workspaceId,
currentFolderId: folderId,
});
- this._loadingResourcesBtn.setFetching(false);
this.resetSelection();
this.setMultiSelection(false);
- this.invalidateStudies();
- this._resourcesContainer.setResourcesToList([]);
+
+ // reset lists
+ this.__setWorkspacesToList([]);
+ this.__setFoldersToList([]);
+ this._resourcesList = [];
+ this._resourcesContainer.setResourcesToList(this._resourcesList);
+ this._resourcesContainer.reloadCards("studies");
this._toolbar.show();
- if (context === "search") {
- this.__reloadFolders();
- this.__reloadStudies();
- } else if (context === "workspaces") {
- this._toolbar.hide();
- this._searchBarFilter.resetFilters();
- this.__reloadWorkspaces();
- } else if (context === "studiesAndFolders") {
- this._searchBarFilter.resetFilters();
- this.__reloadFolders();
- this.__reloadStudies();
+ switch (this.getCurrentContext()) {
+ case "studiesAndFolders":
+ this._searchBarFilter.resetFilters();
+ this.__reloadFolders();
+ this._loadingResourcesBtn.setFetching(false);
+ this.invalidateStudies();
+ this.__reloadStudies();
+ break;
+ case "workspaces":
+ this._toolbar.exclude();
+ this._searchBarFilter.resetFilters();
+ this.__reloadWorkspaces();
+ break;
+ case "search":
+ this.__reloadWorkspaces();
+ this.__reloadFolders();
+ this._loadingResourcesBtn.setFetching(false);
+ this.invalidateStudies();
+ this.__reloadStudies();
+ break;
+ case "trash":
+ this._searchBarFilter.resetFilters();
+ this.__reloadWorkspaces();
+ this.__reloadFolders();
+ this._loadingResourcesBtn.setFetching(false);
+ this.invalidateStudies();
+ this.__reloadStudies();
+ break;
}
// notify header
const header = this.__header;
header.set({
- currentContext: context,
currentWorkspaceId: workspaceId,
currentFolderId: folderId,
});
- // notify workspacesAndFoldersTree
- const workspacesAndFoldersTree = this._resourceFilter.getWorkspacesAndFoldersTree();
- workspacesAndFoldersTree.set({
- currentWorkspaceId: workspaceId,
- currentFolderId: folderId,
- });
- workspacesAndFoldersTree.contextChanged(context);
+ // notify Filters on the left
+ this._resourceFilter.contextChanged(context, workspaceId, folderId);
}
},
@@ -1129,7 +1286,32 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
return moveStudiesButton;
},
- __createDeleteButton: function() {
+ __createTrashStudiesButton: function() {
+ const trashButton = new qx.ui.form.Button(this.tr("Trash"), "@FontAwesome5Solid/trash/14").set({
+ appearance: "danger-button",
+ visibility: "excluded"
+ });
+ osparc.utils.Utils.setIdToWidget(trashButton, "deleteStudiesBtn");
+ trashButton.addListener("execute", () => {
+ const selection = this._resourcesContainer.getSelection();
+ const preferencesSettings = osparc.Preferences.getInstance();
+ if (preferencesSettings.getConfirmDeleteStudy()) {
+ const win = this.__createConfirmTrashWindow(selection.map(button => button.getTitle()));
+ win.center();
+ win.open();
+ win.addListener("close", () => {
+ if (win.getConfirmed()) {
+ this.__trashStudies(selection.map(button => this.__getStudyData(button.getUuid(), false)), false);
+ }
+ }, this);
+ } else {
+ this.__trashStudies(selection.map(button => this.__getStudyData(button.getUuid(), false)), false);
+ }
+ }, this);
+ return trashButton;
+ },
+
+ __createDeleteStudiesButton: function() {
const deleteButton = new qx.ui.form.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/14").set({
appearance: "danger-button",
visibility: "excluded"
@@ -1139,7 +1321,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
const selection = this._resourcesContainer.getSelection();
const preferencesSettings = osparc.Preferences.getInstance();
if (preferencesSettings.getConfirmDeleteStudy()) {
- const win = this.__createConfirmWindow(selection.map(button => button.getTitle()));
+ const win = this.__createConfirmDeleteWindow(selection.map(button => button.getTitle()));
win.center();
win.open();
win.addListener("close", () => {
@@ -1154,6 +1336,20 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
return deleteButton;
},
+ __emptyTrash: function() {
+ const win = this.__createConfirmEmptyTrashWindow();
+ win.center();
+ win.open();
+ win.addListener("close", () => {
+ if (win.getConfirmed()) {
+ osparc.data.Resources.fetch("trash", "delete")
+ .then(() => {
+ this.__resetStudiesList();
+ });
+ }
+ }, this);
+ },
+
__createSelectButton: function() {
const selectButton = new qx.ui.form.ToggleButton().set({
appearance: "form-button-outlined",
@@ -1297,9 +1493,25 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
const menu = card.getMenu();
const studyData = card.getResourceData();
+ const trashed = Boolean(studyData["trashedAt"]);
const writeAccess = osparc.data.model.Study.canIWrite(studyData["accessRights"]);
const deleteAccess = osparc.data.model.Study.canIDelete(studyData["accessRights"]);
+ if (this.getCurrentContext() === "trash") {
+ if (trashed) {
+ if (writeAccess) {
+ const untrashButton = this.__getUntrashStudyMenuButton(studyData);
+ menu.add(untrashButton);
+ }
+ if (deleteAccess) {
+ const deleteButton = this.__getDeleteStudyMenuButton(studyData, false);
+ menu.addSeparator();
+ menu.add(deleteButton);
+ }
+ }
+ return;
+ }
+
const openButton = this._getOpenMenuButton(studyData);
if (openButton) {
menu.add(openButton);
@@ -1363,11 +1575,14 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
}
if (deleteAccess) {
+ menu.addSeparator();
+ const trashButton = this.__getTrashStudyMenuButton(studyData, false);
+ menu.add(trashButton);
+ } else if (this.__deleteOrRemoveMe(studyData) === "remove") {
+ // if I'm a collaborator, let me remove myself from the study. In that case it would be a Delete for me
+ menu.addSeparator();
const deleteButton = this.__getDeleteStudyMenuButton(studyData, false);
- if (deleteButton) {
- menu.addSeparator();
- menu.add(deleteButton);
- }
+ menu.add(deleteButton);
}
card.evaluateMenuButtons();
@@ -1549,13 +1764,33 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
},
_deleteResourceRequested: function(studyId) {
- this.__deleteStudyRequested(this.__getStudyData(studyId));
+ if (this.getCurrentContext() === "trash") {
+ this.__deleteStudyRequested(this.__getStudyData(studyId));
+ } else {
+ this.__trashStudyRequested(this.__getStudyData(studyId));
+ }
+ },
+
+ __trashStudyRequested: function(studyData) {
+ const preferencesSettings = osparc.Preferences.getInstance();
+ if (preferencesSettings.getConfirmDeleteStudy()) {
+ const win = this.__createConfirmTrashWindow([studyData.name]);
+ win.center();
+ win.open();
+ win.addListener("close", () => {
+ if (win.getConfirmed()) {
+ this.__trashStudy(studyData);
+ }
+ }, this);
+ } else {
+ this.__trashStudy(studyData);
+ }
},
__deleteStudyRequested: function(studyData) {
const preferencesSettings = osparc.Preferences.getInstance();
if (preferencesSettings.getConfirmDeleteStudy()) {
- const win = this.__createConfirmWindow([studyData.name]);
+ const win = this.__deleteOrRemoveMe(studyData) === "remove" ? this.__createConfirmRemoveForMeWindow(studyData.name) : this.__createConfirmDeleteWindow([studyData.name]);
win.center();
win.open();
win.addListener("close", () => {
@@ -1568,6 +1803,27 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
}
},
+ __getTrashStudyMenuButton: function(studyData) {
+ const trashButton = new qx.ui.menu.Button(this.tr("Trash"), "@FontAwesome5Solid/trash/12");
+ trashButton["trashButton"] = true;
+ trashButton.set({
+ appearance: "menu-button"
+ });
+ osparc.utils.Utils.setIdToWidget(trashButton, "studyItemMenuDelete");
+ trashButton.addListener("execute", () => this.__trashStudyRequested(studyData), this);
+ return trashButton;
+ },
+
+ __getUntrashStudyMenuButton: function(studyData) {
+ const restoreButton = new qx.ui.menu.Button(this.tr("Restore"), "@MaterialIcons/restore_from_trash/16");
+ restoreButton["untrashButton"] = true;
+ restoreButton.set({
+ appearance: "menu-button"
+ });
+ restoreButton.addListener("execute", () => this.__untrashStudy(studyData), this);
+ return restoreButton;
+ },
+
__getDeleteStudyMenuButton: function(studyData) {
const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12");
deleteButton["deleteButton"] = true;
@@ -1730,17 +1986,60 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
req.send(body);
},
- __doDeleteStudy: function(studyData) {
+ __untrashStudy: function(studyData) {
+ osparc.store.Store.getInstance().untrashStudy(studyData.uuid)
+ .then(() => {
+ this.__removeFromStudyList(studyData.uuid);
+ const msg = this.tr("Successfully restored");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.evaluateTrashEmpty();
+ })
+ .catch(err => {
+ console.error(err);
+ osparc.FlashMessenger.getInstance().logAs(err, "ERROR");
+ })
+ .finally(() => this.resetSelection());
+ },
+
+ __trashStudy: function(studyData) {
+ osparc.store.Store.getInstance().trashStudy(studyData.uuid)
+ .then(() => {
+ this.__removeFromStudyList(studyData.uuid);
+ const msg = this.tr("Successfully moved to Trash");
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+ this._resourceFilter.setTrashEmpty(false);
+ })
+ .catch(err => {
+ console.error(err);
+ osparc.FlashMessenger.getInstance().logAs(err, "ERROR");
+ })
+ .finally(() => this.resetSelection());
+ },
+
+ __trashStudies: function(studiesData) {
+ studiesData.forEach(studyData => this.__trashStudy(studyData));
+ },
+
+ __deleteOrRemoveMe: function(studyData) {
+ const deleteAccess = osparc.data.model.Study.canIDelete(studyData["accessRights"]);
const myGid = osparc.auth.Data.getInstance().getGroupId();
const collabGids = Object.keys(studyData["accessRights"]);
const amICollaborator = collabGids.indexOf(myGid) > -1;
+ return (!deleteAccess && collabGids.length > 1 && amICollaborator) ? "remove" : "delete";
+ },
+ __removeMeFromCollaborators: function(studyData) {
+ const arCopy = osparc.utils.Utils.deepCloneObject(studyData["accessRights"]);
+ // remove me from collaborators
+ const myGid = osparc.auth.Data.getInstance().getGroupId();
+ delete arCopy[myGid];
+ return osparc.info.StudyUtils.patchStudyData(studyData, "accessRights", arCopy);
+ },
+
+ __doDeleteStudy: function(studyData) {
let operationPromise = null;
- if (collabGids.length > 1 && amICollaborator) {
- const arCopy = osparc.utils.Utils.deepCloneObject(studyData["accessRights"]);
- // remove collaborator
- delete arCopy[myGid];
- operationPromise = osparc.info.StudyUtils.patchStudyData(studyData, "accessRights", arCopy);
+ if (this.__deleteOrRemoveMe(studyData) === "remove") {
+ operationPromise = this.__removeMeFromCollaborators(studyData);
} else {
// delete study
operationPromise = osparc.store.Store.getInstance().deleteStudy(studyData.uuid);
@@ -1758,10 +2057,41 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
studiesData.forEach(studyData => this.__doDeleteStudy(studyData));
},
- __createConfirmWindow: function(studyNames) {
- const rUSure = this.tr("Are you sure you want to delete");
+ __createConfirmTrashWindow: function(studyNames) {
+ let msg = this.tr("Are you sure you want to move");
+ if (studyNames.length > 1) {
+ const studiesText = osparc.product.Utils.getStudyAlias({plural: true});
+ msg += ` ${studyNames.length} ${studiesText} `
+ } else {
+ msg += ` '${studyNames[0]}' `;
+ }
+ msg += this.tr("to the Trash?");
+ const trashDays = osparc.store.StaticInfo.getInstance().getTrashRetentionDays();
+ msg += "
" + (studyNames.length > 1 ? "They" : "It") + this.tr(` will be permanently deleted after ${trashDays} days.`);
+ const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
+ caption: this.tr("Move to Trash"),
+ confirmText: this.tr("Move to Trash"),
+ confirmAction: "delete"
+ });
+ osparc.utils.Utils.setIdToWidget(confirmationWin.getConfirmButton(), "confirmDeleteStudyBtn");
+ return confirmationWin;
+ },
+
+ __createConfirmRemoveForMeWindow: function(studyName) {
+ const msg = `'${studyName} ` + this.tr("will be removed from your list. Collaborators will still have access.");
+ const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
+ caption: this.tr("Remove"),
+ confirmText: this.tr("Remove"),
+ confirmAction: "delete"
+ });
+ osparc.utils.Utils.setIdToWidget(confirmationWin.getConfirmButton(), "confirmDeleteStudyBtn");
+ return confirmationWin;
+ },
+
+ __createConfirmDeleteWindow: function(studyNames) {
+ let msg = this.tr("Are you sure you want to delete");
const studyAlias = osparc.product.Utils.getStudyAlias({plural: studyNames.length > 1});
- const msg = rUSure + (studyNames.length > 1 ? ` ${studyNames.length} ${studyAlias}?` : ` ${studyNames[0]}?`)
+ msg += (studyNames.length > 1 ? ` ${studyNames.length} ${studyAlias}?` : ` ${studyNames[0]}?`);
const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
caption: this.tr("Delete") + " " + studyAlias,
confirmText: this.tr("Delete"),
@@ -1771,6 +2101,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
return confirmationWin;
},
+ __createConfirmEmptyTrashWindow: function() {
+ const msg = this.tr("Items in the bin will be permanently deleted");
+ const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
+ caption: this.tr("Delete"),
+ confirmText: this.tr("Delete forever"),
+ confirmAction: "delete"
+ });
+ return confirmationWin;
+ },
+
// TASKS //
__tasksReceived: function(tasks) {
tasks.forEach(taskData => this._taskDataReceived(taskData));
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js
index 87a6a366b58..cb2e130cfcf 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js
@@ -16,7 +16,7 @@
************************************************************************ */
/**
- * Widget used for displaying a Workspace information
+ * Widget used for displaying a Study Browser's context information
*
*/
@@ -35,27 +35,20 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
alignY: "middle",
});
- this.__spacers = [];
-
this.initCurrentWorkspaceId();
this.initCurrentFolderId();
+
+ osparc.store.Store.getInstance().addListener("changeStudyBrowserContext", () => this.__buildLayout(), this);
},
events: {
"locationChanged": "qx.event.type.Data",
"workspaceUpdated": "qx.event.type.Data",
- "deleteWorkspaceRequested": "qx.event.type.Data"
+ "deleteWorkspaceRequested": "qx.event.type.Data",
+ "emptyTrashRequested": "qx.event.type.Event",
},
properties: {
- currentContext: {
- check: ["studiesAndFolders", "workspaces", "search"],
- nullable: false,
- init: "studiesAndFolders",
- event: "changeCurrentContext",
- apply: "__buildLayout"
- },
-
currentWorkspaceId: {
check: "Number",
nullable: true,
@@ -76,7 +69,7 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
nullable: false,
init: {},
event: "changeAccessRights",
- apply: "__applyAccessRights"
+ apply: "__updateShareInfo"
},
myAccessRights: {
@@ -89,7 +82,17 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
},
statics: {
- HEIGHT: 36
+ HEIGHT: 36,
+ POS: {
+ ICON: 0,
+ TITLE: 1,
+ BREADCRUMBS: 2,
+ EDIT_BUTTON: 3,
+ SHARE_LAYOUT: 4,
+ ROLE_LAYOUT: 5,
+ DESCRIPTION: 2,
+ EMPTY_TRASH_BUTTON: 3,
+ }
},
members: {
@@ -106,26 +109,25 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
allowGrowY: true,
decorator: "rounded",
});
- this._add(control);
+ this._addAt(control, this.self().POS.ICON);
break;
case "title":
control = new qx.ui.basic.Label().set({
font: "text-16",
alignY: "middle",
});
- this._add(control);
+ this._addAt(control, this.self().POS.TITLE);
break;
case "breadcrumbs":
control = new osparc.dashboard.ContextBreadcrumbs();
this.bind("currentWorkspaceId", control, "currentWorkspaceId");
this.bind("currentFolderId", control, "currentFolderId");
- this.bind("currentContext", control, "currentContext");
control.bind("currentWorkspaceId", this, "currentWorkspaceId");
control.bind("currentFolderId", this, "currentFolderId");
control.addListener("locationChanged", e => {
this.fireDataEvent("locationChanged", e.getData())
});
- this._add(control);
+ this._addAt(control, this.self().POS.BREADCRUMBS);
break;
case "edit-button":
control = new qx.ui.form.MenuButton().set({
@@ -142,14 +144,15 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
control.getContentElement().setStyles({
"border-radius": `${22 / 2}px`
});
- this._add(control);
+ this._addAt(control, this.self().POS.EDIT_BUTTON);
break;
case "share-layout":
- this.__addSpacer();
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
- alignY: "middle"
- }));
- this._add(control);
+ alignY: "middle",
+ })).set({
+ marginLeft: 10,
+ });
+ this._addAt(control, this.self().POS.SHARE_LAYOUT);
break;
case "share-text": {
control = new qx.ui.basic.Label().set({
@@ -160,11 +163,12 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
break;
}
case "role-layout":
- this.__addSpacer();
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5).set({
- alignY: "middle"
- }));
- this._add(control);
+ alignY: "middle",
+ })).set({
+ marginLeft: 10,
+ });
+ this._addAt(control, this.self().POS.ROLE_LAYOUT);
break;
case "role-text": {
control = new qx.ui.basic.Label().set({
@@ -180,6 +184,24 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
layout.addAt(control, 1);
break;
}
+ case "description": {
+ control = new qx.ui.basic.Label().set({
+ font: "text-14",
+ alignY: "middle",
+ });
+ this._addAt(control, this.self().POS.DESCRIPTION);
+ break;
+ }
+ case "empty-trash-button": {
+ control = new qx.ui.form.Button(this.tr("Empty Trash"), "@FontAwesome5Solid/trash/14").set({
+ appearance: "danger-button",
+ allowGrowY: false,
+ alignY: "middle",
+ });
+ control.addListener("execute", () => this.fireEvent("emptyTrashRequested"));
+ this._addAt(control, this.self().POS.EMPTY_TRASH_BUTTON);
+ break;
+ }
}
return control || this.base(arguments, id);
@@ -198,66 +220,82 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
__buildLayout: function() {
this.getChildControl("icon");
const title = this.getChildControl("title");
- title.resetCursor();
- title.removeListener("tap", this.__titleTapped, this);
- this.getChildControl("breadcrumbs");
- this.getChildControl("edit-button").exclude();
- this.resetAccessRights();
- this.resetMyAccessRights();
-
- const currentContext = this.getCurrentContext();
- if (currentContext === "search") {
- this.__setIcon("@FontAwesome5Solid/search/24");
- title.set({
- value: this.tr("Search results"),
- });
- } else if (currentContext === "workspaces") {
- this.__setIcon(osparc.store.Workspaces.iconPath(32));
- title.set({
- value: this.tr("Shared Workspaces"),
- })
- } else if (currentContext === "studiesAndFolders") {
- const workspaceId = this.getCurrentWorkspaceId();
- title.setCursor("pointer");
- title.addListener("tap", this.__titleTapped, this);
- const workspace = osparc.store.Workspaces.getInstance().getWorkspace(workspaceId);
- if (workspace) {
- const thumbnail = workspace.getThumbnail();
- this.__setIcon(thumbnail ? thumbnail : osparc.store.Workspaces.iconPath(32));
- workspace.bind("name", title, "value");
- workspace.bind("accessRights", this, "accessRights");
- workspace.bind("myAccessRights", this, "myAccessRights");
- } else {
- this.__setIcon("@FontAwesome5Solid/home/30");
- title.setValue(this.tr("My Workspace"));
- }
- }
- },
- __addSpacer: function() {
- const spacer = new qx.ui.basic.Label("-").set({
- font: "text-16",
- alignY: "middle",
+ const locationBreadcrumbs = this.getChildControl("breadcrumbs").set({
+ visibility: "excluded"
+ });
+ const editWorkspace = this.getChildControl("edit-button").set({
+ visibility: "excluded"
+ });
+ const shareWorkspaceLayout = this.getChildControl("share-layout").set({
+ visibility: "excluded"
+ });
+ const roleWorkspaceLayout = this.getChildControl("role-layout").set({
+ visibility: "excluded"
+ });
+
+ const description = this.getChildControl("description").set({
+ visibility: "excluded"
});
- this.__spacers.push(spacer);
- this._add(spacer);
+ // the study browser will take care of making it visible
+ this.getChildControl("empty-trash-button").set({
+ visibility: "excluded"
+ });
+
+ const currentContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ switch (currentContext) {
+ case "studiesAndFolders": {
+ const workspaceId = this.getCurrentWorkspaceId();
+ title.setCursor("pointer");
+ title.addListener("tap", this.__titleTapped, this);
+ locationBreadcrumbs.show();
+ const workspace = osparc.store.Workspaces.getInstance().getWorkspace(workspaceId);
+ if (workspace) {
+ const thumbnail = workspace.getThumbnail();
+ this.__setIcon(thumbnail ? thumbnail : osparc.store.Workspaces.iconPath(32));
+ workspace.bind("name", title, "value");
+ editWorkspace.show();
+ shareWorkspaceLayout.show();
+ roleWorkspaceLayout.show();
+ workspace.bind("accessRights", this, "accessRights");
+ workspace.bind("myAccessRights", this, "myAccessRights");
+ } else {
+ this.__setIcon("@FontAwesome5Solid/home/30");
+ title.setValue(this.tr("My Workspace"));
+ }
+ break;
+ }
+ case "workspaces":
+ this.__setIcon(osparc.store.Workspaces.iconPath(32));
+ title.setValue(this.tr("Shared Workspaces"));
+ break;
+ case "search":
+ this.__setIcon("@FontAwesome5Solid/search/24");
+ title.setValue(this.tr("Search results"));
+ break;
+ case "trash": {
+ this.__setIcon("@FontAwesome5Solid/trash/20");
+ title.setValue(this.tr("Trash"));
+ const trashDays = osparc.store.StaticInfo.getInstance().getTrashRetentionDays();
+ description.set({
+ value: this.tr(`Items in the bin will be permanently deleted after ${trashDays} days.`),
+ visibility: "visible",
+ });
+ break;
+ }
+ }
},
- __resetIcon: function() {
+ __setIcon: function(source) {
+ // reset icon first
const icon = this.getChildControl("icon");
const image = icon.getChildControl("image");
image.resetSource();
icon.getContentElement().setStyles({
"background-image": "none"
});
- },
- __setIcon: function(source) {
- this.__resetIcon();
-
- const icon = this.getChildControl("icon");
if (source.includes("@")) {
- const image = icon.getChildControl("image");
image.set({
source
});
@@ -272,10 +310,6 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
}
},
- __showSpacers: function(show) {
- this.__spacers.forEach(spacer => spacer.setVisibility(show ? "visible" : "excluded"));
- },
-
__getShareIcon: function() {
// reset previous
const layout = this.getChildControl("share-layout");
@@ -292,7 +326,7 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
return shareIcon;
},
- __applyAccessRights: function(accessRights) {
+ __updateShareInfo: function(accessRights) {
const shareIcon = this.__getShareIcon();
const shareText = this.getChildControl("share-text");
if (accessRights && Object.keys(accessRights).length) {
@@ -300,11 +334,9 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
shareText.setValue(Object.keys(accessRights).length + " members");
shareIcon.show();
shareText.show();
- this.__showSpacers(true);
} else {
shareIcon.exclude();
shareText.exclude();
- this.__showSpacers(false);
}
},
@@ -312,7 +344,8 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
const editButton = this.getChildControl("edit-button");
const roleText = this.getChildControl("role-text");
const roleIcon = this.getChildControl("role-icon");
- if (value && Object.keys(value).length) {
+ const currentContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ if (currentContext === "studiesAndFolders" && value && Object.keys(value).length) {
editButton.setVisibility(value["delete"] ? "visible" : "excluded");
const menu = new qx.ui.menu.Menu().set({
position: "bottom-right"
@@ -328,12 +361,10 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
roleText.setValue(osparc.data.Roles.WORKSPACE[val].label);
roleText.show();
roleIcon.show();
- this.__showSpacers(true);
} else {
editButton.exclude();
roleText.exclude();
roleIcon.exclude();
- this.__showSpacers(false);
}
},
@@ -346,6 +377,7 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
win.close();
this.__buildLayout();
}, this);
+ workspaceEditor.addListener("cancel", () => win.close());
},
__openShareWith: function() {
@@ -355,7 +387,7 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", {
const win = osparc.ui.window.Window.popUpInWindow(permissionsView, title, 500, 400);
permissionsView.addListener("updateAccessRights", () => {
win.close();
- this.__applyAccessRights(workspace.getAccessRights());
+ this.__updateShareInfo(workspace.getAccessRights());
}, 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 3ff37cd8412..0b6fc8ccd26 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js
@@ -246,11 +246,7 @@ qx.Class.define("osparc.dashboard.TemplateBrowser", {
// LAYOUT //
_createLayout: function() {
this._createSearchBar();
- this._createResourcesLayout();
- const list = this._resourcesContainer.getFlatList();
- if (list) {
- osparc.utils.Utils.setIdToWidget(list, "templatesList");
- }
+ this._createResourcesLayout("templatesList");
const updateAllButton = this.__createUpdateAllButton();
if (updateAllButton) {
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js
index 187be3598e2..eb777ca5dd7 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js
@@ -45,7 +45,9 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", {
events: {
"workspaceSelected": "qx.event.type.Data",
"workspaceUpdated": "qx.event.type.Data",
- "deleteWorkspaceRequested": "qx.event.type.Data"
+ "trashWorkspaceRequested": "qx.event.type.Data",
+ "untrashWorkspaceRequested": "qx.event.type.Data",
+ "deleteWorkspaceRequested": "qx.event.type.Data",
},
properties: {
@@ -183,31 +185,46 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", {
position: "bottom-right"
});
- const editButton = new qx.ui.menu.Button(this.tr("Edit..."), "@FontAwesome5Solid/pencil-alt/12");
- editButton.addListener("execute", () => {
- const workspace = this.getWorkspace();
- const workspaceEditor = new osparc.editor.WorkspaceEditor(workspace);
- const title = this.tr("Edit Workspace");
- const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 150);
- workspaceEditor.addListener("workspaceUpdated", () => {
- win.close();
- this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId());
+ const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ if (
+ studyBrowserContext === "search" ||
+ studyBrowserContext === "workspaces"
+ ) {
+ const editButton = new qx.ui.menu.Button(this.tr("Edit..."), "@FontAwesome5Solid/pencil-alt/12");
+ editButton.addListener("execute", () => {
+ const workspace = this.getWorkspace();
+ const workspaceEditor = new osparc.editor.WorkspaceEditor(workspace);
+ const title = this.tr("Edit Workspace");
+ const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 150);
+ workspaceEditor.addListener("workspaceUpdated", () => {
+ win.close();
+ this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId());
+ });
+ workspaceEditor.addListener("cancel", () => win.close());
});
- workspaceEditor.addListener("cancel", () => win.close());
- });
- menu.add(editButton);
+ 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);
- 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();
- menu.addSeparator();
+ const trashButton = new qx.ui.menu.Button(this.tr("Trash"), "@FontAwesome5Solid/trash/12");
+ trashButton.addListener("execute", () => this.__trashWorkspaceRequested(), this);
+ menu.add(trashButton);
+ } else if (studyBrowserContext === "trash") {
+ const restoreButton = new qx.ui.menu.Button(this.tr("Restore"), "@MaterialIcons/restore_from_trash/16");
+ restoreButton.addListener("execute", () => this.fireDataEvent("untrashWorkspaceRequested", this.getWorkspace()), this);
+ menu.add(restoreButton);
- const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12");
- osparc.utils.Utils.setIdToWidget(deleteButton, "deleteWorkspaceMenuItem");
- deleteButton.addListener("execute", () => this.__deleteWorkspaceRequested(), this);
- menu.add(deleteButton);
+ menu.addSeparator();
+ const deleteButton = new qx.ui.menu.Button(this.tr("Delete"), "@FontAwesome5Solid/trash/12");
+ osparc.utils.Utils.setIdToWidget(deleteButton, "deleteWorkspaceMenuItem");
+ deleteButton.addListener("execute", () => this.__deleteWorkspaceRequested(), this);
+ menu.add(deleteButton);
+ }
menuButton.setMenu(menu);
}
},
@@ -237,7 +254,9 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", {
},
__itemSelected: function(newVal) {
- if (newVal) {
+ const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext();
+ // do not allow selecting workspace
+ if (studyBrowserContext !== "trash" && newVal) {
this.fireDataEvent("workspaceSelected", this.getWorkspaceId());
}
this.setValue(false);
@@ -250,6 +269,24 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", {
permissionsView.addListener("updateAccessRights", () => this.__applyAccessRights(this.getWorkspace().getAccessRights()), this);
},
+ __trashWorkspaceRequested: function() {
+ const trashDays = osparc.store.StaticInfo.getInstance().getTrashRetentionDays();
+ let msg = this.tr("Are you sure you want to move the Workspace and all its content to the trash?");
+ msg += "
" + this.tr("It will be permanently deleted after ") + trashDays + " days.";
+ const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
+ caption: this.tr("Move to Trash"),
+ confirmText: this.tr("Move to Trash"),
+ confirmAction: "delete"
+ });
+ confirmationWin.center();
+ confirmationWin.open();
+ confirmationWin.addListener("close", () => {
+ if (confirmationWin.getConfirmed()) {
+ this.fireDataEvent("trashWorkspaceRequested", this.getWorkspaceId());
+ }
+ }, 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.");
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js
index 533c90c4643..da5a21797f1 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js
@@ -46,7 +46,7 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", {
this.__initTree();
// preselect "My Workspace"
- this.contextChanged(null, null);
+ this.contextChanged("studiesAndFolders");
osparc.store.Folders.getInstance().addListener("folderAdded", e => {
const folder = e.getData();
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js
index 13793622cb7..75f120a86c5 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js
@@ -24,6 +24,7 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTreeItem", {
this.set({
indent: 12, // defaults to 19,
decorator: "rounded",
+ padding: 2,
maxWidth: osparc.dashboard.ResourceBrowserBase.SIDE_SPACER_WIDTH - 12,
});
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 8ff5eb822ba..ad1b51cc840 100644
--- a/services/static-webserver/client/source/class/osparc/data/Resources.js
+++ b/services/static-webserver/client/source/class/osparc/data/Resources.js
@@ -119,6 +119,16 @@ qx.Class.define("osparc.data.Resources", {
method: "GET",
url: statics.API + "/projects?type=user"
},
+ getOne: {
+ useCache: false,
+ method: "GET",
+ url: statics.API + "/projects/{studyId}"
+ },
+ getActive: {
+ useCache: false,
+ method: "GET",
+ url: statics.API + "/projects/active?client_session_id={tabId}"
+ },
getPage: {
useCache: false,
method: "GET",
@@ -129,15 +139,10 @@ qx.Class.define("osparc.data.Resources", {
method: "GET",
url: statics.API + "/projects:search?offset={offset}&limit={limit}&text={text}&tag_ids={tagIds}&order_by={orderBy}"
},
- getOne: {
- useCache: false,
- method: "GET",
- url: statics.API + "/projects/{studyId}"
- },
- getActive: {
+ getPageTrashed: {
useCache: false,
method: "GET",
- url: statics.API + "/projects/active?client_session_id={tabId}"
+ url: statics.API + "/projects?filters={%22trashed%22:%22true%22}&offset={offset}&limit={limit}&order_by={orderBy}"
},
postToTemplate: {
method: "POST",
@@ -187,6 +192,14 @@ qx.Class.define("osparc.data.Resources", {
method: "PATCH",
url: statics.API + "/projects/{studyId}"
},
+ trash: {
+ method: "POST",
+ url: statics.API + "/projects/{studyId}:trash"
+ },
+ untrash: {
+ method: "POST",
+ url: statics.API + "/projects/{studyId}:untrash"
+ },
delete: {
method: "DELETE",
url: statics.API + "/projects/{studyId}"
@@ -301,14 +314,19 @@ qx.Class.define("osparc.data.Resources", {
method: "GET",
url: statics.API + "/folders?workspace_id={workspaceId}&folder_id={folderId}&offset={offset}&limit={limit}&order_by={orderBy}"
},
+ getOne: {
+ method: "GET",
+ url: statics.API + "/folders/{folderId}"
+ },
getPageSearch: {
useCache: false,
method: "GET",
url: statics.API + "/folders:search?offset={offset}&limit={limit}&text={text}&order_by={orderBy}"
},
- getOne: {
+ getPageTrashed: {
+ useCache: false,
method: "GET",
- url: statics.API + "/folders/{folderId}"
+ url: statics.API + "/folders?filters={%22trashed%22:%22true%22}&offset={offset}&limit={limit}&order_by={orderBy}"
},
post: {
method: "POST",
@@ -326,6 +344,14 @@ qx.Class.define("osparc.data.Resources", {
method: "PUT",
url: statics.API + "/folders/{folderId}/folders/{workspaceId}"
},
+ trash: {
+ method: "POST",
+ url: statics.API + "/folders/{folderId}:trash"
+ },
+ untrash: {
+ method: "POST",
+ url: statics.API + "/folders/{folderId}:untrash"
+ },
}
},
"workspaces": {
@@ -338,6 +364,16 @@ qx.Class.define("osparc.data.Resources", {
method: "GET",
url: statics.API + "/workspaces/{workspaceId}"
},
+ getPageSearch: {
+ useCache: false,
+ method: "GET",
+ url: statics.API + "/workspaces:search?offset={offset}&limit={limit}&text={text}&order_by={orderBy}"
+ },
+ getPageTrashed: {
+ useCache: false,
+ method: "GET",
+ url: statics.API + "/workspaces?filters={%22trashed%22:%22true%22}&offset={offset}&limit={limit}&order_by={orderBy}"
+ },
post: {
method: "POST",
url: statics.API + "/workspaces"
@@ -350,6 +386,14 @@ qx.Class.define("osparc.data.Resources", {
method: "DELETE",
url: statics.API + "/workspaces/{workspaceId}"
},
+ trash: {
+ method: "POST",
+ url: statics.API + "/workspaces/{workspaceId}:trash"
+ },
+ untrash: {
+ method: "POST",
+ url: statics.API + "/workspaces/{workspaceId}:untrash"
+ },
postAccessRights: {
method: "POST",
url: statics.API + "/workspaces/{workspaceId}/groups/{groupId}"
@@ -402,6 +446,18 @@ qx.Class.define("osparc.data.Resources", {
}
},
+ /*
+ * TRASH
+ */
+ "trash": {
+ endpoints: {
+ delete: {
+ method: "DELETE",
+ url: statics.API + "/trash"
+ }
+ }
+ },
+
/*
* SNAPSHOTS
*/
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 b8b9eb03b21..6a255a45dc5 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
@@ -37,7 +37,7 @@ qx.Class.define("osparc.data.model.Folder", {
owner: folderData.owner,
createdAt: new Date(folderData.createdAt),
lastModified: new Date(folderData.modifiedAt),
- trashedAt: folderData.trashedAt ? new Date(folderData.trashedAt) : this.getTrashedAt(),
+ trashedAt: folderData.trashedAt ? new Date(folderData.trashedAt) : null,
});
},
diff --git a/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js b/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js
index fa037642af4..583b31f979e 100644
--- a/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js
+++ b/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js
@@ -300,7 +300,7 @@ qx.Class.define("osparc.data.model.IframeHandler", {
if (response.status < 400) {
this.__serviceReadyIn(srvUrl);
} else {
- console.log(`Connecting: ${srvUrl} is not reachable. Status: ${response.status}`);
+ console.error(`Connecting: ${srvUrl} is not reachable. Status: ${response.status}`);
retry();
}
})
diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workspace.js b/services/static-webserver/client/source/class/osparc/data/model/Workspace.js
index 56023d1eb4e..65592fb1789 100644
--- a/services/static-webserver/client/source/class/osparc/data/model/Workspace.js
+++ b/services/static-webserver/client/source/class/osparc/data/model/Workspace.js
@@ -37,6 +37,8 @@ qx.Class.define("osparc.data.model.Workspace", {
accessRights: workspaceData.accessRights,
createdAt: new Date(workspaceData.createdAt),
modifiedAt: new Date(workspaceData.modifiedAt),
+ trashedAt: workspaceData.trashedAt ? new Date(workspaceData.trashedAt) : null,
+ trashedBy: workspaceData.trashedBy,
});
},
@@ -95,7 +97,19 @@ qx.Class.define("osparc.data.model.Workspace", {
nullable: true,
init: null,
event: "changeModifiedAt"
- }
+ },
+
+ trashedAt: {
+ check: "Date",
+ nullable: true,
+ init: null,
+ },
+
+ trashedBy: {
+ check: "Number",
+ nullable: true,
+ init: null,
+ },
},
statics: {
diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js
index d6e83d8fb23..4206cb212dd 100644
--- a/services/static-webserver/client/source/class/osparc/store/Folders.js
+++ b/services/static-webserver/client/source/class/osparc/store/Folders.js
@@ -78,6 +78,33 @@ qx.Class.define("osparc.store.Folders", {
});
},
+ fetchAllTrashedFolders: function(orderBy = {
+ field: "modified_at",
+ direction: "desc"
+ }) {
+ if (osparc.auth.Data.getInstance().isGuest()) {
+ return new Promise(resolve => {
+ resolve([]);
+ });
+ }
+
+ const curatedOrderBy = this.self().curateOrderBy(orderBy);
+ const params = {
+ url: {
+ orderBy: JSON.stringify(curatedOrderBy),
+ }
+ };
+ return osparc.data.Resources.getInstance().getAllPages("folders", params, "getPageTrashed")
+ .then(trashedFoldersData => {
+ const folders = [];
+ trashedFoldersData.forEach(folderData => {
+ const folder = this.__addToCache(folderData);
+ folders.push(folder);
+ });
+ return folders;
+ });
+ },
+
searchFolders: function(
text,
orderBy = {
@@ -126,6 +153,37 @@ qx.Class.define("osparc.store.Folders", {
});
},
+ trashFolder: function(folderId, workspaceId) {
+ const params = {
+ "url": {
+ folderId
+ }
+ };
+ return osparc.data.Resources.getInstance().fetch("folders", "trash", params)
+ .then(() => {
+ const folder = this.getFolder(folderId);
+ if (folder) {
+ this.__deleteFromCache(folderId, workspaceId);
+ this.fireDataEvent("folderRemoved", folder);
+ }
+ })
+ .catch(console.error);
+ },
+
+ untrashFolder: function(folder) {
+ const params = {
+ "url": {
+ folderId: folder.getFolderId(),
+ }
+ };
+ return osparc.data.Resources.getInstance().fetch("folders", "untrash", params)
+ .then(() => {
+ this.foldersCached.unshift(folder);
+ this.fireDataEvent("folderAdded", folder);
+ })
+ .catch(console.error);
+ },
+
deleteFolder: function(folderId, workspaceId) {
const params = {
"url": {
diff --git a/services/static-webserver/client/source/class/osparc/store/StaticInfo.js b/services/static-webserver/client/source/class/osparc/store/StaticInfo.js
index 1681a801cf4..2ac96fd58b0 100644
--- a/services/static-webserver/client/source/class/osparc/store/StaticInfo.js
+++ b/services/static-webserver/client/source/class/osparc/store/StaticInfo.js
@@ -69,6 +69,16 @@ qx.Class.define("osparc.store.StaticInfo", {
return null;
},
+ getTrashRetentionDays: function() {
+ const staticKey = "webserverProjects";
+ const wsStaticData = this.getValue(staticKey);
+ const key = "PROJECTS_TRASH_RETENTION_DAYS";
+ if (key in wsStaticData) {
+ return wsStaticData[key];
+ }
+ return "unknown";
+ },
+
getAccountDeletionRetentionDays: function() {
const staticKey = "webserverLogin";
const wsStaticData = this.getValue(staticKey);
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 d2c2d24104e..7b94b336852 100644
--- a/services/static-webserver/client/source/class/osparc/store/Store.js
+++ b/services/static-webserver/client/source/class/osparc/store/Store.js
@@ -67,7 +67,7 @@ qx.Class.define("osparc.store.Store", {
nullable: true
},
studyBrowserContext: {
- check: ["studiesAndFolders", "workspaces", "search"],
+ check: ["studiesAndFolders", "workspaces", "search", "trash"],
init: "studiesAndFolders",
nullable: false,
event: "changeStudyBrowserContext",
@@ -413,6 +413,43 @@ qx.Class.define("osparc.store.Store", {
}
},
+ trashStudy: function(studyId) {
+ const params = {
+ url: {
+ studyId
+ }
+ };
+ return new Promise((resolve, reject) => {
+ osparc.data.Resources.fetch("studies", "trash", params)
+ .then(() => {
+ this.remove("studies", "uuid", studyId);
+ resolve();
+ })
+ .catch(err => {
+ console.error(err);
+ reject(err);
+ });
+ });
+ },
+
+ untrashStudy: function(studyId) {
+ const params = {
+ url: {
+ studyId
+ }
+ };
+ return new Promise((resolve, reject) => {
+ osparc.data.Resources.fetch("studies", "untrash", params)
+ .then(() => {
+ resolve();
+ })
+ .catch(err => {
+ console.error(err);
+ reject(err);
+ });
+ });
+ },
+
getTemplate: function(templateId) {
const templates = this.getTemplates();
return templates.find(template => template["uuid"] === templateId);
diff --git a/services/static-webserver/client/source/class/osparc/store/Workspaces.js b/services/static-webserver/client/source/class/osparc/store/Workspaces.js
index 253ac714a1d..924312639de 100644
--- a/services/static-webserver/client/source/class/osparc/store/Workspaces.js
+++ b/services/static-webserver/client/source/class/osparc/store/Workspaces.js
@@ -46,6 +46,15 @@ qx.Class.define("osparc.store.Workspaces", {
thumbnail,
};
},
+
+ curateOrderBy: function(orderBy) {
+ const curatedOrderBy = osparc.utils.Utils.deepCloneObject(orderBy);
+ if (curatedOrderBy.field !== "name") {
+ // only "modified_at" and "name" supported
+ curatedOrderBy.field = "modified_at";
+ }
+ return curatedOrderBy;
+ },
},
members: {
@@ -61,26 +70,105 @@ qx.Class.define("osparc.store.Workspaces", {
return osparc.data.Resources.getInstance().getAllPages("workspaces")
.then(workspacesData => {
workspacesData.forEach(workspaceData => {
- const workspace = new osparc.data.model.Workspace(workspaceData);
- this.__addToCache(workspace);
+ this.__addToCache(workspaceData);
});
return this.workspacesCached;
});
},
+ fetchAllTrashedWorkspaces: function(orderBy = {
+ field: "modified_at",
+ direction: "desc"
+ }) {
+ if (osparc.auth.Data.getInstance().isGuest()) {
+ return new Promise(resolve => {
+ resolve([]);
+ });
+ }
+
+ const curatedOrderBy = this.self().curateOrderBy(orderBy);
+ const params = {
+ url: {
+ orderBy: JSON.stringify(curatedOrderBy),
+ }
+ };
+ return osparc.data.Resources.getInstance().getAllPages("workspaces", params, "getPageTrashed")
+ .then(trashedWorkspacesData => {
+ const workspaces = [];
+ trashedWorkspacesData.forEach(workspaceData => {
+ const workspace = this.__addToCache(workspaceData);
+ workspaces.push(workspace);
+ });
+ return workspaces;
+ });
+ },
+
+ searchWorkspaces: function(text) {
+ if (osparc.auth.Data.getInstance().isGuest()) {
+ return new Promise(resolve => {
+ resolve([]);
+ });
+ }
+
+ const params = {
+ url: {
+ text,
+ }
+ };
+ return osparc.data.Resources.getInstance().getAllPages("workspaces", params, "getPageSearch")
+ .then(workspacesData => {
+ const workspaces = [];
+ workspacesData.forEach(workspaceData => {
+ const workspace = this.__addToCache(workspaceData);
+ workspaces.push(workspace);
+ });
+ return workspaces;
+ });
+ },
+
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);
+ const newWorkspace = this.__addToCache(workspaceData);
this.fireDataEvent("workspaceAdded", newWorkspace);
return newWorkspace;
});
},
+ trashWorkspace: function(workspaceId) {
+ const params = {
+ "url": {
+ workspaceId
+ }
+ };
+ return osparc.data.Resources.getInstance().fetch("workspaces", "trash", params)
+ .then(() => {
+ const workspace = this.getWorkspace(workspaceId);
+ if (workspace) {
+ this.__deleteFromCache(workspaceId);
+ this.fireDataEvent("workspaceRemoved", workspace);
+ }
+ })
+ .catch(console.error);
+ },
+
+ untrashWorkspace: function(workspace) {
+ const params = {
+ "url": {
+ workspaceId: workspace.getWorkspaceId(),
+ }
+ };
+ return osparc.data.Resources.getInstance().fetch("workspaces", "untrash", params)
+ .then(() => {
+ this.workspacesCached.unshift(workspace);
+ this.fireDataEvent("workspaceAdded", workspace);
+ })
+ .catch(console.error);
+ },
+
deleteWorkspace: function(workspaceId) {
const params = {
"url": {
@@ -201,11 +289,27 @@ qx.Class.define("osparc.store.Workspaces", {
return this.workspacesCached;
},
- __addToCache: function(workspace) {
- const found = this.workspacesCached.find(w => w.getWorkspaceId() === workspace.getWorkspaceId());
- if (!found) {
+ __addToCache: function(workspaceData) {
+ let workspace = this.workspacesCached.find(w => w.getWorkspaceId() === workspaceData["workspaceId"]);
+ if (workspace) {
+ const props = Object.keys(qx.util.PropertyUtil.getProperties(osparc.data.model.Workspace));
+ // put
+ Object.keys(workspaceData).forEach(key => {
+ if (key === "createdAt") {
+ workspace.set("createdAt", new Date(workspaceData["createdAt"]));
+ } else if (key === "modifiedAt") {
+ workspace.set("modifiedAt", new Date(workspaceData["modifiedAt"]));
+ } else if (key === "trashedAt") {
+ workspace.set("trashedAt", new Date(workspaceData["trashedAt"]));
+ } else if (props.includes(key)) {
+ workspace.set(key, workspaceData[key]);
+ }
+ });
+ } else {
+ workspace = new osparc.data.model.Workspace(workspaceData);
this.workspacesCached.unshift(workspace);
}
+ return workspace;
},
__deleteFromCache: function(workspaceId) {