diff --git a/htdocs/lib/settings/ImageUpload.js b/htdocs/lib/settings/ImageUpload.js new file mode 100644 index 0000000..e95eaab --- /dev/null +++ b/htdocs/lib/settings/ImageUpload.js @@ -0,0 +1,35 @@ +$.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'; + + input.onchange = function(e) { + var reader = new FileReader() + 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; + }); +} \ No newline at end of file diff --git a/htdocs/settings.js b/htdocs/settings.js index e95e2fe..e30de75 100644 --- a/htdocs/settings.js +++ b/htdocs/settings.js @@ -22,4 +22,5 @@ $(function(){ }); $(".sdrdevice").sdrdevice(); + $(".imageupload").imageUpload(); }); \ No newline at end of file diff --git a/owrx/controllers/assets.py b/owrx/controllers/assets.py index 88338b5..a1468e4 100644 --- a/owrx/controllers/assets.py +++ b/owrx/controllers/assets.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) class GzipMixin(object): - def send_response(self, content, headers=None, content_type="text/html", *args, **kwargs): + def send_response(self, content, code=200, headers=None, content_type="text/html", *args, **kwargs): if self.zipable(content_type) and "accept-encoding" in self.request.headers: accepted = [s.strip().lower() for s in self.request.headers["accept-encoding"].split(",")] if "gzip" in accepted: @@ -23,7 +23,7 @@ class GzipMixin(object): if headers is None: headers = {} headers["Content-Encoding"] = "gzip" - super().send_response(content, headers=headers, content_type=content_type, *args, **kwargs) + super().send_response(content, code, headers=headers, content_type=content_type, *args, **kwargs) def zipable(self, content_type): types = ["application/javascript", "text/css", "text/html"] @@ -78,7 +78,7 @@ class AssetsController(GzipMixin, ModificationAwareController, metaclass=ABCMeta f.close() if content_type is None: - (content_type, encoding) = mimetypes.MimeTypes().guess_type(file) + (content_type, encoding) = mimetypes.MimeTypes().guess_type(self.getFilePath(file)) self.send_response(data, content_type=content_type, last_modified=modified, max_age=3600) except FileNotFoundError: self.send_response("file not found", code=404) @@ -137,6 +137,7 @@ class CompiledAssetsController(GzipMixin, ModificationAwareController): "lib/Header.js", "lib/settings/Input.js", "lib/settings/SdrDevice.js", + "lib/settings/ImageUpload.js", "settings.js", ], } diff --git a/owrx/controllers/imageupload.py b/owrx/controllers/imageupload.py new file mode 100644 index 0000000..000b4d5 --- /dev/null +++ b/owrx/controllers/imageupload.py @@ -0,0 +1,32 @@ +from owrx.controllers.assets import AssetsController +from owrx.config import CoreConfig +import uuid +import json + + +class ImageUploadController(AssetsController): + def __init__(self, handler, request, options): + super().__init__(handler, request, options) + self.uuid = request.query["uuid"][0] if "uuid" in request.query else None + self.id = request.query["id"][0] if "id" in request.query else None + + def getFilePath(self, file=None): + if self.uuid is None: + raise FileNotFoundError("missing uuid") + if self.id is None: + raise FileNotFoundError("missing id") + return "{tmp}/{file}-{uuid}".format( + tmp=CoreConfig().get_temporary_directory(), + file=self.id, + uuid=self.uuid, + ) + + def indexAction(self): + self.serve_file(None) + + def processImage(self): + self.uuid = uuid.uuid4().hex + contents = self.get_body() + 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 b166530..a910865 100644 --- a/owrx/controllers/settings.py +++ b/owrx/controllers/settings.py @@ -19,6 +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 urllib.parse import quote from owrx.wsjt import Fst4Profile, Fst4wProfile import json @@ -107,6 +108,13 @@ class GeneralSettingsController(AdminController): TextInput("photo_title", "Photo title"), TextAreaInput("photo_desc", "Photo description"), ), + Section( + "Receiver images", + AvatarInput( + "receiver_avatar", + "Receiver Avatar", + ), + ), Section( "Receiver listings", TextAreaInput( diff --git a/owrx/form/gfx.py b/owrx/form/gfx.py index 3712b87..1731a61 100644 --- a/owrx/form/gfx.py +++ b/owrx/form/gfx.py @@ -2,6 +2,16 @@ from owrx.form import Input class AvatarInput(Input): - def render_input(self, value): - pass + def __init__(self, id, label, infotext=None): + super().__init__(id, label, infotext=infotext) + def render_input(self, value): + return """ +
+ + Receiver avatar + +
+ """.format( + id=self.id + ) diff --git a/owrx/http.py b/owrx/http.py index 1ccd216..ec791d8 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -7,6 +7,7 @@ from owrx.controllers.metrics import MetricsController from owrx.controllers.settings import SettingsController, GeneralSettingsController, SdrSettingsController from owrx.controllers.session import SessionController from owrx.controllers.profile import ProfileController +from owrx.controllers.imageupload import ImageUploadController from http.server import BaseHTTPRequestHandler from urllib.parse import urlparse, parse_qs import re @@ -112,6 +113,8 @@ class Router(object): StaticRoute("/logout", SessionController, options={"action": "logoutAction"}), StaticRoute("/pwchange", ProfileController), StaticRoute("/pwchange", ProfileController, method="POST", options={"action": "processPwChange"}), + StaticRoute("/imageupload", ImageUploadController), + StaticRoute("/imageupload", ImageUploadController, method="POST", options={"action": "processImage"}), ] def find_route(self, request):