diff --git a/htdocs/css/admin.css b/htdocs/css/admin.css index 8457124..103fbf8 100644 --- a/htdocs/css/admin.css +++ b/htdocs/css/admin.css @@ -32,4 +32,14 @@ h1 { .q65-matrix { grid-template-columns: repeat(5, auto); -} \ No newline at end of file +} + +.imageupload .image-container { + max-width: 100%; + padding: 7px; +} + +.imageupload img.webrx-top-photo { + max-height: 350px; + max-width: 100%; +} diff --git a/htdocs/lib/settings/ImageUpload.js b/htdocs/lib/settings/ImageUpload.js index b81d9e8..1e02563 100644 --- a/htdocs/lib/settings/ImageUpload.js +++ b/htdocs/lib/settings/ImageUpload.js @@ -1,36 +1,37 @@ $.fn.imageUpload = function() { - var $button = $(this).find('button'); - var $img = $(this).find('img'); - var $input = $(this).find('input'); - var id = $input.prop('id'); - console.info(id); - $button.click(function(){ - $button.prop('disabled', true); - var input = document.createElement('input'); - input.type = 'file'; - input.accept = 'image/jpeg, image/png'; + $.each(this, function(){ + var $button = $(this).find('button'); + var $img = $(this).find('img'); + var $input = $(this).find('input'); + var id = $input.prop('id'); + $button.click(function(){ + $button.prop('disabled', true); + var input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/jpeg, image/png'; - input.onchange = function(e) { - var reader = new FileReader() - // TODO: implement file size check - reader.readAsArrayBuffer(e.target.files[0]); - reader.onload = function(e) { - $.ajax({ - url: '/imageupload?id=' + id, - type: 'POST', - data: e.target.result, - processData: false, - contentType: 'application/octet-stream', - }).done(function(data){ - $input.val(data.uuid); - $img.prop('src', "/imageupload?id=" + id + "&uuid=" + data.uuid); - }).always(function(){ - $button.prop('disabled', false); - }); - } - }; + input.onchange = function(e) { + var reader = new FileReader() + // TODO: implement file size check + reader.readAsArrayBuffer(e.target.files[0]); + reader.onload = function(e) { + $.ajax({ + url: '/imageupload?id=' + id, + type: 'POST', + data: e.target.result, + processData: false, + contentType: 'application/octet-stream', + }).done(function(data){ + $input.val(data.uuid); + $img.prop('src', "/imageupload?id=" + id + "&uuid=" + data.uuid); + }).always(function(){ + $button.prop('disabled', false); + }); + } + }; - input.click(); - return false; + input.click(); + return false; + }); }); } \ No newline at end of file diff --git a/owrx/controllers/assets.py b/owrx/controllers/assets.py index 824b344..07b2f6f 100644 --- a/owrx/controllers/assets.py +++ b/owrx/controllers/assets.py @@ -90,11 +90,15 @@ class AssetsController(GzipMixin, ModificationAwareController, metaclass=ABCMeta class OwrxAssetsController(AssetsController): def getFilePath(self, file): + mappedFiles = { + "gfx/openwebrx-avatar.png": "receiver_avatar", + "gfx/openwebrx-top-photo.jpg": "receiver_top_photo", + } config = CoreConfig() - if file == "gfx/openwebrx-avatar.png": - file = config.get_data_directory() + "/receiver_avatar" - if os.path.exists(file) and os.path.isfile(file): - return file + if file in mappedFiles: + user_file = config.get_data_directory() + "/" + mappedFiles[file] + if os.path.exists(user_file) and os.path.isfile(user_file): + return user_file return pkg_resources.resource_filename("htdocs", file) diff --git a/owrx/controllers/imageupload.py b/owrx/controllers/imageupload.py index 9275be3..28ce8ce 100644 --- a/owrx/controllers/imageupload.py +++ b/owrx/controllers/imageupload.py @@ -28,7 +28,9 @@ class ImageUploadController(AssetsController): def processImage(self): self.uuid = uuid.uuid4().hex # TODO: limit file size + # TODO: check image mime type, if possible contents = self.get_body() + # TODO: clean up files after timeout or on shutdown with open(self.getFilePath(), 'wb') as f: f.write(contents) self.send_response(json.dumps({"uuid": self.uuid}), content_type="application/json") diff --git a/owrx/controllers/settings.py b/owrx/controllers/settings.py index 1dd09d3..7e4674e 100644 --- a/owrx/controllers/settings.py +++ b/owrx/controllers/settings.py @@ -19,7 +19,7 @@ from owrx.form.receiverid import ReceiverKeysConverter from owrx.form.aprs import AprsBeaconSymbols, AprsAntennaDirections from owrx.form.wfm import WfmTauValues from owrx.form.wsjt import Q65ModeMatrix -from owrx.form.gfx import AvatarInput +from owrx.form.gfx import AvatarInput, TopPhotoInput from urllib.parse import quote from owrx.wsjt import Fst4Profile, Fst4wProfile import json @@ -115,6 +115,10 @@ class GeneralSettingsController(AdminController): "receiver_avatar", "Receiver Avatar", ), + TopPhotoInput( + "receiver_top_photo", + "Receiver Panorama", + ), ), Section( "Receiver listings", @@ -303,11 +307,7 @@ class GeneralSettingsController(AdminController): append="dBi", converter=OptionalConverter(), ), - DropdownInput( - "aprs_igate_dir", - "Antenna direction", - AprsAntennaDirections - ), + DropdownInput("aprs_igate_dir", "Antenna direction", AprsAntennaDirections), ), Section( "pskreporter settings", @@ -365,14 +365,11 @@ class GeneralSettingsController(AdminController): return variables def handle_image(self, data, image_id): - if image_id not in data: + if image_id not in data or not data[image_id]: return config = CoreConfig() filename = "{}-{}".format(image_id, data[image_id]) - shutil.copy( - config.get_temporary_directory() + "/" + filename, - config.get_data_directory() + "/" + image_id - ) + shutil.copy(config.get_temporary_directory() + "/" + filename, config.get_data_directory() + "/" + image_id) del data[image_id] def processFormData(self): @@ -380,6 +377,7 @@ class GeneralSettingsController(AdminController): data = {k: v for i in GeneralSettingsController.sections for k, v in i.parse(data).items()} # Image handling self.handle_image(data, "receiver_avatar") + self.handle_image(data, "receiver_top_photo") config = Config.get() for k, v in data.items(): if v is None: diff --git a/owrx/form/gfx.py b/owrx/form/gfx.py index 1731a61..591c052 100644 --- a/owrx/form/gfx.py +++ b/owrx/form/gfx.py @@ -2,14 +2,28 @@ from owrx.form import Input class AvatarInput(Input): - def __init__(self, id, label, infotext=None): - super().__init__(id, label, infotext=infotext) - def render_input(self, value): return """
- Receiver avatar +
+ Receiver avatar +
+ +
+ """.format( + id=self.id + ) + + +class TopPhotoInput(Input): + def render_input(self, value): + return """ +
+ +
+ Receiver Panorama +
""".format(