Added create and remove folder API commands

This commit is contained in:
Salandora 2015-10-09 02:37:42 +02:00
parent fd0456ed26
commit c3ddbd0b1b
3 changed files with 196 additions and 128 deletions

View file

@ -340,6 +340,9 @@ class FileManager(object):
def get_busy_files(self):
return self._slicing_jobs.keys()
def file_in_path(self, destination, path, file):
return self._storage(destination).file_in_path(path, file)
def file_exists(self, destination, path):
return self._storage(destination).file_exists(path)

View file

@ -34,6 +34,15 @@ class StorageInterface(object):
return
yield
def file_in_path(self, path, filepath):
"""
Returns whether the file indicated by ``file`` is inside ``path`` or not.
:param string path: the path to check
:param string filepath: path to the file
:return: ``True`` if the file is inside the path, ``False`` otherwise
"""
return NotImplementedError()
def file_exists(self, path):
"""
Returns whether the file indicated by ``path`` exists or not.
@ -419,6 +428,9 @@ class LocalFileStorage(StorageInterface):
for sub_entry in self._analysis_backlog_generator(absolute_path):
yield self.join_path(entry, sub_entry[0]), sub_entry[1], sub_entry[2]
def file_in_path(self, path, filepath):
return filepath.startswith(path)
def file_exists(self, path):
path, name = self.sanitize(path)
file_path = os.path.join(path, name)

View file

@ -157,148 +157,178 @@ def _verifyFileExists(origin, filename):
return fileManager.file_exists(origin, filename)
def _verifyFolderExists(origin, foldername):
if origin == FileDestinations.SDCARD:
return False
else:
return fileManager.folder_exists(origin, foldername)
def _verifyFolderNotBusy(target, foldername):
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
return True
@api.route("/files/<string:target>", methods=["POST"])
@restricted_access
def uploadGcodeFile(target):
if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]:
return make_response("Unknown target: %s" % target, 404)
input_name = "file"
input_upload_name = input_name + "." + settings().get(["server", "uploads", "nameSuffix"])
input_upload_path = input_name + "." + settings().get(["server", "uploads", "pathSuffix"])
if input_upload_name in request.values and input_upload_path in request.values:
if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]:
return make_response("Unknown target: %s" % target, 404)
upload = octoprint.filemanager.util.DiskFileWrapper(request.values[input_upload_name], request.values[input_upload_path])
else:
return make_response("No file included", 400)
# Store any additional user data the caller may have passed.
userdata = None
if "userdata" in request.values:
import json
# Store any additional user data the caller may have passed.
userdata = None
if "userdata" in request.values:
import json
try:
userdata = json.loads(request.values["userdata"])
except:
return make_response("userdata contains invalid JSON", 400)
if target == FileDestinations.SDCARD and not settings().getBoolean(["feature", "sdSupport"]):
return make_response("SD card support is disabled", 404)
sd = target == FileDestinations.SDCARD
selectAfterUpload = "select" in request.values.keys() and request.values["select"] in valid_boolean_trues
printAfterSelect = "print" in request.values.keys() and request.values["print"] in valid_boolean_trues
if sd:
# validate that all preconditions for SD upload are met before attempting it
if not (printer.is_operational() and not (printer.is_printing() or printer.is_paused())):
return make_response("Can not upload to SD card, printer is either not operational or already busy", 409)
if not printer.is_sd_ready():
return make_response("Can not upload to SD card, not yet initialized", 409)
# determine current job
currentFilename = None
currentFullPath = None
currentOrigin = None
currentJob = printer.get_current_job()
if currentJob is not None and "file" in currentJob.keys():
currentJobFile = currentJob["file"]
if currentJobFile is not None and "name" in currentJobFile.keys() and "origin" in currentJobFile.keys() and currentJobFile["name"] is not None and currentJobFile["origin"] is not None:
currentPath, currentFilename = fileManager.sanitize(currentJobFile["origin"], currentJobFile["name"])
currentFullPath = fileManager.join_path(target, currentPath, currentFilename)
currentOrigin = currentJobFile["origin"]
# determine future filename of file to be uploaded, abort if it can't be uploaded
try:
userdata = json.loads(request.values["userdata"])
futurePath, futureFilename = fileManager.sanitize(target, upload.filename)
except:
return make_response("userdata contains invalid JSON", 400)
futurePath = None
futureFilename = None
if target == FileDestinations.SDCARD and not settings().getBoolean(["feature", "sdSupport"]):
return make_response("SD card support is disabled", 404)
if futureFilename is None:
return make_response("Can not upload file %s, wrong format?" % upload.filename, 415)
sd = target == FileDestinations.SDCARD
selectAfterUpload = "select" in request.values.keys() and request.values["select"] in valid_boolean_trues
printAfterSelect = "print" in request.values.keys() and request.values["print"] in valid_boolean_trues
if "path" in request.values:
futurePath = fileManager.sanitize_path(target, request.values["path"])
if sd:
# validate that all preconditions for SD upload are met before attempting it
if not (printer.is_operational() and not (printer.is_printing() or printer.is_paused())):
return make_response("Can not upload to SD card, printer is either not operational or already busy", 409)
if not printer.is_sd_ready():
return make_response("Can not upload to SD card, not yet initialized", 409)
futureFullPath = fileManager.join_path(target, futurePath, futureFilename)
# determine current job
currentFilename = None
currentFullPath = None
currentOrigin = None
currentJob = printer.get_current_job()
if currentJob is not None and "file" in currentJob.keys():
currentJobFile = currentJob["file"]
if currentJobFile is not None and "name" in currentJobFile.keys() and "origin" in currentJobFile.keys() and currentJobFile["name"] is not None and currentJobFile["origin"] is not None:
currentPath, currentFilename = fileManager.sanitize(currentJobFile["origin"], currentJobFile["name"])
currentFullPath = fileManager.join_path(target, currentPath, currentFilename)
currentOrigin = currentJobFile["origin"]
# prohibit overwriting currently selected file while it's being printed
if futureFullPath == currentFullPath and target == currentOrigin and printer.is_printing() or printer.is_paused():
return make_response("Trying to overwrite file that is currently being printed: %s" % currentFilename, 409)
# determine future filename of file to be uploaded, abort if it can't be uploaded
try:
futurePath, futureFilename = fileManager.sanitize(target, upload.filename)
except:
futurePath = None
futureFilename = None
def fileProcessingFinished(filename, absFilename, destination):
"""
Callback for when the file processing (upload, optional slicing, addition to analysis queue) has
finished.
if futureFilename is None:
return make_response("Can not upload file %s, wrong format?" % upload.filename, 415)
Depending on the file's destination triggers either streaming to SD card or directly calls selectAndOrPrint.
"""
if "path" in request.values:
futurePath = fileManager.sanitize_path(target, request.values["path"])
if destination == FileDestinations.SDCARD and octoprint.filemanager.valid_file_type(filename, "gcode"):
return filename, printer.add_sd_file(filename, absFilename, selectAndOrPrint)
else:
selectAndOrPrint(filename, absFilename, destination)
return filename
futureFullPath = fileManager.join_path(target, futurePath, futureFilename)
def selectAndOrPrint(filename, absFilename, destination):
"""
Callback for when the file is ready to be selected and optionally printed. For SD file uploads this is only
the case after they have finished streaming to the printer, which is why this callback is also used
for the corresponding call to addSdFile.
# prohibit overwriting currently selected file while it's being printed
if futureFullPath == currentFullPath and target == currentOrigin and printer.is_printing() or printer.is_paused():
return make_response("Trying to overwrite file that is currently being printed: %s" % currentFilename, 409)
Selects the just uploaded file if either selectAfterUpload or printAfterSelect are True, or if the
exact file is already selected, such reloading it.
"""
if octoprint.filemanager.valid_file_type(added_file, "gcode") and (selectAfterUpload or printAfterSelect or (currentFilename == filename and currentOrigin == destination)):
printer.select_file(absFilename, destination == FileDestinations.SDCARD, printAfterSelect)
def fileProcessingFinished(filename, absFilename, destination):
"""
Callback for when the file processing (upload, optional slicing, addition to analysis queue) has
finished.
Depending on the file's destination triggers either streaming to SD card or directly calls selectAndOrPrint.
"""
if destination == FileDestinations.SDCARD and octoprint.filemanager.valid_file_type(filename, "gcode"):
return filename, printer.add_sd_file(filename, absFilename, selectAndOrPrint)
added_file = fileManager.add_file(FileDestinations.LOCAL, futureFullPath, upload, allow_overwrite=True)
if added_file is None:
return make_response("Could not upload the file %s" % upload.filename, 500)
if octoprint.filemanager.valid_file_type(added_file, "stl"):
filename = added_file
done = True
else:
selectAndOrPrint(filename, absFilename, destination)
return filename
filename = fileProcessingFinished(added_file, fileManager.path_on_disk(FileDestinations.LOCAL, added_file), target)
done = True
def selectAndOrPrint(filename, absFilename, destination):
"""
Callback for when the file is ready to be selected and optionally printed. For SD file uploads this is only
the case after they have finished streaming to the printer, which is why this callback is also used
for the corresponding call to addSdFile.
if userdata is not None:
# upload included userdata, add this now to the metadata
fileManager.set_additional_metadata(FileDestinations.LOCAL, added_file, "userdata", userdata)
Selects the just uploaded file if either selectAfterUpload or printAfterSelect are True, or if the
exact file is already selected, such reloading it.
"""
if octoprint.filemanager.valid_file_type(added_file, "gcode") and (selectAfterUpload or printAfterSelect or (currentFilename == filename and currentOrigin == destination)):
printer.select_file(absFilename, destination == FileDestinations.SDCARD, printAfterSelect)
sdFilename = None
if isinstance(filename, tuple):
filename, sdFilename = filename
added_file = fileManager.add_file(FileDestinations.LOCAL, futureFullPath, upload, allow_overwrite=True)
if added_file is None:
return make_response("Could not upload the file %s" % upload.filename, 500)
if octoprint.filemanager.valid_file_type(added_file, "stl"):
filename = added_file
done = True
else:
filename = fileProcessingFinished(added_file, fileManager.path_on_disk(FileDestinations.LOCAL, added_file), target)
done = True
eventManager.fire(Events.UPLOAD, {"file": filename, "target": target})
if userdata is not None:
# upload included userdata, add this now to the metadata
fileManager.set_additional_metadata(FileDestinations.LOCAL, added_file, "userdata", userdata)
sdFilename = None
if isinstance(filename, tuple):
filename, sdFilename = filename
eventManager.fire(Events.UPLOAD, {"file": filename, "target": target})
files = {}
location = url_for(".readGcodeFile", target=FileDestinations.LOCAL, filename=filename, _external=True)
files.update({
FileDestinations.LOCAL: {
"name": filename,
"origin": FileDestinations.LOCAL,
"refs": {
"resource": location,
"download": url_for("index", _external=True) + "downloads/files/" + FileDestinations.LOCAL + "/" + filename
}
}
})
if sd and sdFilename:
location = url_for(".readGcodeFile", target=FileDestinations.SDCARD, filename=sdFilename, _external=True)
files = {}
location = url_for(".readGcodeFile", target=FileDestinations.LOCAL, filename=filename, _external=True)
files.update({
FileDestinations.SDCARD: {
"name": sdFilename,
"origin": FileDestinations.SDCARD,
FileDestinations.LOCAL: {
"name": filename,
"origin": FileDestinations.LOCAL,
"refs": {
"resource": location
"resource": location,
"download": url_for("index", _external=True) + "downloads/files/" + FileDestinations.LOCAL + "/" + filename
}
}
})
r = make_response(jsonify(files=files, done=done), 201)
r.headers["Location"] = location
return r
if sd and sdFilename:
location = url_for(".readGcodeFile", target=FileDestinations.SDCARD, filename=sdFilename, _external=True)
files.update({
FileDestinations.SDCARD: {
"name": sdFilename,
"origin": FileDestinations.SDCARD,
"refs": {
"resource": location
}
}
})
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)
if not target in [FileDestinations.LOCAL]:
return make_response("Unknown target: %s" % target, 404)
futurePath, futureName = fileManager.sanitize(target, request.json["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)
added_folder = fileManager.add_folder(target, futureFullPath)
if added_folder is None:
return make_response("Could not create folder %s" % futureName, 500)
return NO_CONTENT
@api.route("/files/<string:target>/<path:filename>", methods=["GET"])
@ -464,29 +494,52 @@ def gcodeFileCommand(filename, target):
@api.route("/files/<string:target>/<path:filename>", methods=["DELETE"])
@restricted_access
def deleteGcodeFile(filename, target):
if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]:
return make_response("Unknown target: %s" % target, 404)
if not _verifyFileExists(target, filename) and not _verifyFolderExists(target, filename):
return make_response("File/Folder not found on '%s': %s" % (target, filename), 404)
if not _verifyFileExists(target, filename):
return make_response("File not found on '%s': %s" % (target, filename), 404)
if _verifyFileExists(target, filename):
if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]:
return make_response("Unknown target: %s" % target, 404)
# prohibit deleting files that are currently in use
currentOrigin, currentFilename = _getCurrentFile()
if currentFilename == filename and currentOrigin == target and (printer.is_printing() or printer.is_paused()):
make_response("Trying to delete file that is currently being printed: %s" % filename, 409)
# prohibit deleting files that are currently in use
currentOrigin, currentFilename = _getCurrentFile()
if (target, filename) in fileManager.get_busy_files():
make_response("Trying to delete a file that is currently in use: %s" % filename, 409)
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)
# deselect the file if it's currently selected
if currentFilename is not None and filename == currentFilename:
printer.unselect_file()
if not _verifyFolderNotBusy(target, filename):
return make_response("Trying to delete a file that is currently in use: %s" % filename, 409)
# delete it
if target == FileDestinations.SDCARD:
printer.delete_sd_file(filename)
else:
fileManager.remove_file(target, filename)
# deselect the file if it's currently selected
if currentFilename is not None and filename == currentFilename:
printer.unselect_file()
# delete it
if target == FileDestinations.SDCARD:
printer.delete_sd_file(filename)
else:
fileManager.remove_file(target, filename)
elif _verifyFolderExists(target, filename):
if not target in [FileDestinations.LOCAL]:
return make_response("Unknown target: %s" % target, 404)
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):
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
if currentFilename is not None and fileManager.file_in_path(target, folderpath, currentFilename):
printer.unselect_file()
# delete it
fileManager.remove_folder(target, folderpath)
return NO_CONTENT