Skip to content

Commit c1f0a35

Browse files
authored
🎨 [Frontend] Enh: batch delete files (#7458)
1 parent 5f1c5d7 commit c1f0a35

File tree

9 files changed

+77
-144
lines changed

9 files changed

+77
-144
lines changed

services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ qx.Class.define("osparc.dashboard.DataBrowser", {
7676
reloadButton.addListener("execute", () => this.__reloadTree(), this);
7777

7878
const selectedFileLayout = treeFolderView.getChildControl("folder-viewer").getChildControl("selected-file-layout");
79-
selectedFileLayout.addListener("fileDeleted", e => this.__fileDeleted(e.getData()), this);
79+
selectedFileLayout.addListener("pathsDeleted", e => treeFolderView.pathsDeleted(e.getData()), this);
8080
},
8181

8282
__reloadTree: function() {
@@ -89,34 +89,5 @@ qx.Class.define("osparc.dashboard.DataBrowser", {
8989
const folderViewer = treeFolderView.getChildControl("folder-viewer");
9090
folderViewer.resetFolder();
9191
},
92-
93-
__fileDeleted: function(fileMetadata) {
94-
// After deleting a file, try to keep the user in the same folder.
95-
// If the folder doesn't longer exist, open the closest available parent
96-
97-
const pathParts = fileMetadata["fileUuid"].split("/");
98-
99-
const treeFolderView = this.getChildControl("tree-folder-view");
100-
const foldersTree = treeFolderView.getChildControl("folder-tree");
101-
const folderViewer = treeFolderView.getChildControl("folder-viewer");
102-
103-
const openSameFolder = () => {
104-
// drop last, which is the file
105-
pathParts.pop();
106-
treeFolderView.openPath(pathParts);
107-
};
108-
109-
folderViewer.resetFolder();
110-
const locationId = fileMetadata["locationId"];
111-
const path = pathParts[0];
112-
foldersTree.resetCache();
113-
foldersTree.populateLocations()
114-
.then(datasetPromises => {
115-
Promise.all(datasetPromises)
116-
.then(() => foldersTree.requestPathItems(locationId, path))
117-
.then(() => openSameFolder());
118-
})
119-
.catch(err => console.error(err));
120-
}
12192
}
12293
});

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,10 +1190,6 @@ qx.Class.define("osparc.data.Resources", {
11901190
copy: {
11911191
method: "PUT",
11921192
url: statics.API + "/storage/locations/{toLoc}/files/{fileName}?extra_location={fromLoc}&extra_source={fileUuid}"
1193-
},
1194-
delete: {
1195-
method: "DELETE",
1196-
url: statics.API + "/storage/locations/{locationId}/files/{fileUuid}"
11971193
}
11981194
}
11991195
},
@@ -1219,6 +1215,10 @@ qx.Class.define("osparc.data.Resources", {
12191215
method: "GET",
12201216
url: statics.API + "/storage/locations/{locationId}/paths?file_filter={path}&cursor={cursor}&size=1000"
12211217
},
1218+
batchDelete: {
1219+
method: "POST",
1220+
url: statics.API + "/storage/locations/{locationId}/-/paths:batchDelete"
1221+
},
12221222
requestSize: {
12231223
method: "POST",
12241224
url: statics.API + "/storage/locations/0/paths/{pathId}:size"

services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* It is used together with a virtual tree of files where the selection is displayed
2222
* in the text field and the download and delete are related to that selection.
2323
* Download and deleted methods are also provided.
24-
* If a file is deleted it fires "fileDeleted" data event
24+
* If a file is deleted it fires "pathsDeleted" data event
2525
*
2626
* *Example*
2727
*
@@ -54,7 +54,7 @@ qx.Class.define("osparc.file.FileLabelWithActions", {
5454
},
5555

5656
events: {
57-
"fileDeleted": "qx.event.type.Data"
57+
"pathsDeleted": "qx.event.type.Data",
5858
},
5959

6060
properties: {
@@ -188,6 +188,13 @@ qx.Class.define("osparc.file.FileLabelWithActions", {
188188
}
189189
}
190190
}
191+
if (toBeDeleted.length === 0) {
192+
return;
193+
}
194+
if (toBeDeleted[0].getLocation() != 0) {
195+
osparc.FlashMessenger.logAs(this.tr("You can only delete files in the local storage"), "WARNING");
196+
return;
197+
}
191198

192199
let msg = this.tr("This action cannot be undone.");
193200
msg += isFolderSelected ? ("<br>"+this.tr("All contents within the folders will be deleted.")) : "";
@@ -207,36 +214,24 @@ qx.Class.define("osparc.file.FileLabelWithActions", {
207214
},
208215

209216
__doDeleteSelected: function(toBeDeleted) {
210-
const requests = [];
211-
toBeDeleted.forEach(selection => {
212-
if (selection) {
213-
let request = null;
214-
if (osparc.file.FilesTree.isFile(selection)) {
215-
request = this.__deleteItem(selection.getFileId(), selection.getLocation());
216-
} else {
217-
request = this.__deleteItem(selection.getPath(), selection.getLocation());
218-
}
219-
if (request) {
220-
requests.push(request);
221-
}
222-
}
223-
});
224-
Promise.all(requests)
225-
.then(datas => {
226-
if (datas.length) {
227-
this.fireDataEvent("fileDeleted", datas[0]);
228-
osparc.FlashMessenger.logAs(this.tr("Items successfully deleted"), "INFO");
229-
}
230-
});
231-
},
232-
233-
__deleteItem: function(itemId, locationId) {
234-
if (locationId !== 0 && locationId !== "0") {
235-
osparc.FlashMessenger.logAs(this.tr("Externally managed items cannot be deleted"));
236-
return null;
217+
if (toBeDeleted.length === 0) {
218+
osparc.FlashMessenger.logAs(this.tr("Nothing to delete"), "ERROR");
219+
return;
220+
} else if (toBeDeleted.length > 0) {
221+
const paths = toBeDeleted.map(item => item.getPath());
222+
const dataStore = osparc.store.Data.getInstance();
223+
const fetchPromise = dataStore.deleteFiles(paths);
224+
const pollTasks = osparc.store.PollTasks.getInstance();
225+
const interval = 1000;
226+
pollTasks.createPollingTask(fetchPromise, interval)
227+
.then(task => {
228+
task.addListener("resultReceived", e => {
229+
this.fireDataEvent("pathsDeleted", paths);
230+
osparc.FlashMessenger.logAs(this.tr("Items successfully deleted"), "INFO");
231+
});
232+
})
233+
.catch(err => osparc.FlashMessenger.logError(err, this.tr("Unsuccessful files deletion")));
237234
}
238-
const dataStore = osparc.store.Data.getInstance();
239-
return dataStore.deleteFile(locationId, itemId);
240235
},
241236
}
242237
});

services/static-webserver/client/source/class/osparc/file/FilePicker.js

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,12 @@ qx.Class.define("osparc.file.FilePicker", {
152152
getOutputFileMetadata: function(node) {
153153
return new Promise((resolve, reject) => {
154154
const outValue = osparc.file.FilePicker.getOutput(node.getOutputs());
155-
const params = {
156-
url: {
157-
locationId: outValue.store,
158-
path: outValue.path
159-
}
160-
};
161-
osparc.data.Resources.fetch("storagePaths", "getPaths", params)
162-
.then(pagResp => {
163-
if (pagResp["items"]) {
164-
const file = pagResp["items"].find(item => item.path === outValue.path);
155+
const locationId = outValue.store;
156+
const path = outValue.path;
157+
osparc.store.Data.getAllItems(locationId, path)
158+
.then(items => {
159+
if (items && items.length) {
160+
const file = items.find(item => item.path === outValue.path);
165161
if (file) {
166162
resolve(file["file_meta_data"]);
167163
return;

services/static-webserver/client/source/class/osparc/file/FolderContent.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@ qx.Class.define("osparc.file.FolderContent", {
261261
}
262262
},
263263

264+
resetSelection: function() {
265+
if (this.getMode() === "list") {
266+
const table = this.getChildControl("table");
267+
table.getSelectionModel().resetSelection();
268+
} else if (this.getMode() === "icons") {
269+
const iconsLayout = this.getChildControl("icons-layout");
270+
iconsLayout.getChildren().forEach(btn => btn.setValue(false));
271+
}
272+
},
273+
264274
__selectionChanged: function(selection) {
265275
if (this.isMultiSelect()) {
266276
this.fireDataEvent("multiSelectionChanged", selection);

services/static-webserver/client/source/class/osparc/file/FolderViewer.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ qx.Class.define("osparc.file.FolderViewer", {
179179

180180
__applyFolder: function() {
181181
this.getChildControl("selected-file-layout").resetSelection();
182-
}
182+
},
183+
184+
resetSelection: function() {
185+
const folderContent = this.getChildControl("folder-content");
186+
folderContent.resetSelection();
187+
188+
const selectedFileLayout = this.getChildControl("selected-file-layout");
189+
selectedFileLayout.resetSelection();
190+
},
183191
}
184192
});

services/static-webserver/client/source/class/osparc/file/TreeFolderView.js

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,9 @@ qx.Class.define("osparc.file.TreeFolderView", {
118118
if (osparc.file.FilesTree.isDir(selectedModel)) {
119119
folderViewer.setFolder(selectedModel);
120120
}
121+
// this will trigger the fetching of the content
121122
folderTree.openNodeAndParents(selectedModel);
122123
folderTree.setSelection(new qx.data.Array([selectedModel]));
123-
if (selectedModel.getPath() && !selectedModel.getLoaded()) {
124-
selectedModel.setLoaded(true);
125-
folderTree.requestPathItems(selectedModel.getLocation(), selectedModel.getPath());
126-
}
127124
}
128125
}, this);
129126

@@ -139,22 +136,18 @@ qx.Class.define("osparc.file.TreeFolderView", {
139136
}, this);
140137
},
141138

142-
openPath: function(path) {
143-
const foldersTree = this.getChildControl("folder-tree");
144-
const folderViewer = this.getChildControl("folder-viewer");
145-
let found = false;
146-
while (!found && path.length) {
147-
found = foldersTree.findItemId(path.join("/"));
148-
// look for next parent
149-
path.pop();
150-
}
151-
if (found) {
152-
foldersTree.openNodeAndParents(found);
153-
foldersTree.setSelection(new qx.data.Array([found]));
154-
foldersTree.fireEvent("selectionChanged");
155-
} else {
156-
folderViewer.resetFolder();
157-
}
139+
pathsDeleted: function(paths) {
140+
this.getChildControl("folder-viewer").resetSelection();
141+
142+
const folderTree = this.getChildControl("folder-tree");
143+
const selectedFolder = folderTree.getSelectedItem();
144+
const children = selectedFolder.getChildren();
145+
paths.forEach(path => {
146+
const found = children.toArray().find(child => child.getPath() === path);
147+
if (found) {
148+
children.remove(found);
149+
}
150+
});
158151
},
159152

160153
requestSize: function(pathId) {

services/static-webserver/client/source/class/osparc/store/Data.js

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -234,29 +234,20 @@ qx.Class.define("osparc.store.Data", {
234234
return true;
235235
},
236236

237-
// if folder path is provided as fileUuid, it can also be deleted
238-
deleteFile: function(locationId, fileUuid) {
237+
deleteFiles: function(paths) {
239238
if (!osparc.data.Permissions.getInstance().canDo("study.node.data.delete", true)) {
240239
return null;
241240
}
242241

243-
// Deletes File
244242
const params = {
245243
url: {
246-
locationId,
247-
fileUuid: encodeURIComponent(fileUuid)
244+
locationId: 0,
245+
},
246+
data: {
247+
paths,
248248
}
249249
};
250-
return osparc.data.Resources.fetch("storageFiles", "delete", params)
251-
.then(files => {
252-
const data = {
253-
data: files,
254-
locationId: locationId,
255-
fileUuid: fileUuid
256-
};
257-
return data;
258-
})
259-
.catch(err => osparc.FlashMessenger.logError(err, this.tr("Unsuccessful file deletion")));
260-
}
250+
return osparc.data.Resources.fetch("storagePaths", "batchDelete", params);
251+
},
261252
}
262253
});

services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ qx.Class.define("osparc.widget.StudyDataManager", {
9696
treeFolderView.getChildControl("folder-tree").setBackgroundColor("window-popup-background");
9797

9898
const selectedFileLayout = treeFolderView.getChildControl("folder-viewer").getChildControl("selected-file-layout");
99-
selectedFileLayout.addListener("fileDeleted", e => this.__fileDeleted(e.getData()), this);
99+
selectedFileLayout.addListener("pathsDeleted", e => treeFolderView.pathsDeleted(e.getData()), this);
100100
},
101101

102102
__reloadTree: function() {
@@ -115,36 +115,5 @@ qx.Class.define("osparc.widget.StudyDataManager", {
115115
const folderViewer = treeFolderView.getChildControl("folder-viewer");
116116
folderViewer.resetFolder();
117117
},
118-
119-
__fileDeleted: function(fileMetadata) {
120-
// After deleting a file, try to keep the user in the same folder.
121-
// If the folder doesn't longer exist, open the closest available parent
122-
123-
const path = fileMetadata["fileUuid"].split("/");
124-
125-
const treeFolderView = this.getChildControl("tree-folder-view");
126-
const foldersTree = treeFolderView.getChildControl("folder-tree");
127-
foldersTree.resetCache();
128-
129-
const openSameFolder = () => {
130-
if (!this.getStudyId()) {
131-
// drop first, which is the study id
132-
path.shift();
133-
}
134-
// drop last, which is the file
135-
path.pop();
136-
treeFolderView.openPath(path);
137-
};
138-
139-
if (this.getNodeId()) {
140-
foldersTree.populateNodeTree(this.getStudyId(), this.getNodeId())
141-
.then(() => openSameFolder())
142-
.catch(err => console.error(err));
143-
} else if (this.getStudyId()) {
144-
foldersTree.populateStudyTree(this.getStudyId())
145-
.then(() => openSameFolder())
146-
.catch(err => console.error(err));
147-
}
148-
}
149118
}
150119
});

0 commit comments

Comments
 (0)