Allow removal of folders

This commit is contained in:
Gina Häußge 2016-07-04 18:59:06 +02:00
parent 69b231139f
commit 277860b831
5 changed files with 119 additions and 27 deletions

View file

@ -616,7 +616,7 @@ def deleteGcodeFile(filename, target):
printer.unselect_file()
# delete it
fileManager.remove_folder(target, filename)
fileManager.remove_folder(target, filename, recursive=True)
return NO_CONTENT

File diff suppressed because one or more lines are too long

View file

@ -62,6 +62,7 @@ $(function() {
self.ignoreUpdatedFilesEvent = false;
self.addingFolder = ko.observable(false);
self.activeRemovals = ko.observableArray([]);
self.addFolderDialog = undefined;
self.addFolderName = ko.observable(undefined);
@ -325,6 +326,29 @@ $(function() {
});
};
self.removeFolder = function(folder, event) {
if (!folder) {
return;
}
if (folder.type != "folder") {
return;
}
if (folder.weight > 0) {
// confirm recursive delete
var options = {
message: _.sprintf(gettext("You are about to delete the folder \"%(folder)s\" which still contains files and/or sub folders."), {folder: folder.name}),
onproceed: function() {
self._removeEntry(folder, event);
}
};
showConfirmationDialog(options);
} else {
self._removeEntry(folder, event);
}
};
self.loadFile = function(file, printAfterLoad) {
if (!file) {
return;
@ -338,29 +362,16 @@ $(function() {
});
};
self.removeFile = function(file) {
self.removeFile = function(file, event) {
if (!file) {
return;
}
var index = self.listHelper.paginatedItems().indexOf(file) + 1;
if (index >= self.listHelper.paginatedItems().length) {
index = index - 2;
}
if (index < 0) {
index = 0;
if (file.type == "folder") {
return;
}
var filenameToFocus = undefined;
var fileToFocus = self.listHelper.paginatedItems()[index];
if (fileToFocus) {
filenameToFocus = fileToFocus.name;
}
OctoPrint.files.delete(file.origin, file.path)
.done(function() {
self.requestData(undefined, filenameToFocus, (file.parent ? file.parent.path : ""));
})
self._removeEntry(file, event);
};
self.sliceFile = function(file) {
@ -383,6 +394,70 @@ $(function() {
OctoPrint.printer.refreshSd();
};
self._removeEntry = function(entry, event) {
var index = self.listHelper.paginatedItems().indexOf(entry) + 1;
if (index >= self.listHelper.paginatedItems().length) {
index = index - 2;
}
if (index < 0) {
index = 0;
}
var filenameToFocus = undefined;
var fileToFocus = self.listHelper.paginatedItems()[index];
if (fileToFocus) {
filenameToFocus = fileToFocus.name;
}
self.activeRemovals.push(entry.origin + ":" + entry.path);
var finishActiveRemoval = function() {
self.activeRemovals(_.filter(self.activeRemovals(), function(e) {
return e != entry.origin + ":" + entry.path;
}));
};
var activateSpinner = function(){},
finishSpinner = function(){};
if (event) {
var element = $(event.currentTarget);
if (element.length) {
var icon = $("i.icon-trash", element);
if (icon.length) {
activateSpinner = function() {
icon.removeClass("icon-trash").addClass("icon-spinner icon-spin");
};
finishSpinner = function() {
icon.removeClass("icon-spinner icon-spin").addClass("icon-trash");
};
}
}
}
activateSpinner();
var deferred = $.Deferred();
OctoPrint.files.delete(entry.origin, entry.path)
.done(function() {
self.requestData(undefined, filenameToFocus, (entry.parent ? entry.parent.path : ""))
.done(function() {
deferred.resolve();
})
.fail(function() {
deferred.reject();
});
})
.fail(function() {
deferred.reject();
});
return deferred.promise()
.always(function() {
finishActiveRemoval();
finishSpinner();
});
};
self.downloadLink = function(data) {
if (data["refs"] && data["refs"]["download"]) {
return data["refs"]["download"];
@ -425,7 +500,19 @@ $(function() {
};
self.enableRemove = function(data) {
return self.loginState.isUser() && !_.contains(self.printerState.busyFiles(), data.origin + ":" + data.name);
if (_.contains(self.activeRemovals(), data.origin + ":" + data.path)) {
return false;
}
var busy = false;
if (data.type == "folder") {
busy = _.any(self.printerState.busyFiles(), function(name) {
return _.startsWith(name, data.origin + ":" + data.path + "/");
});
} else {
busy = _.contains(self.printerState.busyFiles(), data.origin + ":" + data.path);
}
return self.loginState.isUser() && !busy;
};
self.enableSelect = function(data, printAfterSelect) {

View file

@ -788,6 +788,10 @@ ul.dropdown-menu li a {
overflow: visible !important;
}
.clickable {
cursor: pointer;
}
.border_box {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;

View file

@ -2,21 +2,21 @@
<input type="text" class="input-block search-query" data-bind="value: searchQuery, valueUpdate: 'input'" placeholder="{{ _('Search...') }}">
</form>
<div class="gcode_files">
<div class="entry back" data-bind="visible: currentPath() != '', click: function() { $root.navigateUp(); }" style="display: none"><i class="icon-arrow-left"></i> {{ _('Back') }}</div>
<div class="entry back clickable" data-bind="visible: currentPath() != '', click: function() { $root.navigateUp(); }" style="display: none"><i class="icon-arrow-left"></i> {{ _('Back') }}</div>
<!-- ko slimScrolledForeach: filesAndFolders -->
<div class="entry" data-bind="attr: { id: $root.getEntryId($data) }, template: { name: $root.templateFor($data), data: $data }, css: $data.type"></div>
<!-- /ko -->
<script type="text/html" id="files_template_machinecode">
<div class="title" data-bind="css: $root.getSuccessClass($data), style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }, text: name"></div>
<div class="title clickable" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, false); } else { return; } }, css: $root.getSuccessClass($data), style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }, text: name"></div>
<div class="uploaded">{{ _('Uploaded') }}: <span data-bind="text: formatTimeAgo(date)"></span></div>
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
<div class="additionalInfo hide" data-bind="html: $root.getAdditionalData($data)"></div>
<div class="btn-group action-buttons">
<div class="btn btn-mini toggleAdditionalData" data-bind="click: function() { if ($root.enableAdditionalData($data)) { $root.toggleAdditionalData($data); } else { return; } }, css: { disabled: !$root.enableAdditionalData($data) }" title="{{ _('Additional data') }}"><i class="icon-chevron-down"></i></div>
<a class="btn btn-mini" data-bind="attr: {href: $root.downloadLink($data)}, css: {disabled: !$root.downloadLink($data)}" title="{{ _('Download') }}"><i class="icon-download-alt"></i></a>
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}" title="{{ _('Remove') }}"><i class="icon-trash"></i></div>
<div class="btn btn-mini" data-bind="click: function(data, event) { if ($root.enableRemove($data)) { $root.removeFile($data, event); } else { return; } }, css: {disabled: !$root.enableRemove($data)}" title="{{ _('Remove') }}"><i class="icon-trash"></i></div>
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, false); } else { return; } }, css: {disabled: !$root.enableSelect($data)}" title="{{ _('Load') }}"><i class="icon-folder-open"></i></div>
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.loadFile($data, true); } else { return; } }, css: {disabled: !$root.enableSelect($data)}" title="{{ _('Load and Print') }}"><i class="icon-print"></i></div>
</div>
@ -28,15 +28,16 @@
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
<div class="btn-group action-buttons">
<a class="btn btn-mini" data-bind="attr: {href: $root.downloadLink($data), css: {disabled: !$root.downloadLink($data)}}" title="{{ _('Download') }}"><i class="icon-download-alt"></i></a>
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}" title="{{ _('Remove') }}"><i class="icon-trash"></i></div>
<div class="btn btn-mini" data-bind="click: function(data, event) { if ($root.enableRemove($data)) { $root.removeFile($data, event); } else { return; } }, css: {disabled: !$root.enableRemove($data)}" title="{{ _('Remove') }}"><i class="icon-trash"></i></div>
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSlicing($data)) { $root.sliceFile($data); } else { return; } }, css: {disabled: !$root.enableSlicing($data)}" title="{{ _('Slice') }}"><i class="icon-magic"></i></div>
</div>
</script>
<script type="text/html" id="files_template_folder">
<div data-bind="click: $root.changeFolder">
<div class="title" data-bind="style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }"><i class="icon-folder-open"></i> <span data-bind="text: name"></span></div>
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
<div class="title clickable" data-bind="click: $root.changeFolder, style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }"><i class="icon-folder-open"></i> <span data-bind="text: name"></span></div>
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
<div class="btn-group action-buttons">
<div class="btn btn-mini" data-bind="click: function(data, event) { if ($root.enableRemove($data)) { $root.removeFolder($data, event); } else { return; } }, css: {disabled: !$root.enableRemove($data)}" title="{{ _('Remove') }}"><i class="icon-trash"></i></div>
</div>
</script>
</div>