implement dynamic file upload
This commit is contained in:
		
							
								
								
									
										35
									
								
								htdocs/lib/settings/ImageUpload.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								htdocs/lib/settings/ImageUpload.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
|     }); | ||||
| } | ||||
| @@ -22,4 +22,5 @@ $(function(){ | ||||
|     }); | ||||
|  | ||||
|     $(".sdrdevice").sdrdevice(); | ||||
|     $(".imageupload").imageUpload(); | ||||
| }); | ||||
| @@ -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", | ||||
|         ], | ||||
|     } | ||||
|   | ||||
							
								
								
									
										32
									
								
								owrx/controllers/imageupload.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								owrx/controllers/imageupload.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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 """ | ||||
|             <div class="imageupload"> | ||||
|                 <input type="hidden" id="{id}" name="{id}"> | ||||
|                 <img class="webrx-rx-avatar" src="static/gfx/openwebrx-avatar.png" alt="Receiver avatar"/> | ||||
|                 <button class="btn btn-primary">Upload new image...</button> | ||||
|             </div> | ||||
|         """.format( | ||||
|             id=self.id | ||||
|         ) | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jakob Ketterl
					Jakob Ketterl