From 45f52c54599d7c972a9d5a8def048c5a39703d0a Mon Sep 17 00:00:00 2001 From: bohwaz Date: Tue, 22 Nov 2022 04:05:07 +0100 Subject: [PATCH] Update dependencies --- README.md | 1 + lib/KD2/WebDAV/Server.php | 2 +- www/webdav.css | 21 ++++++- www/webdav.js | 124 ++++++++++++++++++++++++++++++++------ 4 files changed, 125 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9c9337a..3296b94 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Its original purpose was to serve as a demo and test for the KD2 WebDAV library, * MarkDown live preview * Preview of images, text, MarkDown and PDF * Editing of Office files using Collabora or OnlyOffice + * Download all files from a directory * WebDAV class 1, 2, 3 support, support for Etags * No database server is required (SQLite3 is used) * Multiple user accounts diff --git a/lib/KD2/WebDAV/Server.php b/lib/KD2/WebDAV/Server.php index 1de4d2f..1d6d528 100644 --- a/lib/KD2/WebDAV/Server.php +++ b/lib/KD2/WebDAV/Server.php @@ -789,7 +789,7 @@ class Server $uri = trim(rtrim($this->base_uri, '/') . '/' . ltrim($uri, '/'), '/'); $path = '/' . str_replace('%2F', '/', rawurlencode($uri)); - if (($item['DAV::resourcetype'] ?? null) == 'collection') { + if (($item['DAV::resourcetype'] ?? null) == 'collection' && $path != '/') { $path .= '/'; } diff --git a/www/webdav.css b/www/webdav.css index 89450a5..5b1de18 100644 --- a/www/webdav.css +++ b/www/webdav.css @@ -309,11 +309,12 @@ input[name=rename], input[name=paste_name] { width: 0; height: 0; border: none; - display: flex; + align-items: center; + justify-content: center; opacity: 0; } -.loading .bg::after { +.loading .bg::after, .spinner span::after { display: block; content: " "; width: 70px; @@ -336,6 +337,17 @@ input[name=rename], input[name=paste_name] { position: absolute; } +.spinner { + align-items: center; + justify-content: center; + display: flex; +} + +.spinner span::after { + width: 30px; + height: 30px; +} + .loading .bg, .dragging .bg, .dialog .bg { backdrop-filter: blur(5px); background: rgba(0, 0, 0, 0.5); @@ -348,6 +360,11 @@ dialog { transition: all .3s; } +progress { + height: 2em; + width: 90%; +} + @keyframes spin { to { transform: rotate(360deg); } } @media screen and (max-width: 800px) { diff --git a/www/webdav.js b/www/webdav.js index 1b3d6c9..3d3e3a7 100644 --- a/www/webdav.js +++ b/www/webdav.js @@ -38,6 +38,7 @@ const WebDAVNavigator = (url, options) => { + %table%
`; @@ -47,7 +48,7 @@ const WebDAVNavigator = (url, options) => { `; const dir_row_tpl = `%icon%%name%%modified%
`; - const file_row_tpl = `%icon%%name%%size%%modified%
${_('Download')}
`; + const file_row_tpl = `%icon%%name%%size_bytes%%modified%
${_('Download')}
`; const propfind_tpl = ` @@ -97,17 +98,6 @@ const WebDAVNavigator = (url, options) => { return false; }; - const get_url = async (url) => { - if (temp_object_url) { - window.URL.revokeObjectURL(temp_object_url); - } - - return req('GET', url).then(r => r.blob()).then(blob => { - temp_object_url = window.URL.createObjectURL(blob); - return temp_object_url; - }); - } - const req = (method, url, body, headers) => { if (!headers) { headers = {}; @@ -120,6 +110,52 @@ const WebDAVNavigator = (url, options) => { return fetch(url, {method, body, headers}); }; + const xhr = (method, url, progress_callback) => { + var xhr = new XMLHttpRequest(); + current_xhr = xhr; + xhr.responseType = 'blob'; + var p = new Promise((resolve, reject) => { + xhr.open(method, url); + xhr.onload = function () { + if (this.status >= 200 && this.status < 300) { + resolve(xhr.response); + } else { + reject({ + status: this.status, + statusText: xhr.statusText + }); + } + }; + xhr.onerror = function () { + reject({ + status: this.status, + statusText: xhr.statusText + }); + }; + xhr.onprogress = progress_callback; + xhr.send(); + }); + return p; + }; + + const get_url = async (url) => { + var progress = (e) => { + var p = $('progress'); + if (!p || e.loaded <= 0) return; + p.value = e.loaded; + $('.progress_bytes').innerHTML = formatBytes(e.loaded); + }; + + if (temp_object_url) { + window.URL.revokeObjectURL(temp_object_url); + } + + return await xhr('GET', url, progress).then(blob => { + temp_object_url = window.URL.createObjectURL(blob); + return temp_object_url; + }); + }; + const wopi_init = async () => { if (!wopi_discovery_url) { return; @@ -221,6 +257,17 @@ const WebDAVNavigator = (url, options) => { }; const closeDialog = (e) => { + if (!$('body').classList.contains('dialog')) { + return; + } + + if (current_xhr) { + current_xhr.abort(); + current_xhr = null; + } + + window.onbeforeunload = null; + $('body').classList.remove('dialog'); if (!$('dialog')) return; $('dialog').remove(); @@ -228,16 +275,43 @@ const WebDAVNavigator = (url, options) => { evt = null; }; - const download = async (name, url) => { - var url = await get_url(url); + const download = async (name, size, url) => { + window.onbeforeunload = () => { + if (current_xhr) { + current_xhr.abort(); + } + + return true; + }; + + openDialog(`

+

${html(name)}

+ +

/ ${formatBytes(size)}

`, false); + + await get_url(url); const a = document.createElement('a'); a.style.display = 'none'; - a.href = url; + a.href = temp_object_url; a.download = name; document.body.appendChild(a); a.click(); - window.URL.revokeObjectURL(url); + window.URL.revokeObjectURL(temp_object_url); a.remove(); + + closeDialog(); + window.onbeforeunload = null; + }; + + const download_all = async () => { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.is_dir) { + continue; + } + + await download(item.name, item.size, item.uri) + } }; const preview = (type, url) => { @@ -354,7 +428,7 @@ const WebDAVNavigator = (url, options) => { const buildListing = (uri, xml) => { uri = normalizeURL(uri); - var items = [[], []]; + items = [[], []]; var title = null; var root_permissions = null; @@ -442,7 +516,7 @@ const WebDAVNavigator = (url, options) => { } var row = item.is_dir ? dir_row_tpl : file_row_tpl; - item.size = item.size !== null ? formatBytes(item.size).replace(/ /g, ' ') : null; + item.size_bytes = item.size !== null ? formatBytes(item.size).replace(/ /g, ' ') : null; item.icon = item.is_dir ? '📁' : (item.uri.indexOf('.') > 0 ? item.uri.replace(/^.*\.(\w+)$/, '$1').toUpperCase() : ''); item.modified = item.modified !== null ? formatDate(item.modified) : null; item.name = html(item.name); @@ -460,6 +534,13 @@ const WebDAVNavigator = (url, options) => { reloadListing(); }; + if (!items.length) { + $('.download_all').disabled = true; + } + else { + $('.download_all').onclick = download_all; + } + if (!root_permissions || root_permissions.indexOf('CK') != -1) { $('.upload').insertAdjacentHTML('afterbegin', create_buttons); @@ -518,6 +599,7 @@ const WebDAVNavigator = (url, options) => { var mime = !dir ? tr.getAttribute('data-mime') : 'dir'; var buttons = $$('td.buttons div'); var permissions = tr.getAttribute('data-permissions'); + var size = tr.getAttribute('data-size'); if (permissions == 'null') { permissions = null; @@ -541,7 +623,7 @@ const WebDAVNavigator = (url, options) => { // This is to get around CORS when not on the same domain if (user && password && (a = tr.querySelector('a[download]'))) { a.onclick = () => { - download(file_name, url); + download(file_name, size, url); return false; }; } @@ -615,7 +697,7 @@ const WebDAVNavigator = (url, options) => { $$('a').onclick = () => { wopi_open(file_url, view_url); return false; }; } else if (user && password && !dir) { - $$('a').onclick = () => { download(file_name, file_url); return false; }; + $$('a').onclick = () => { download(file_name, size, file_url); return false; }; } else { $$('a').download = file_name; @@ -670,6 +752,8 @@ const WebDAVNavigator = (url, options) => { }); }; + var items = [[], []]; + var current_xhr = null; var current_url = url; var base_url = url; const user = options.user || null;