mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
share folders as qr-codes
This commit is contained in:
parent
21be82ef8b
commit
e45420646f
|
@ -37,6 +37,7 @@ from .__version__ import S_VERSION
|
||||||
from .authsrv import VFS # typechk
|
from .authsrv import VFS # typechk
|
||||||
from .bos import bos
|
from .bos import bos
|
||||||
from .star import StreamTar
|
from .star import StreamTar
|
||||||
|
from .stolen.qrcodegen import QrCode, qr2svg
|
||||||
from .sutil import StreamArc, gfilter
|
from .sutil import StreamArc, gfilter
|
||||||
from .szip import StreamZip
|
from .szip import StreamZip
|
||||||
from .up2k import up2k_chunksize
|
from .up2k import up2k_chunksize
|
||||||
|
@ -495,6 +496,9 @@ class HttpCli(object):
|
||||||
self.vpath + "/" if self.trailing_slash and self.vpath else self.vpath
|
self.vpath + "/" if self.trailing_slash and self.vpath else self.vpath
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if "qr" in uparam:
|
||||||
|
return self.tx_qr()
|
||||||
|
|
||||||
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
|
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
|
||||||
self.log("invalid relpath [{}]".format(self.vpath))
|
self.log("invalid relpath [{}]".format(self.vpath))
|
||||||
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
|
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
|
||||||
|
@ -3934,6 +3938,33 @@ class HttpCli(object):
|
||||||
self.reply(ico, mime=mime, headers={"Last-Modified": lm})
|
self.reply(ico, mime=mime, headers={"Last-Modified": lm})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def tx_qr(self):
|
||||||
|
url = "%s://%s%s%s" % (
|
||||||
|
"https" if self.is_https else "http",
|
||||||
|
self.host,
|
||||||
|
self.args.SRS,
|
||||||
|
self.vpaths,
|
||||||
|
)
|
||||||
|
uhash = ""
|
||||||
|
uparams = []
|
||||||
|
if self.ouparam:
|
||||||
|
for k, v in self.ouparam.items():
|
||||||
|
if k == "qr":
|
||||||
|
continue
|
||||||
|
if k == "uhash":
|
||||||
|
uhash = v
|
||||||
|
continue
|
||||||
|
uparams.append(k if v is "" else "%s=%s" % (k, v))
|
||||||
|
if uparams:
|
||||||
|
url += "?" + "&".join(uparams)
|
||||||
|
if uhash:
|
||||||
|
url += "#" + uhash
|
||||||
|
|
||||||
|
self.log("qrcode(%r)" % (url,))
|
||||||
|
ret = qr2svg(QrCode.encode_binary(url.encode("utf-8")), 2)
|
||||||
|
self.reply(ret.encode("utf-8"), mime="image/svg+xml")
|
||||||
|
return True
|
||||||
|
|
||||||
def tx_md(self, vn: VFS, fs_path: str) -> bool:
|
def tx_md(self, vn: VFS, fs_path: str) -> bool:
|
||||||
logmsg = " %s @%s " % (self.req, self.uname)
|
logmsg = " %s @%s " % (self.req, self.uname)
|
||||||
|
|
||||||
|
@ -4505,9 +4536,6 @@ class HttpCli(object):
|
||||||
if self.uname != self.args.shr_adm:
|
if self.uname != self.args.shr_adm:
|
||||||
rows = [x for x in rows if x[5] == self.uname]
|
rows = [x for x in rows if x[5] == self.uname]
|
||||||
|
|
||||||
for x in rows:
|
|
||||||
x[1] = "yes" if x[1] else ""
|
|
||||||
|
|
||||||
html = self.j2s(
|
html = self.j2s(
|
||||||
"shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time())
|
"shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time())
|
||||||
)
|
)
|
||||||
|
@ -4585,7 +4613,7 @@ class HttpCli(object):
|
||||||
else:
|
else:
|
||||||
for zs in vps:
|
for zs in vps:
|
||||||
if zs.endswith("/"):
|
if zs.endswith("/"):
|
||||||
t = "you cannot select more than one folder, or mix flies and folders in one selection"
|
t = "you cannot select more than one folder, or mix files and folders in one selection"
|
||||||
raise Pebkac(400, t)
|
raise Pebkac(400, t)
|
||||||
vp = vps[0].rsplit("/", 1)[0]
|
vp = vps[0].rsplit("/", 1)[0]
|
||||||
for zs in vps:
|
for zs in vps:
|
||||||
|
|
|
@ -594,3 +594,20 @@ def _get_bit(x: int, i: int) -> bool:
|
||||||
|
|
||||||
class DataTooLongError(ValueError):
|
class DataTooLongError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def qr2svg(qr: QrCode, border: int) -> str:
|
||||||
|
parts: list[str] = []
|
||||||
|
for y in range(qr.size):
|
||||||
|
sy = border + y
|
||||||
|
for x in range(qr.size):
|
||||||
|
if qr.modules[y][x]:
|
||||||
|
parts.append("M%d,%dh1v1h-1z" % (border + x, sy))
|
||||||
|
t = """\
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none">
|
||||||
|
<rect width="100%" height="100%" fill="#F7F7F7"/>
|
||||||
|
<path d="{1}" fill="#111111"/>
|
||||||
|
</svg>
|
||||||
|
"""
|
||||||
|
return t.format(qr.size + border * 2, " ".join(parts))
|
||||||
|
|
|
@ -345,7 +345,7 @@ var Ls = {
|
||||||
"fs_tsrc": "the file or folder to share",
|
"fs_tsrc": "the file or folder to share",
|
||||||
"fs_ppwd": "optional password",
|
"fs_ppwd": "optional password",
|
||||||
"fs_w8": "creating share...",
|
"fs_w8": "creating share...",
|
||||||
"fs_ok": "<h6>share-URL created</h6>\npress <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close\n\n",
|
"fs_ok": "press <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close",
|
||||||
|
|
||||||
"frt_dec": "may fix some cases of broken filenames\">url-decode",
|
"frt_dec": "may fix some cases of broken filenames\">url-decode",
|
||||||
"frt_rst": "reset modified filenames back to the original ones\">↺ reset",
|
"frt_rst": "reset modified filenames back to the original ones\">↺ reset",
|
||||||
|
@ -913,7 +913,7 @@ var Ls = {
|
||||||
"fs_tsrc": "fil/mappe som skal deles",
|
"fs_tsrc": "fil/mappe som skal deles",
|
||||||
"fs_ppwd": "frivillig passord",
|
"fs_ppwd": "frivillig passord",
|
||||||
"fs_w8": "oppretter deling...",
|
"fs_w8": "oppretter deling...",
|
||||||
"fs_ok": "<h6>URL opprettet</h6>\ntrykk <code>Enter/OK</code> for å kopiere linken (for CTRL-V)\ntrykk <code>ESC/Avbryt</code> for å bare bekrefte\n\n",
|
"fs_ok": "trykk <code>Enter/OK</code> for å kopiere linken (for CTRL-V)\ntrykk <code>ESC/Avbryt</code> for å bare bekrefte",
|
||||||
|
|
||||||
"frt_dec": "kan korrigere visse ødelagte filnavn\">url-decode",
|
"frt_dec": "kan korrigere visse ødelagte filnavn\">url-decode",
|
||||||
"frt_rst": "nullstiller endringer (tilbake til de originale filnavnene)\">↺ reset",
|
"frt_rst": "nullstiller endringer (tilbake til de originale filnavnene)\">↺ reset",
|
||||||
|
@ -1481,7 +1481,7 @@ var Ls = {
|
||||||
"fs_tsrc": "共享的文件或文件夹",
|
"fs_tsrc": "共享的文件或文件夹",
|
||||||
"fs_ppwd": "密码可选",
|
"fs_ppwd": "密码可选",
|
||||||
"fs_w8": "正在创建文件共享...",
|
"fs_w8": "正在创建文件共享...",
|
||||||
"fs_ok": "<h6>分享链接已创建</h6>\n按 <code>Enter/OK</code> 复制到剪贴板\n按 <code>ESC/Cancel</code> 关闭\n\n",
|
"fs_ok": "按 <code>Enter/OK</code> 复制到剪贴板\n按 <code>ESC/Cancel</code> 关闭",
|
||||||
|
|
||||||
"frt_dec": "可能修复一些损坏的文件名\">url-decode",
|
"frt_dec": "可能修复一些损坏的文件名\">url-decode",
|
||||||
"frt_rst": "将修改后的文件名重置为原始文件名\">↺ 重置",
|
"frt_rst": "将修改后的文件名重置为原始文件名\">↺ 重置",
|
||||||
|
@ -1788,7 +1788,7 @@ ebi('widget').innerHTML = (
|
||||||
' <canvas id="barbuf"></canvas>' +
|
' <canvas id="barbuf"></canvas>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div id="np_inf">' +
|
'<div id="np_inf">' +
|
||||||
' <img id="np_img"></span>' +
|
' <img id="np_img" />' +
|
||||||
' <span id="np_url"></span>' +
|
' <span id="np_url"></span>' +
|
||||||
' <span id="np_circle"></span>' +
|
' <span id="np_circle"></span>' +
|
||||||
' <span id="np_album"></span>' +
|
' <span id="np_album"></span>' +
|
||||||
|
@ -4596,11 +4596,12 @@ var fileman = (function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
surl = surl.slice(15);
|
surl = surl.slice(15);
|
||||||
modal.confirm(L.fs_ok + esc(surl), function() {
|
var txt = esc(surl) + '<img class="b64" src="' + surl + '?qr" />';
|
||||||
|
modal.confirm(txt + L.fs_ok, function() {
|
||||||
cliptxt(surl, function () {
|
cliptxt(surl, function () {
|
||||||
toast.ok(2, L.clipped);
|
toast.ok(2, L.clipped);
|
||||||
});
|
});
|
||||||
});
|
}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
sh_apply.onclick = function () {
|
sh_apply.onclick = function () {
|
||||||
|
|
|
@ -27,7 +27,7 @@ a {
|
||||||
padding: .2em .6em;
|
padding: .2em .6em;
|
||||||
margin: 0 .3em;
|
margin: 0 .3em;
|
||||||
}
|
}
|
||||||
td a {
|
#wrap td a {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
#w {
|
#w {
|
||||||
|
@ -53,12 +53,13 @@ th {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
}
|
}
|
||||||
td, th {
|
#wrap td,
|
||||||
|
#wrap th {
|
||||||
padding: .3em .6em;
|
padding: .3em .6em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
td+td+td+td+td+td+td+td {
|
#wrap td+td+td+td+td+td+td+td {
|
||||||
font-family: var(--font-mono), monospace, monospace;
|
font-family: var(--font-mono), monospace, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
<span>min/hrs = time left</span>
|
<span>min/hrs = time left</span>
|
||||||
|
|
||||||
<table id="tab"><thead><tr>
|
<table id="tab"><thead><tr>
|
||||||
<th>delete</th>
|
|
||||||
<th>sharekey</th>
|
<th>sharekey</th>
|
||||||
|
<th>delete</th>
|
||||||
<th>pw</th>
|
<th>pw</th>
|
||||||
<th>source</th>
|
<th>source</th>
|
||||||
<th>axs</th>
|
<th>axs</th>
|
||||||
|
@ -37,10 +37,13 @@
|
||||||
</tr></thead><tbody>
|
</tr></thead><tbody>
|
||||||
{% for k, pw, vp, pr, st, un, t0, t1 in rows %}
|
{% for k, pw, vp, pr, st, un, t0, t1 in rows %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ r }}{{ shr }}{{ k }}?qr">qr</a>
|
||||||
|
<a href="{{ r }}{{ shr }}{{ k }}">{{ k }}</a>
|
||||||
|
</td>
|
||||||
<td><a href="#" k="{{ k }}">delete</a></td>
|
<td><a href="#" k="{{ k }}">delete</a></td>
|
||||||
<td><a href="{{ r }}{{ shr }}{{ k }}">{{ k }}</a></td>
|
<td>{{ "yes" if pw else "--" }}</td>
|
||||||
<td>{{ pw }}</td>
|
<td><a href="{{ r }}/{{ vp|e }}">/{{ vp|e }}</a></td>
|
||||||
<td><a href="{{ r }}/{{ vp|e }}">{{ vp|e }}</a></td>
|
|
||||||
<td>{{ pr }}</td>
|
<td>{{ pr }}</td>
|
||||||
<td>{{ st }}</td>
|
<td>{{ st }}</td>
|
||||||
<td>{{ un|e }}</td>
|
<td>{{ un|e }}</td>
|
||||||
|
|
|
@ -12,7 +12,7 @@ function rm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function bump() {
|
function bump() {
|
||||||
var k = this.closest('tr').getElementsByTagName('a')[0].getAttribute('k'),
|
var k = this.closest('tr').getElementsByTagName('a')[2].getAttribute('k'),
|
||||||
u = SR + shr + uricom_enc(k) + '?eshare=' + this.value,
|
u = SR + shr + uricom_enc(k) + '?eshare=' + this.value,
|
||||||
xhr = new XHR();
|
xhr = new XHR();
|
||||||
|
|
||||||
|
@ -28,14 +28,36 @@ function cb() {
|
||||||
document.location = '?shares';
|
document.location = '?shares';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function qr(e) {
|
||||||
|
ev(e);
|
||||||
|
var href = this.href,
|
||||||
|
pw = this.closest('tr').cells[2].textContent;
|
||||||
|
|
||||||
|
if (pw.indexOf('yes') < 0)
|
||||||
|
return showqr(href);
|
||||||
|
|
||||||
|
modal.prompt("if you want to bypass the password protection by\nembedding the password into the qr-code, then\ntype the password now, otherwise leave this empty", "", function (v) {
|
||||||
|
if (v)
|
||||||
|
href += "&pw=" + v;
|
||||||
|
showqr(href);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showqr(href) {
|
||||||
|
var vhref = href.replace('?qr&', '?').replace('?qr', '');
|
||||||
|
modal.alert(esc(vhref) + '<img class="b64" src="' + href + '" />');
|
||||||
|
}
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var tab = ebi('tab').tBodies[0],
|
var tab = ebi('tab').tBodies[0],
|
||||||
tr = Array.prototype.slice.call(tab.rows, 0);
|
tr = Array.prototype.slice.call(tab.rows, 0);
|
||||||
|
|
||||||
var buf = [];
|
var buf = [];
|
||||||
for (var a = 0; a < tr.length; a++)
|
for (var a = 0; a < tr.length; a++) {
|
||||||
|
tr[a].cells[0].getElementsByTagName('a')[0].onclick = qr;
|
||||||
for (var b = 7; b < 9; b++)
|
for (var b = 7; b < 9; b++)
|
||||||
buf.push(parseInt(tr[a].cells[b].innerHTML));
|
buf.push(parseInt(tr[a].cells[b].innerHTML));
|
||||||
|
}
|
||||||
|
|
||||||
var ibuf = 0;
|
var ibuf = 0;
|
||||||
for (var a = 0; a < tr.length; a++)
|
for (var a = 0; a < tr.length; a++)
|
||||||
|
|
|
@ -293,6 +293,12 @@ html.y #tth {
|
||||||
#modalc a {
|
#modalc a {
|
||||||
color: #07b;
|
color: #07b;
|
||||||
}
|
}
|
||||||
|
#modalc .b64 {
|
||||||
|
display: block;
|
||||||
|
margin: .1em auto;
|
||||||
|
width: 60%;
|
||||||
|
height: 60%;
|
||||||
|
}
|
||||||
#modalb {
|
#modalb {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
|
@ -426,7 +426,7 @@ var tl_browser = {
|
||||||
"fs_tsrc": "the file or folder to share",
|
"fs_tsrc": "the file or folder to share",
|
||||||
"fs_ppwd": "optional password",
|
"fs_ppwd": "optional password",
|
||||||
"fs_w8": "creating share...",
|
"fs_w8": "creating share...",
|
||||||
"fs_ok": "<h6>share-URL created</h6>\npress <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close\n\n",
|
"fs_ok": "press <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close",
|
||||||
|
|
||||||
"frt_dec": "may fix some cases of broken filenames\">url-decode",
|
"frt_dec": "may fix some cases of broken filenames\">url-decode",
|
||||||
"frt_rst": "reset modified filenames back to the original ones\">↺ reset",
|
"frt_rst": "reset modified filenames back to the original ones\">↺ reset",
|
||||||
|
|
Loading…
Reference in a new issue