From 5fd0dc420b076520fc8e42119748c792ccf8725f Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sat, 4 Mar 2023 16:57:32 -0800 Subject: [PATCH] WebUI: Traverse target dir to get subdirs to download/upload to (#1115) --- python/common/src/piscsi/file_cmds.py | 24 ++++++++++++++-- python/web/src/templates/index.html | 12 ++++++++ python/web/src/templates/upload.html | 13 +++++++-- python/web/src/web.py | 41 +++++++++++++++++++-------- python/web/tests/api/test_files.py | 12 +++++--- 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/python/common/src/piscsi/file_cmds.py b/python/common/src/piscsi/file_cmds.py index 19959608..5987f1c7 100644 --- a/python/common/src/piscsi/file_cmds.py +++ b/python/common/src/piscsi/file_cmds.py @@ -4,7 +4,7 @@ Module for methods reading from and writing to the file system import logging import asyncio -from os import walk +from os import walk, path from functools import lru_cache from pathlib import PurePath, Path from zipfile import ZipFile, is_zipfile @@ -57,7 +57,7 @@ class FileCmds: # noinspection PyMethodMayBeStatic def list_config_files(self): """ - Finds fils with file ending CONFIG_FILE_SUFFIX in CFG_DIR. + Finds files with file ending CONFIG_FILE_SUFFIX in CFG_DIR. Returns a (list) of (str) files_list """ files_list = [] @@ -67,6 +67,26 @@ class FileCmds: files_list.append(file) return files_list + # noinspection PyMethodMayBeStatic + def list_subdirs(self, directory): + """ + Finds subdirs within the (str) directory dir. + Returns a (list) of (str) subdir_list. + """ + subdir_list = [] + # Filter out file sharing meta data dirs + excluded_dirs = ("Network Trash Folder", "Temporary Items", "TheVolumeSettingsFolder") + for root, dirs, _files in walk(directory, topdown=True): + # Strip out dirs that begin with . + dirs[:] = [d for d in dirs if not d[0] == "."] + for dir in dirs: + if dir not in excluded_dirs: + dirpath = path.join(root, dir) + subdir_list.append(dirpath.replace(directory, "", 1)) + + subdir_list.sort() + return subdir_list + def list_images(self): """ Sends a IMAGE_FILES_INFO command to the server diff --git a/python/web/src/templates/index.html b/python/web/src/templates/index.html index 8ab45539..b9d74cc4 100644 --- a/python/web/src/templates/index.html +++ b/python/web/src/templates/index.html @@ -393,8 +393,20 @@ + + diff --git a/python/web/src/templates/upload.html b/python/web/src/templates/upload.html index 11c269b5..67d6b57f 100644 --- a/python/web/src/templates/upload.html +++ b/python/web/src/templates/upload.html @@ -14,13 +14,20 @@
- + {% for dir in images_subdirs %} + {% endfor %} + +
diff --git a/python/web/src/web.py b/python/web/src/web.py index 0e15fd59..40d9b032 100644 --- a/python/web/src/web.py +++ b/python/web/src/web.py @@ -275,6 +275,8 @@ def index(): image_suffixes_to_create=image_suffixes_to_create, valid_image_suffixes=valid_image_suffixes, drive_properties=format_drive_properties(APP.config["PISCSI_DRIVE_PROPERTIES"]), + images_subdirs=file_cmd.list_subdirs(server_info["image_dir"]), + shared_subdirs=file_cmd.list_subdirs(FILE_SERVER_DIR), RESERVATIONS=RESERVATIONS, CFG_DIR=CFG_DIR, FILE_SERVER_DIR=FILE_SERVER_DIR, @@ -314,13 +316,13 @@ def upload_page(): """ Sets up the data structures and kicks off the rendering of the file uploading page """ - image_files = file_cmd.list_images() - formatted_image_files = format_image_list(image_files["files"]) + server_info = piscsi_cmd.get_server_info() return response( template="upload.html", page_title=_("PiSCSI File Upload"), - formatted_image_files=formatted_image_files, + images_subdirs=file_cmd.list_subdirs(server_info["image_dir"]), + shared_subdirs=file_cmd.list_subdirs(FILE_SERVER_DIR), max_file_size=int(int(MAX_FILE_SIZE) / 1024 / 1024), CFG_DIR=CFG_DIR, FILE_SERVER_DIR=FILE_SERVER_DIR, @@ -959,11 +961,22 @@ def download_file(): """ destination = request.form.get("destination") url = request.form.get("url") - if destination == "shared_files": - destination_dir = FILE_SERVER_DIR - else: + images_subdir = request.form.get("images_subdir") + shared_subdir = request.form.get("shared_subdir") + if destination == "disk_images": + safe_path = is_safe_path(Path("." + images_subdir)) + if not safe_path["status"]: + return make_response(safe_path["msg"], 403) server_info = piscsi_cmd.get_server_info() - destination_dir = server_info["image_dir"] + destination_dir = server_info["image_dir"] + images_subdir + elif destination == "shared_files": + safe_path = is_safe_path(Path("." + shared_subdir)) + if not safe_path["status"]: + return make_response(safe_path["msg"], 403) + destination_dir = FILE_SERVER_DIR + shared_subdir + else: + return response(error=True, message=_("Unknown destination")) + process = file_cmd.download_to_dir(url, destination_dir, Path(url).name) process = ReturnCodeMapper.add_msg(process) if process["status"]: @@ -990,19 +1003,23 @@ def upload_file(): return make_response(auth["msg"], 403) destination = request.form.get("destination") - subdir = request.form.get("subdir").replace("images/", "", 1) + images_subdir = request.form.get("images_subdir") + shared_subdir = request.form.get("shared_subdir") if destination == "disk_images": - safe_path = is_safe_path(Path(subdir)) + safe_path = is_safe_path(Path("." + images_subdir)) if not safe_path["status"]: return make_response(safe_path["msg"], 403) server_info = piscsi_cmd.get_server_info() - destination_dir = Path(server_info["image_dir"]) / subdir + destination_dir = server_info["image_dir"] + images_subdir elif destination == "shared_files": - destination_dir = FILE_SERVER_DIR + safe_path = is_safe_path(Path("." + shared_subdir)) + if not safe_path["status"]: + return make_response(safe_path["msg"], 403) + destination_dir = FILE_SERVER_DIR + shared_subdir elif destination == "piscsi_config": destination_dir = CFG_DIR else: - return make_response("Unknown destination", 403) + return make_response(_("Unknown destination"), 403) return upload_with_dropzonejs(destination_dir) diff --git a/python/web/tests/api/test_files.py b/python/web/tests/api/test_files.py index 7da66f12..8239dd22 100644 --- a/python/web/tests/api/test_files.py +++ b/python/web/tests/api/test_files.py @@ -207,7 +207,8 @@ def test_extract_file( http_client.post( "/files/download_url", data={ - "destination": "images", + "destination": "disk_images", + "images_subdir": "/", "url": url, }, ) @@ -254,7 +255,7 @@ def test_upload_file(http_client, delete_file): form_data = { "destination": "disk_images", - "subdir": "", + "images_subdir": "/", "dzuuid": str(uuid.uuid4()), "dzchunkindex": chunk_number, "dzchunksize": chunk_size, @@ -334,6 +335,7 @@ def test_download_properties(http_client, list_files, delete_file): def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file): file_name = str(uuid.uuid4()) http_path = f"/images/{file_name}" + subdir = "/" url = httpserver.url_for(http_path) with open("tests/assets/test_image.hds", mode="rb") as file: @@ -347,7 +349,8 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi response = http_client.post( "/files/download_url", data={ - "destination": "images", + "destination": "disk_images", + "images_subdir": subdir, "url": url, }, ) @@ -358,7 +361,8 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi assert response_data["status"] == STATUS_SUCCESS assert file_name in list_files() assert ( - response_data["messages"][0]["message"] == f"{file_name} downloaded to {env['images_dir']}" + response_data["messages"][0]["message"] + == f"{file_name} downloaded to {env['images_dir']}{subdir}" ) # Cleanup