From 3458f9af433ec27a0634bc17efba9b4edbd448c5 Mon Sep 17 00:00:00 2001 From: Salandora Date: Fri, 9 Oct 2015 19:51:36 +0200 Subject: [PATCH] Refactorings based on Commit comments --- src/octoprint/filemanager/__init__.py | 12 +- src/octoprint/filemanager/storage.py | 190 ++++++++--------- src/octoprint/server/api/files.py | 193 +++++++++--------- src/octoprint/static/js/app/client/files.js | 23 ++- .../static/js/app/viewmodels/files.js | 16 +- src/octoprint/templates/sidebar/files.jinja2 | 38 ++-- 6 files changed, 242 insertions(+), 230 deletions(-) diff --git a/src/octoprint/filemanager/__init__.py b/src/octoprint/filemanager/__init__.py index a3f0a73a..795cb2c0 100644 --- a/src/octoprint/filemanager/__init__.py +++ b/src/octoprint/filemanager/__init__.py @@ -391,12 +391,12 @@ class FileManager(object): self._storage(destination).remove_file(path) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) - def copy_file(self, destination, source, dst, allow_overwrite=False): - self._storage(destination).copy_file(source, dst, allow_overwrite) + def copy_file(self, destination, source, dst): + self._storage(destination).copy_file(source, dst) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) - def move_file(self, destination, source, dst, allow_overwrite=False): - self._storage(destination).move_file(source, dst, allow_overwrite) + def move_file(self, destination, source, dst): + self._storage(destination).move_file(source, dst) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) def add_folder(self, destination, path, ignore_existing=True): @@ -412,8 +412,8 @@ class FileManager(object): self._storage(destination).copy_folder(source, dst) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) - def move_folder(self, destination, source, dst, allow_overwrite=False): - self._storage(destination).move_folder(source, dst, allow_overwrite) + def move_folder(self, destination, source, dst): + self._storage(destination).move_folder(source, dst) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) def get_metadata(self, destination, path): diff --git a/src/octoprint/filemanager/storage.py b/src/octoprint/filemanager/storage.py index 28c0fccb..47ba0941 100644 --- a/src/octoprint/filemanager/storage.py +++ b/src/octoprint/filemanager/storage.py @@ -139,7 +139,7 @@ class StorageInterface(object): """ raise NotImplementedError() - def move_folder(self, source, destination, allow_overwrite=False): + def move_folder(self, source, destination): """ Moves the folder ``source`` to ``destination`` @@ -172,7 +172,7 @@ class StorageInterface(object): """ raise NotImplementedError() - def copy_file(self, source, destination, allow_overwrite=False): + def copy_file(self, source, destination): """ Copys the file ``source`` to ``destination`` @@ -181,7 +181,7 @@ class StorageInterface(object): """ raise NotImplementedError() - def move_file(self, source, destination, allow_overwrite=False): + def move_file(self, source, destination): """ Moves the file ``source`` to ``destination`` @@ -429,6 +429,9 @@ class LocalFileStorage(StorageInterface): yield self.join_path(entry, sub_entry[0]), sub_entry[1], sub_entry[2] def file_in_path(self, path, filepath): + filepath = self.sanitize_path(filepath) + path = self.sanitize_path(path) + return filepath.startswith(path) def file_exists(self, path): @@ -476,45 +479,69 @@ class LocalFileStorage(StorageInterface): import shutil shutil.rmtree(folder_path) + def _copyMove1(self, source, destination): + sourcepath, sourcename = self.sanitize(source) + destinationpath, destinationname = self.sanitize(destination) + + source_path = os.path.join(sourcepath, sourcename) + if not os.path.exists(source_path): + raise RuntimeError("{sourcename} in {sourcepath} does not exist".format(**locals())) + + destination_path = os.path.join(destinationpath, destinationname) + if os.path.exists(destination_path): + raise RuntimeError("{destinationname} does already exist in {destinationpath}".format(**locals())) + + sourceObj = dict( + path=sourcepath, + name=sourcename, + fullpath=source_path, + ) + destinationObj = dict( + path=destinationpath, + name=destinationname, + fullpath=destination_path, + ) + return sourceObj, destinationObj + + def _copyMove2(self, source, destination): + sourceObj, destinationObj = self._copyMove1(source, destination) + + if not os.path.isfile(sourceObj["fullpath"]): + raise RuntimeError("%s in %s is not a file" % (sourceObj["name"], sourceObj["path"])) + + sourcemetadata = self._get_metadata(sourceObj["path"]) + if not sourcemetadata: + sourcemetadata = dict() + + destinationmetadata = self._get_metadata(destinationObj["path"]) + if not destinationmetadata: + destinationmetadata = dict() + + sourceObj.update(dict(metadata=sourcemetadata)) + destinationObj.update(dict(metadata=destinationmetadata)) + return sourceObj, destinationObj + def copy_folder(self, source, destination): - sourcepath, sourcename = self.sanitize(source) - destinationpath, destinationname = self.sanitize(destination) + sourceObj, destinationObj = self._copyMove1(source, destination) - sourcefolder_path = os.path.join(sourcepath, sourcename) - if not os.path.exists(sourcefolder_path): - return - - if not os.path.isdir(sourcefolder_path): - raise RuntimeError("{name} in {path} is not a folder".format(**locals())) - - destinationfolder_path = os.path.join(destinationpath, destinationname) - if os.path.exists(destinationfolder_path): - raise RuntimeError("{newname} does already exist in {newpath}".format(**locals())) + if not os.path.isdir(sourceObj["fullpath"]): + raise RuntimeError("%s in %s is not a folder" % (sourceObj["name"], sourceObj["path"])) try: - shutil.copytree(sourcefolder_path, destinationfolder_path) + shutil.copytree(sourceObj["fullpath"], destinationObj["fullpath"]) except Exception as e: - raise RuntimeError("Could not copy {oldname} in {oldpath} to {newname} in {newpath}".format(**locals()), e) + raise RuntimeError("Could not copy %s in %s to %s in %s" % (sourceObj["name"], sourceObj["path"], destinationObj["name"], destinationObj["path"]), e) - def move_folder(self, source, destination, allow_overwrite=False): - sourcepath, sourcename = self.sanitize(source) - destinationpath, destinationname = self.sanitize(destination) + def move_folder(self, source, destination): + sourceObj, destinationObj = self._copyMove1(source, destination) - sourcefolder_path = os.path.join(sourcepath, sourcename) - if not os.path.exists(sourcefolder_path): - return - - if not os.path.isdir(sourcefolder_path): - raise RuntimeError("{name} in {path} is not a folder".format(**locals())) - - destinationfolder_path = os.path.join(destinationpath, destinationname) - if os.path.exists(destinationfolder_path) and not allow_overwrite: - raise RuntimeError("{newname} does already exist in {newpath} and overwriting is prohibited".format(**locals())) + if not os.path.isdir(sourceObj["fullpath"]): + raise RuntimeError("%s in %s is not a folder" % (sourceObj["name"], sourceObj["path"])) try: - shutil.move(sourcefolder_path, destinationfolder_path) + shutil.move(sourceObj["fullpath"], destinationObj["fullpath"]) except Exception as e: - raise RuntimeError("Could not move {oldname} in {oldpath} to {newname} in {newpath}".format(**locals()), e) + raise RuntimeError("Could not move %s in %s to %s in %s" % (sourceObj["name"], sourceObj["path"], destinationObj["name"], destinationObj["path"]), e) def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False): path, name = self.sanitize(path) @@ -585,83 +612,41 @@ class LocalFileStorage(StorageInterface): if not "links" in m: continue for link in m["links"]: - if "rel" in link and "hash" in link and (link["rel"] == "model" or link["rel"] == "machinecode") and link["hash"] == hash: + if "rel" in link and "hash" in link and ( + link["rel"] == "model" or link["rel"] == "machinecode") and link["hash"] == hash: m["links"].remove(link) del metadata[name] self._save_metadata(path, metadata) - def copy_file(self, source, destination, allow_overwrite=False): - sourcepath, sourcename = self.sanitize(source) - destinationpath, destinationname = self.sanitize(destination) - - oldmetadata = self._get_metadata(sourcepath) - if not oldmetadata: - oldmetadata = dict() - - newmetadata = self._get_metadata(destinationpath) - if not newmetadata: - newmetadata = dict() - - sourcefile_path = os.path.join(sourcepath, sourcename) - if not os.path.exists(sourcefile_path): - return - - if not os.path.isfile(sourcefile_path): - raise RuntimeError("{oldname} in {oldpath} is not a file".format(**locals())) - - destinationfile_path = os.path.join(destinationpath, destinationname) - if os.path.exists(destinationfile_path) and not allow_overwrite: - raise RuntimeError("{newname} does already exist in {newpath} and overwriting is prohibited".format(**locals())) + def copy_file(self, source, destination): + sourceObj, destinationObj = self._copyMove2(source, destination) try: - shutil.copy2(sourcefile_path, destinationfile_path) + shutil.copy2(sourceObj["fullpath"], destinationObj["fullpath"]) except Exception as e: - raise RuntimeError("Could not copy {oldname} in {oldpath} to {newname} in {newpath}".format(**locals()), e) + raise RuntimeError("Could not copy %s in %s to %s in %s" % (sourceObj["name"], sourceObj["path"], destinationObj["name"], destinationObj["path"]), e) - if sourcename in oldmetadata: - metadata = oldmetadata[sourcename] - - newmetadata[destinationname] = metadata - newmetadata[destinationname]["hash"] = self._create_hash(destinationfile_path) - self._save_metadata(destinationpath, newmetadata) + if sourceObj["name"] in sourceObj["metadata"]: + destinationObj["metadata"][destinationObj["name"]] = sourceObj["metadata"][sourceObj["name"]] + destinationObj["metadata"][destinationObj["name"]]["hash"] = self._create_hash(destinationObj["fullpath"]) + self._save_metadata(destinationObj["path"], destinationObj["metadata"]) def move_file(self, source, destination, allow_overwrite=False): - sourcepath, sourcename = self.sanitize(source) - destinationpath, destinationname = self.sanitize(destination) - - oldmetadata = self._get_metadata(sourcepath) - if not oldmetadata: - oldmetadata = dict() - - newmetadata = self._get_metadata(destinationpath) - if not newmetadata: - newmetadata = dict() - - sourcefile_path = os.path.join(sourcepath, sourcename) - if not os.path.exists(sourcefile_path): - return - - if not os.path.isfile(sourcefile_path): - raise RuntimeError("{oldname} in {oldpath} is not a file".format(**locals())) - - destinationfile_path = os.path.join(destinationpath, destinationname) - if os.path.exists(destinationfile_path) and not allow_overwrite: - raise RuntimeError("{newname} does already exist in {newpath} and overwriting is prohibited".format(**locals())) + sourceObj, destinationObj = self._copyMove2(source, destination) try: - shutil.move(sourcefile_path, destinationfile_path) + shutil.move(sourceObj["fullpath"], destinationObj["fullpath"]) except Exception as e: - raise RuntimeError("Could not move {oldname} in {oldpath} to {newname} in {newpath}".format(**locals()), e) + raise RuntimeError("Could not move %s in %s to %s in %s" % (sourceObj["name"], sourceObj["path"], destinationObj["name"], destinationObj["path"]), e) - if sourcename in oldmetadata: - metadata = oldmetadata[sourcename] - del oldmetadata[sourcename] - self._save_metadata(sourcepath, oldmetadata) - - newmetadata[destinationname] = metadata - newmetadata[destinationname]["hash"] = self._create_hash(destinationfile_path) - self._save_metadata(destinationpath, newmetadata) + if sourceObj["name"] in sourceObj["metadata"]: + metadata = sourceObj["metadata"][sourceObj["name"]] + del sourceObj["metadata"][sourceObj["name"]] + self._save_metadata(sourceObj["path"], sourceObj["metadata"]) + destinationObj["metadata"][destinationObj["name"]] = metadata + destinationObj["metadata"][destinationObj["name"]]["hash"] = self._create_hash(destinationObj["fullpath"]) + self._save_metadata(destinationObj["path"], destinationObj["metadata"]) def get_metadata(self, path): path, name = self.sanitize(path) @@ -1160,25 +1145,18 @@ class LocalFileStorage(StorageInterface): ) if not filter or filter(entry, entry_data): - def get_size(start_path): + def get_size(): total_size = 0 - for root, dirs, files in os.walk(start_path): - for f in files: - if f is ".metadata.yaml": - continue - - fp = os.path.join(root, f) - total_size += os.path.getsize(fp) + for element in entry_data["children"].itervalues(): + if "size" in element: + total_size += element["size"] return total_size # only add folders passing the optional filter extended_entry_data = dict() extended_entry_data.update(entry_data) - extended_entry_data["size"] = get_size(entry_path) - stat = os.stat(entry_path) - if stat: - extended_entry_data["date"] = int(stat.st_ctime) + extended_entry_data["size"] = get_size() result[entry] = extended_entry_data diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index cc00e438..d98c24de 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -25,13 +25,8 @@ import psutil @api.route("/files", methods=["GET"]) def readGcodeFiles(): - filter = None - recursive = False - if "filter" in request.values: - filter = request.values["filter"] - - if "recursive" in request.values: - recursive = request.values["recursive"] == 'true' + filter = "filter" in request.values and request.values["recursive"] in valid_boolean_trues + recursive = "recursive" in request.values and request.values["recursive"] in valid_boolean_trues files = _getFileList(FileDestinations.LOCAL, filter=filter, recursive=recursive) files.extend(_getFileList(FileDestinations.SDCARD)) @@ -62,17 +57,40 @@ def _getFileDetails(origin, path): files = _getFileList(origin, recursive=True) path = path.split('/') - def recursive_get_filedetails(files, path): - for file in files: - if file["name"] == path[0]: - if len(path) > 1: - return recursive_get_filedetails(file["children"], path[1:]) - else: - return file + if len(path) == 1: + # shortcut for files in the root folder + name = path[0] + for f in files: + if f["name"] == name: + return f return None - return recursive_get_filedetails(files, path) + node = files + while path: + segment = path.pop(0) + for f in node: + if not f["name"] == segment: + # wrong name => next! + continue + + if not path: + # no path left and name matches => found it! + return f + + if not f["type"] == "folder": + # path left but not a folder => that doesn't work + return None + + # we'll use this folder's children as the next iteration + node = f["children"] + break + else: + # nothing matched the name, we can't find it + return None + + # nothing returned until now => not found + return None def _getFileList(origin, filter=None, recursive=False): @@ -100,52 +118,55 @@ def _getFileList(origin, filter=None, recursive=False): files = fileManager.list_files(origin, filter=filter_func, recursive=recursive)[origin].values() - def recursive_analysis(files, path): + def analyse_recursively(files, path=None): + if path is None: + path = "" + for file in files: file["origin"] = FileDestinations.LOCAL if file["type"] == "folder": - file["children"] = recursive_analysis(file["children"].values(), path + file["name"] + "/") + file["children"] = analyse_recursively(file["children"].values(), path + file["name"] + "/") + else: + if "analysis" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): + file["gcodeAnalysis"] = file["analysis"] + del file["analysis"] - if "analysis" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): - file["gcodeAnalysis"] = file["analysis"] - del file["analysis"] - - if "history" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): - # convert print log - history = file["history"] - del file["history"] - success = 0 - failure = 0 - last = None - for entry in history: - success += 1 if "success" in entry and entry["success"] else 0 - failure += 1 if "success" in entry and not entry["success"] else 0 - if not last or ("timestamp" in entry and "timestamp" in last and entry["timestamp"] > last["timestamp"]): - last = entry - if last: - prints = dict( - success=success, - failure=failure, - last=dict( - success=last["success"], - date=last["timestamp"] + if "history" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): + # convert print log + history = file["history"] + del file["history"] + success = 0 + failure = 0 + last = None + for entry in history: + success += 1 if "success" in entry and entry["success"] else 0 + failure += 1 if "success" in entry and not entry["success"] else 0 + if not last or ("timestamp" in entry and "timestamp" in last and entry["timestamp"] > last["timestamp"]): + last = entry + if last: + prints = dict( + success=success, + failure=failure, + last=dict( + success=last["success"], + date=last["timestamp"] + ) ) - ) - if "printTime" in last: - prints["last"]["printTime"] = last["printTime"] - file["prints"] = prints + if "printTime" in last: + prints["last"]["printTime"] = last["printTime"] + file["prints"] = prints - file.update({ - "refs": { - "resource": url_for(".readGcodeFile", target=FileDestinations.LOCAL, filename=path + file["name"], _external=True), - "download": url_for("index", _external=True) + "downloads/files/" + FileDestinations.LOCAL + "/" + path + file["name"] - } - }) + file.update({ + "refs": { + "resource": url_for(".readGcodeFile", target=FileDestinations.LOCAL, filename=path + file["name"], _external=True), + "download": url_for("index", _external=True) + "downloads/files/" + FileDestinations.LOCAL + "/" + path + file["name"] + } + }) return files - files = recursive_analysis(files, "") + analyse_recursively(files) return files @@ -164,13 +185,17 @@ def _verifyFolderExists(origin, foldername): return fileManager.folder_exists(origin, foldername) -def _verifyFolderNotBusy(target, foldername): +def _isBusy(target, path): + currentOrigin, currentFilename = _getCurrentFile() + if currentFilename is not None and fileManager.file_in_path(target, path, currentFilename) and currentOrigin == target and (printer.is_printing() or printer.is_paused()): + return True + busy_files = fileManager.get_busy_files() for item in busy_files: - if target == item[0] and fileManager.file_in_path(target, foldername, item[1]): - return False + if target == item[0] and fileManager.file_in_path(target, path, item[1]): + return True - return True + return False @api.route("/files/", methods=["POST"]) @restricted_access @@ -312,14 +337,13 @@ def uploadGcodeFile(target): r = make_response(jsonify(files=files, done=done), 201) r.headers["Location"] = location return r - else: - if "foldername" not in request.json: - return make_response("No path information or no file included", 409) + elif "foldername" in request.values: + foldername = request.values["foldername"] if not target in [FileDestinations.LOCAL]: - return make_response("Unknown target: %s" % target, 404) + return make_response("Unknown target: %s" % target, 400) - futurePath, futureName = fileManager.sanitize(target, request.json["foldername"]) + futurePath, futureName = fileManager.sanitize(target, foldername) futureFullPath = fileManager.join_path(target, futurePath, futureName) if octoprint.filemanager.valid_file_type(futureName): return make_response("Can't create a folder named %s, please try another name" % futureName, 409) @@ -327,6 +351,8 @@ def uploadGcodeFile(target): added_folder = fileManager.add_folder(target, futureFullPath) if added_folder is None: return make_response("Could not create folder %s" % futureName, 500) + else: + return make_response("No file to upload and no folder to create", 400) return NO_CONTENT @@ -496,44 +522,37 @@ def gcodeFileCommand(filename, target): elif command == "copy" or command == "move": # Copy and move are only possible on local storage if not target in [FileDestinations.LOCAL]: - return make_response("Unknown target: %s" % target, 404) + return make_response("Unsupported target for {}: {}".format(command, target), 400) if not _verifyFileExists(target, filename) and not _verifyFolderExists(target, filename): return make_response("File/Folder not found on '%s': %s" % (target, filename), 404) - overwrite = data["overwrite"] if "overwrite" in data else False destination = data["destination"] - if _verifyFolderExists(target, destination): path, name = fileManager.split_path(target, filename) destination = fileManager.join_path(target, destination, name) - if fileManager.file_exists(target, destination) and not overwrite: - return make_response("File already exists and overwrite is prohibited: %s" % filename, 409) + if _verifyFileExists(target, destination) or _verifyFolderExists(target, destination): + return make_response("File/Folder already exists: %s" % filename, 409) if command == "copy": if fileManager.file_exists(target, filename): - fileManager.copy_file(target, filename, destination, overwrite) + fileManager.copy_file(target, filename, destination) elif fileManager.folder_exists(target, filename): fileManager.copy_folder(target, filename, destination) elif command == "move": - # prohibit deleting or moving files that are currently in use - currentOrigin, currentFilename = _getCurrentFile() - - if currentFilename is not None and fileManager.file_in_path(target, filename, currentFilename) and currentOrigin == target and (printer.is_printing() or printer.is_paused()): - return make_response("Trying to delete a folder that contains a file that is currently being printed: %s" % filename, 409) - - if not _verifyFolderNotBusy(target, filename): - return make_response("Trying to delete a folder that contains a file that is currently in use: %s" % filename, 409) + if _isBusy(target, filename): + return make_response("Trying to move a file/folder that is currently in use: %s" % filename, 409) # deselect the file if it's currently selected + currentOrigin, currentFilename = _getCurrentFile() if currentFilename is not None and filename == currentFilename: printer.unselect_file() if fileManager.file_exists(target, filename): - fileManager.move_file(target, filename, destination, overwrite) + fileManager.move_file(target, filename, destination) elif fileManager.folder_exists(target, filename): - fileManager.move_folder(target, filename, destination, overwrite) + fileManager.move_folder(target, filename, destination) return NO_CONTENT @@ -546,18 +565,13 @@ def deleteGcodeFile(filename, target): if _verifyFileExists(target, filename): if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]: - return make_response("Unknown target: %s" % target, 404) + return make_response("Unknown target: %s" % target, 400) - # prohibit deleting files that are currently in use - currentOrigin, currentFilename = _getCurrentFile() - - if currentFilename is not None and currentFilename == filename and currentOrigin == target and (printer.is_printing() or printer.is_paused()): - return make_response("Trying to delete file that is currently being printed: %s" % filename, 409) - - if not _verifyFolderNotBusy(target, filename): + if _isBusy(target, filename): return make_response("Trying to delete a file that is currently in use: %s" % filename, 409) # deselect the file if it's currently selected + currentOrigin, currentFilename = _getCurrentFile() if currentFilename is not None and filename == currentFilename: printer.unselect_file() @@ -569,19 +583,14 @@ def deleteGcodeFile(filename, target): elif _verifyFolderExists(target, filename): if not target in [FileDestinations.LOCAL]: - return make_response("Unknown target: %s" % target, 404) + return make_response("Unknown target: %s" % target, 400) folderpath = filename - # prohibit deleting folders that are currently in use - currentOrigin, currentFilename = _getCurrentFile() - - if currentFilename is not None and fileManager.file_in_path(target, folderpath, currentFilename) and currentOrigin == target and (printer.is_printing() or printer.is_paused()): - return make_response("Trying to delete a folder that contains a file that is currently being printed: %s" % folderpath, 409) - - if not _verifyFolderNotBusy(target, folderpath): + if _isBusy(target, folderpath): return make_response("Trying to delete a folder that contains a file that is currently in use: %s" % folderpath, 409) # deselect the file if it's currently selected + currentOrigin, currentFilename = _getCurrentFile() if currentFilename is not None and fileManager.file_in_path(target, folderpath, currentFilename): printer.unselect_file() diff --git a/src/octoprint/static/js/app/client/files.js b/src/octoprint/static/js/app/client/files.js index d1b5b046..50d896de 100644 --- a/src/octoprint/static/js/app/client/files.js +++ b/src/octoprint/static/js/app/client/files.js @@ -47,11 +47,13 @@ get: getFile, list: function (opts) { - return OctoPrint.get(url, opts).done(preProcessList); + return OctoPrint.get(url, opts) + .done(preProcessList); }, listForLocation: function (location, opts) { - return OctoPrint.get(resourceForLocation(location), opts).done(preProcessList); + return OctoPrint.get(resourceForLocation(location), opts) + .done(preProcessList); }, select: function (location, filename, print, opts) { @@ -73,6 +75,23 @@ return OctoPrint.delete(resourceForFile(location, filename), opts); }, + copy: function(location, filename, destination, opts) { + return issueFileCommand(location, filename, "copy", { destination: destination }, opts); + }, + + move: function(location, filename, destination, opts) { + return issueFileCommand(location, filename, "move", { destination: destination }, opts); + }, + + createFolder: function (location, name, path) { + var data = "foldername=" + name; + if (path != undefined && path != "") { + data = "foldername:" + path + "/" + name; + } + + return OctoPrint.post(resourceForLocation(location), data); + }, + upload: function (location, file, data) { data = data || {}; diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 481d7c29..d94f13d5 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -99,10 +99,7 @@ $(function() { return data["type"] && (data["type"] == "model" || data["type"] == "folder"); }, "emptyFolder": function(data) { - if (data["type"] && data["type"] != "folder") - return true; - - return data.children.length != 0; + return data["type"] && (data["type"] != "folder" || data["children"].length != 0); } }, "name", @@ -115,6 +112,7 @@ $(function() { var filter = function(data) { return data["type"] && data["type"] == "folder"; }; return _.filter(self.listHelper.paginatedItems(), filter); }); + self.filesOnlyList = ko.dependentObservable(function() { var filter = function(data) { return data["type"] && data["type"] != "folder"; }; return _.filter(self.listHelper.paginatedItems(), filter); @@ -215,6 +213,7 @@ $(function() { self.currentPath(OctoPrint.files.pathForElement(data)); self.listHelper.updateItems(data.children); }; + self.changeFolderByPath = function(path) { var element = OctoPrint.files.elementByPath(path, { children: self.allItems() }); if (element) { @@ -245,15 +244,18 @@ $(function() { } var index = self.listHelper.paginatedItems().indexOf(file) + 1; - if (index >= self.listHelper.paginatedItems().length) + if (index >= self.listHelper.paginatedItems().length) { index = index - 2; - if (index < 0) + } + if (index < 0) { index = 0; + } var filenameToFocus = undefined; var fileToFocus = self.listHelper.paginatedItems()[index]; - if (fileToFocus) + if (fileToFocus) { filenameToFocus = fileToFocus.name; + } OctoPrint.files.delete(file.origin, OctoPrint.files.pathForElement(file)) .done(function() { diff --git a/src/octoprint/templates/sidebar/files.jinja2 b/src/octoprint/templates/sidebar/files.jinja2 index 1d16d1c5..180cea90 100644 --- a/src/octoprint/templates/sidebar/files.jinja2 +++ b/src/octoprint/templates/sidebar/files.jinja2 @@ -7,25 +7,27 @@ - -
- - -
- + +
+ + +
+ + - -
- - -
- + +
+ + +
+ + - -
- + +
+