diff --git a/src/octoprint/filemanager/__init__.py b/src/octoprint/filemanager/__init__.py index fad1fc41..e63731a5 100644 --- a/src/octoprint/filemanager/__init__.py +++ b/src/octoprint/filemanager/__init__.py @@ -344,6 +344,10 @@ class FileManager(object): self._storage(destination).remove_file(path) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) + def move_file(self, destination, oldpath, newpath): + self._storage(destination).move_file(oldpath, newpath) + eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) + def add_folder(self, destination, path, ignore_existing=True): folder_path = self._storage(destination).add_folder(path, ignore_existing=ignore_existing) eventManager().fire(Events.UPDATED_FILES, dict(type="printables")) diff --git a/src/octoprint/filemanager/storage.py b/src/octoprint/filemanager/storage.py index 273107ff..30cba083 100644 --- a/src/octoprint/filemanager/storage.py +++ b/src/octoprint/filemanager/storage.py @@ -10,6 +10,7 @@ import logging import os import pylru import tempfile +import shutil import octoprint.filemanager @@ -112,6 +113,15 @@ class StorageInterface(object): """ raise NotImplementedError() + def move_folder(self, oldpath, newpath): + """ + Moves the folder ``oldpath`` to ``newpath`` + + :param string oldpath: path to the old folder + :param string newpath: path to the new folder + """ + raise NotImplementedError() + def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False): """ Adds the file ``file_object`` as ``path`` @@ -136,6 +146,15 @@ class StorageInterface(object): """ raise NotImplementedError() + def move_file(self, oldpath, newpath): + """ + Moves the file ``oldpath`` to ``newpath`` + + :param string oldpath: path to the old file + :param string newpath: path to the new file + """ + raise NotImplementedError() + def get_metadata(self, path): """ Retrieves the metadata for the file ``path``. @@ -414,6 +433,25 @@ class LocalFileStorage(StorageInterface): import shutil shutil.rmtree(folder_path) + def move_folder(self, oldpath, newpath): + oldpath, oldname = self.sanitize(oldpath) + newpath, newname = self.sanitize(newpath) + + oldfolder_path = os.path.join(oldpath, oldname) + if not os.path.exists(oldfolder_path): + return + if not os.path.isdir(oldfolder_path): + raise RuntimeError("{name} in {path} is not a folder".format(**locals())) + + newfolder_path = os.path.join(newpath, newname) + if os.path.exists(newfolder_path): + return + + try: + shutil.move(oldfolder_path, newfolder_path) + except Exception as e: + raise RuntimeError("Could not move/rename {oldname} in {oldpath} to {newname} in {newpath}".format(**locals()), e) + def add_file(self, path, file_object, printer_profile=None, links=None, allow_overwrite=False): path, name = self.sanitize(path) if not octoprint.filemanager.valid_file_type(name): @@ -488,6 +526,38 @@ class LocalFileStorage(StorageInterface): del metadata[name] self._save_metadata(path, metadata) + def move_file(self, oldpath, newpath): + oldpath, oldname = self.sanitize(oldpath) + newpath, newname = self.sanitize(newpath) + + oldmetadata = self._get_metadata(oldpath) + newmetadata = self._get_metadata(newpath) + + oldfile_path = os.path.join(oldpath, oldname) + if not os.path.exists(oldfile_path): + return + if not os.path.isfile(oldfile_path): + raise RuntimeError("{name} in {path} is not a file".format(**locals())) + + newfile_path = os.path.join(newpath, newname) + if os.path.exists(newfile_path): + return + + try: + shutil.move(oldfile_path, newfile_path) + except Exception as e: + raise RuntimeError("Could not move/rename {oldname} in {oldpath} to {newname} in {newpath}".format(**locals()), e) + + if oldname in oldmetadata: + metadata = oldmetadata[oldname] + del metadata[oldname] + self._save_metadata(oldpath, oldmetadata) + + newmetadata[newname] = metadata + newmetadata[newname]["hash"] = self._create_hash(os.path.join(newpath, newname)) + self._save_metadata(newpath, newmetadata) + + def get_metadata(self, path): path, name = self.sanitize(path) diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 2804f5d5..aacd5bdf 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -325,7 +325,9 @@ def gcodeFileCommand(filename, target): # valid file commands, dict mapping command name to mandatory parameters valid_commands = { "select": [], - "slice": [] + "slice": [], + "move": ["newpath"], + "rename": ["newpath"], } command, data, response = get_json_command_from_request(request, valid_commands) @@ -457,6 +459,8 @@ def gcodeFileCommand(filename, target): r = make_response(jsonify(result), 202) r.headers["Location"] = location return r + elif command == "mode" or command == "rename": + fileManager.move_file(target, filename, data["newpath"]) return NO_CONTENT