implement dynamic file upload
This commit is contained in:
parent
2d72055070
commit
a1c024bfe2
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();
|
$(".sdrdevice").sdrdevice();
|
||||||
|
$(".imageupload").imageUpload();
|
||||||
});
|
});
|
@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class GzipMixin(object):
|
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:
|
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(",")]
|
accepted = [s.strip().lower() for s in self.request.headers["accept-encoding"].split(",")]
|
||||||
if "gzip" in accepted:
|
if "gzip" in accepted:
|
||||||
@ -23,7 +23,7 @@ class GzipMixin(object):
|
|||||||
if headers is None:
|
if headers is None:
|
||||||
headers = {}
|
headers = {}
|
||||||
headers["Content-Encoding"] = "gzip"
|
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):
|
def zipable(self, content_type):
|
||||||
types = ["application/javascript", "text/css", "text/html"]
|
types = ["application/javascript", "text/css", "text/html"]
|
||||||
@ -78,7 +78,7 @@ class AssetsController(GzipMixin, ModificationAwareController, metaclass=ABCMeta
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
if content_type is None:
|
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)
|
self.send_response(data, content_type=content_type, last_modified=modified, max_age=3600)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.send_response("file not found", code=404)
|
self.send_response("file not found", code=404)
|
||||||
@ -137,6 +137,7 @@ class CompiledAssetsController(GzipMixin, ModificationAwareController):
|
|||||||
"lib/Header.js",
|
"lib/Header.js",
|
||||||
"lib/settings/Input.js",
|
"lib/settings/Input.js",
|
||||||
"lib/settings/SdrDevice.js",
|
"lib/settings/SdrDevice.js",
|
||||||
|
"lib/settings/ImageUpload.js",
|
||||||
"settings.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.aprs import AprsBeaconSymbols, AprsAntennaDirections
|
||||||
from owrx.form.wfm import WfmTauValues
|
from owrx.form.wfm import WfmTauValues
|
||||||
from owrx.form.wsjt import Q65ModeMatrix
|
from owrx.form.wsjt import Q65ModeMatrix
|
||||||
|
from owrx.form.gfx import AvatarInput
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from owrx.wsjt import Fst4Profile, Fst4wProfile
|
from owrx.wsjt import Fst4Profile, Fst4wProfile
|
||||||
import json
|
import json
|
||||||
@ -107,6 +108,13 @@ class GeneralSettingsController(AdminController):
|
|||||||
TextInput("photo_title", "Photo title"),
|
TextInput("photo_title", "Photo title"),
|
||||||
TextAreaInput("photo_desc", "Photo description"),
|
TextAreaInput("photo_desc", "Photo description"),
|
||||||
),
|
),
|
||||||
|
Section(
|
||||||
|
"Receiver images",
|
||||||
|
AvatarInput(
|
||||||
|
"receiver_avatar",
|
||||||
|
"Receiver Avatar",
|
||||||
|
),
|
||||||
|
),
|
||||||
Section(
|
Section(
|
||||||
"Receiver listings",
|
"Receiver listings",
|
||||||
TextAreaInput(
|
TextAreaInput(
|
||||||
|
@ -2,6 +2,16 @@ from owrx.form import Input
|
|||||||
|
|
||||||
|
|
||||||
class AvatarInput(Input):
|
class AvatarInput(Input):
|
||||||
def render_input(self, value):
|
def __init__(self, id, label, infotext=None):
|
||||||
pass
|
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.settings import SettingsController, GeneralSettingsController, SdrSettingsController
|
||||||
from owrx.controllers.session import SessionController
|
from owrx.controllers.session import SessionController
|
||||||
from owrx.controllers.profile import ProfileController
|
from owrx.controllers.profile import ProfileController
|
||||||
|
from owrx.controllers.imageupload import ImageUploadController
|
||||||
from http.server import BaseHTTPRequestHandler
|
from http.server import BaseHTTPRequestHandler
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
import re
|
import re
|
||||||
@ -112,6 +113,8 @@ class Router(object):
|
|||||||
StaticRoute("/logout", SessionController, options={"action": "logoutAction"}),
|
StaticRoute("/logout", SessionController, options={"action": "logoutAction"}),
|
||||||
StaticRoute("/pwchange", ProfileController),
|
StaticRoute("/pwchange", ProfileController),
|
||||||
StaticRoute("/pwchange", ProfileController, method="POST", options={"action": "processPwChange"}),
|
StaticRoute("/pwchange", ProfileController, method="POST", options={"action": "processPwChange"}),
|
||||||
|
StaticRoute("/imageupload", ImageUploadController),
|
||||||
|
StaticRoute("/imageupload", ImageUploadController, method="POST", options={"action": "processImage"}),
|
||||||
]
|
]
|
||||||
|
|
||||||
def find_route(self, request):
|
def find_route(self, request):
|
||||||
|
Loading…
Reference in New Issue
Block a user