Add a Copy image file flow to the Web UI. (#760)

* Add a Copy image file flow to the Web UI.

* Introduce a generic file creation message and use that consistently.

* Clarify code comment
This commit is contained in:
Daniel Markstedt 2022-06-14 19:03:56 -07:00 committed by GitHub
parent a5d397b3fc
commit 6397d9c9a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 17 deletions

View File

@ -11,6 +11,7 @@ from re import escape, findall
from time import time from time import time
from subprocess import run, CalledProcessError from subprocess import run, CalledProcessError
from json import dump, load from json import dump, load
from shutil import copyfile
import requests import requests
@ -180,6 +181,25 @@ class FileCmds:
result.ParseFromString(data) result.ParseFromString(data)
return {"status": result.status, "msg": result.msg} return {"status": result.status, "msg": result.msg}
def copy_image(self, file_name, new_file_name):
"""
Takes (str) file_name, (str) new_file_name
Sends a COPY_IMAGE command to the server
Returns (dict) with (bool) status and (str) msg
"""
command = proto.PbCommand()
command.operation = proto.PbOperation.COPY_IMAGE
command.params["token"] = self.ractl.token
command.params["locale"] = self.ractl.locale
command.params["from"] = file_name
command.params["to"] = new_file_name
data = self.sock_cmd.send_pb_command(command.SerializeToString())
result = proto.PbResult()
result.ParseFromString(data)
return {"status": result.status, "msg": result.msg}
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
def delete_file(self, file_path): def delete_file(self, file_path):
""" """
@ -224,6 +244,28 @@ class FileCmds:
"parameters": parameters, "parameters": parameters,
} }
# noinspection PyMethodMayBeStatic
def copy_file(self, file_path, target_path):
"""
Takes (str) file_path and (str) target_path
Returns (dict) with (bool) status and (str) msg
"""
parameters = {
"target_path": target_path
}
if os.path.exists(PurePath(target_path).parent):
copyfile(file_path, target_path)
return {
"status": True,
"return_code": ReturnCodes.WRITEFILE_SUCCESS,
"parameters": parameters,
}
return {
"status": False,
"return_code": ReturnCodes.WRITEFILE_UNABLE_TO_WRITE,
"parameters": parameters,
}
def unzip_file(self, file_name, member=False, members=False): def unzip_file(self, file_name, member=False, members=False):
""" """
Takes (str) file_name, optional (str) member, optional (list) of (str) members Takes (str) file_name, optional (str) member, optional (list) of (str) members
@ -404,11 +446,11 @@ class FileCmds:
indent=4 indent=4
) )
parameters = { parameters = {
"file_name": file_name "target_path": file_name
} }
return { return {
"status": True, "status": True,
"return_code": ReturnCodes.WRITECONFIG_SUCCESS, "return_code": ReturnCodes.WRITEFILE_SUCCESS,
"parameters": parameters, "parameters": parameters,
} }
except (IOError, ValueError, EOFError, TypeError) as error: except (IOError, ValueError, EOFError, TypeError) as error:
@ -423,7 +465,7 @@ class FileCmds:
} }
return { return {
"status": False, "status": False,
"return_code": ReturnCodes.WRITECONFIG_COULD_NOT_WRITE, "return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE,
"parameters": parameters, "parameters": parameters,
} }
@ -515,11 +557,11 @@ class FileCmds:
with open(file_path, "w") as json_file: with open(file_path, "w") as json_file:
dump(conf, json_file, indent=4) dump(conf, json_file, indent=4)
parameters = { parameters = {
"file_path": file_path "target_path": file_path
} }
return { return {
"status": True, "status": True,
"return_code": ReturnCodes.WRITEDRIVEPROPS_SUCCESS, "return_code": ReturnCodes.WRITEFILE_SUCCESS,
"parameters": parameters, "parameters": parameters,
} }
except (IOError, ValueError, EOFError, TypeError) as error: except (IOError, ValueError, EOFError, TypeError) as error:
@ -530,11 +572,11 @@ class FileCmds:
logging.error("Could not write to file: %s", file_path) logging.error("Could not write to file: %s", file_path)
self.delete_file(file_path) self.delete_file(file_path)
parameters = { parameters = {
"file_path": file_path "target_path": file_path
} }
return { return {
"status": False, "status": False,
"return_code": ReturnCodes.WRITEDRIVEPROPS_COULD_NOT_WRITE, "return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE,
"parameters": parameters, "parameters": parameters,
} }

View File

@ -12,13 +12,11 @@ class ReturnCodes:
RENAMEFILE_UNABLE_TO_MOVE = 11 RENAMEFILE_UNABLE_TO_MOVE = 11
DOWNLOADFILETOISO_SUCCESS = 20 DOWNLOADFILETOISO_SUCCESS = 20
DOWNLOADTODIR_SUCCESS = 30 DOWNLOADTODIR_SUCCESS = 30
WRITECONFIG_SUCCESS = 40 WRITEFILE_SUCCESS = 40
WRITECONFIG_COULD_NOT_WRITE = 41 WRITEFILE_COULD_NOT_WRITE = 41
READCONFIG_SUCCESS = 50 READCONFIG_SUCCESS = 50
READCONFIG_COULD_NOT_READ = 51 READCONFIG_COULD_NOT_READ = 51
READCONFIG_INVALID_CONFIG_FILE_FORMAT = 51 READCONFIG_INVALID_CONFIG_FILE_FORMAT = 52
WRITEDRIVEPROPS_SUCCESS = 60
WRITEDRIVEPROPS_COULD_NOT_WRITE = 61
READDRIVEPROPS_SUCCESS = 70 READDRIVEPROPS_SUCCESS = 70
READDRIVEPROPS_COULD_NOT_READ = 71 READDRIVEPROPS_COULD_NOT_READ = 71
ATTACHIMAGE_COULD_NOT_ATTACH = 80 ATTACHIMAGE_COULD_NOT_ATTACH = 80

View File

@ -16,15 +16,12 @@ class ReturnCodeMapper:
ReturnCodes.DOWNLOADFILETOISO_SUCCESS: _("Created CD-ROM ISO image with " ReturnCodes.DOWNLOADFILETOISO_SUCCESS: _("Created CD-ROM ISO image with "
"arguments \"%(value)s\""), "arguments \"%(value)s\""),
ReturnCodes.DOWNLOADTODIR_SUCCESS: _("%(file_name)s downloaded to %(save_dir)s"), ReturnCodes.DOWNLOADTODIR_SUCCESS: _("%(file_name)s downloaded to %(save_dir)s"),
ReturnCodes.WRITECONFIG_SUCCESS: _("Saved configuration file to %(file_name)s"), ReturnCodes.WRITEFILE_SUCCESS: _("File created: %(target_path)s"),
ReturnCodes.WRITECONFIG_COULD_NOT_WRITE: _("Could not write to file: %(file_name)s"), ReturnCodes.WRITEFILE_COULD_NOT_WRITE: _("Could not create file: %(target_path)s"),
ReturnCodes.READCONFIG_SUCCESS: _("Loaded configurations from: %(file_name)s"), ReturnCodes.READCONFIG_SUCCESS: _("Loaded configurations from: %(file_name)s"),
ReturnCodes.READCONFIG_COULD_NOT_READ: _("Could not read configuration " ReturnCodes.READCONFIG_COULD_NOT_READ: _("Could not read configuration "
"file: %(file_name)s"), "file: %(file_name)s"),
ReturnCodes.READCONFIG_INVALID_CONFIG_FILE_FORMAT: _("Invalid configuration file format"), ReturnCodes.READCONFIG_INVALID_CONFIG_FILE_FORMAT: _("Invalid configuration file format"),
ReturnCodes.WRITEDRIVEPROPS_SUCCESS: _("Created properties file: %(file_path)s"),
ReturnCodes.WRITEDRIVEPROPS_COULD_NOT_WRITE: _("Could not write to properties "
"file: %(file_path)s"),
ReturnCodes.READDRIVEPROPS_SUCCESS: _("Read properties from file: %(file_path)s"), ReturnCodes.READDRIVEPROPS_SUCCESS: _("Read properties from file: %(file_path)s"),
ReturnCodes.READDRIVEPROPS_COULD_NOT_READ: _("Could not read properties from " ReturnCodes.READDRIVEPROPS_COULD_NOT_READ: _("Could not read properties from "
"file: %(file_path)s"), "file: %(file_path)s"),

View File

@ -283,6 +283,11 @@
<input name="new_file_name" id="new_file_name_{{ loop.index }}" type="hidden" value=""> <input name="new_file_name" id="new_file_name_{{ loop.index }}" type="hidden" value="">
<input type="submit" value="{{ _("Rename") }}"> <input type="submit" value="{{ _("Rename") }}">
</form> </form>
<form action="/files/copy" method="post" onsubmit="var copy_file_name = prompt('{{ _("Save copy of %(file_name)s as:", file_name=file["name"]) }}', '{{ file['name'] }}'); if (copy_file_name === null) event.preventDefault(); document.getElementById('copy_file_name_{{ loop.index }}').value = copy_file_name;">
<input name="file_name" type="hidden" value="{{ file['name'] }}">
<input name="copy_file_name" id="copy_file_name_{{ loop.index }}" type="hidden" value="">
<input type="submit" value="{{ _("Copy") }}">
</form>
<form action="/files/delete" method="post" onsubmit="return confirm('{{ _("Delete file: %(file_name)s?", file_name=file["name"]) }}')"> <form action="/files/delete" method="post" onsubmit="return confirm('{{ _("Delete file: %(file_name)s?", file_name=file["name"]) }}')">
<input name="file_name" type="hidden" value="{{ file['name'] }}"> <input name="file_name" type="hidden" value="{{ file['name'] }}">
<input type="submit" value="{{ _("Delete") }}"> <input type="submit" value="{{ _("Delete") }}">

View File

@ -912,6 +912,38 @@ def rename():
return redirect(url_for("index")) return redirect(url_for("index"))
@APP.route("/files/copy", methods=["POST"])
@login_required
def copy():
"""
Creates a copy of a specified file in the images dir
"""
file_name = request.form.get("file_name")
new_file_name = request.form.get("copy_file_name")
process = file_cmd.copy_image(file_name, new_file_name)
if process["status"]:
flash(_("Copy of image file saved as: %(file_name)s", file_name=new_file_name))
else:
flash(process["msg"], "error")
return redirect(url_for("index"))
# Create a copy of the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
new_prop_file_path = f"{CFG_DIR}/{new_file_name}.{PROPERTIES_SUFFIX}"
if Path(prop_file_path).is_file():
process = file_cmd.copy_file(prop_file_path, new_prop_file_path)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
flash(process["msg"], "error")
return redirect(url_for("index"))
return redirect(url_for("index"))
@APP.route("/files/unzip", methods=["POST"]) @APP.route("/files/unzip", methods=["POST"])
@login_required @login_required
def unzip(): def unzip():