From d1c3d72d677b9e6cf8a21d8f3405a1e16d97dba7 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 11:06:09 -0300 Subject: [PATCH 01/10] allow cancelling upload --- navigator/components/FileUpload.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/navigator/components/FileUpload.js b/navigator/components/FileUpload.js index f65139b..12273c9 100644 --- a/navigator/components/FileUpload.js +++ b/navigator/components/FileUpload.js @@ -58,6 +58,21 @@ export class FileUpload { header.classList.add("nav-notification-header"); notification.appendChild(header); header.innerText = "Uploading " + this.filename; + header.style.position = "relative"; + header.style.paddingRight = "1em"; + + var cancel = document.createElement("i"); + cancel.classList.add("fa", "fa-times"); + cancel.style.position = "absolute" + cancel.style.right = "0"; + cancel.style.cursor = "pointer"; + cancel.onclick = () => { + if (this.proc) { + this.reader.onload = () => {}; + this.done(); + } + } + header.appendChild(cancel); var info = document.createElement("div"); info.classList.add("flex-row", "space-between"); @@ -110,16 +125,12 @@ export class FileUpload { } async upload() { - if (await this.check_if_exists()) { - if (!await this.nav_window_ref.modal_prompt.confirm(this.filename + ": File exists. Replace?", "", true)) - return; - } this.make_html_element(); this.proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/write-chunks.py3", this.path], {err: "out", superuser: "try"}); this.proc.fail((e, data) => { this.reader.onload = () => {} this.done(); - this.nav_window_ref.modal_prompt.alert(data); + this.nav_window_ref.modal_prompt.alert(data, e); }) this.proc.done((data) => { this.nav_window_ref.refresh(); From f7753d2a0a3dd2b56e0194078473b3f02ca01af0 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 11:28:35 -0300 Subject: [PATCH 02/10] make button selected outline color match theme --- navigator/navigator.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/navigator/navigator.css b/navigator/navigator.css index eee3968..fd861d1 100644 --- a/navigator/navigator.css +++ b/navigator/navigator.css @@ -35,6 +35,7 @@ --nav-border-radius: 4px; --symlink-symbol-color: var(--navigation); --list-view-header: var(--selected); + --outline-color: black; } [data-theme="dark"] { @@ -53,6 +54,11 @@ --nav-entry-color: #555F6E; --symlink-symbol-color: var(--navigation); --list-view-header: var(--container); + --outline-color: white; +} + +button { + outline-color: var(--outline-color) !important; } html { From 45b20774fd5150040d0f73dba2279f2eed0afd94 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 11:30:36 -0300 Subject: [PATCH 03/10] set focus on buttons for alert and confirm --- navigator/components/ModalPrompt.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/navigator/components/ModalPrompt.js b/navigator/components/ModalPrompt.js index 5bc2eb2..109d96b 100644 --- a/navigator/components/ModalPrompt.js +++ b/navigator/components/ModalPrompt.js @@ -111,6 +111,7 @@ export class ModalPrompt { this.footer.innerHTML = ""; this.footer.appendChild(this.ok); this.show(); + this.ok.focus(); return new Promise((resolve, reject) => { this.ok.onclick = () => { resolve(); @@ -137,6 +138,10 @@ export class ModalPrompt { else this.yes.classList.add(primary_btn); this.show(); + if (danger) + this.no.focus(); + else + this.yes.focus(); return new Promise((resolve, reject) => { let resolve_true = () => { resolve(true); From d922bdfde0948efa3a7f7dbfe6755e078766a4a1 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 13:33:26 -0300 Subject: [PATCH 04/10] fix style of checkbox row so cursor is pointer --- navigator/components/ModalPrompt.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/navigator/components/ModalPrompt.js b/navigator/components/ModalPrompt.js index 109d96b..5f940d1 100644 --- a/navigator/components/ModalPrompt.js +++ b/navigator/components/ModalPrompt.js @@ -184,7 +184,7 @@ export class ModalPrompt { let label = document.createElement("label"); label.innerText = request.label; label.htmlFor = key; - label.style.marginRight = "1em"; + label.style.paddingRight = "1em"; label.style.flexBasis = "0"; label.style.flexGrow = "1"; let req = document.createElement("input"); @@ -201,6 +201,9 @@ export class ModalPrompt { case "text": req.style.flexGrow = "3"; break; + case "checkbox": + label.style.cursor = req.style.cursor = "pointer"; + break; default: break; } From a837129f0c953dbef411b48993cef5aad070e5e1 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 13:34:09 -0300 Subject: [PATCH 05/10] fix aborting upload for directories & empty files --- navigator/components/FileUpload.js | 12 +++- navigator/components/NavDragDrop.js | 104 ++++++++++++++++++---------- navigator/scripts/write-chunks.py3 | 19 ++--- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/navigator/components/FileUpload.js b/navigator/components/FileUpload.js index 12273c9..6497d59 100644 --- a/navigator/components/FileUpload.js +++ b/navigator/components/FileUpload.js @@ -19,6 +19,7 @@ import {NavWindow} from "./NavWindow.js"; import {format_time_remaining} from "../functions.js"; +import {ModalPrompt} from "./ModalPrompt.js"; export class FileUpload { /** @@ -40,6 +41,7 @@ export class FileUpload { this.chunks = this.slice_file(file); this.chunk_index = 0; this.timestamp = Date.now(); + this.modal_prompt = new ModalPrompt(); } check_if_exists() { @@ -130,7 +132,7 @@ export class FileUpload { this.proc.fail((e, data) => { this.reader.onload = () => {} this.done(); - this.nav_window_ref.modal_prompt.alert(data, e); + this.nav_window_ref.modal_prompt.alert(e, data); }) this.proc.done((data) => { this.nav_window_ref.refresh(); @@ -147,7 +149,13 @@ export class FileUpload { } }; })(this); - this.reader.readAsArrayBuffer(this.chunks[0]); + try { + this.reader.readAsArrayBuffer(this.chunks[0]); + } catch { + this.reader.onload = () => {}; + this.done(); + this.modal_prompt.alert("Failed to read file: " + this.filename, "Upload of directories and empty files not supported."); + } } arrayBufferToBase64(buffer) { diff --git a/navigator/components/NavDragDrop.js b/navigator/components/NavDragDrop.js index 3ffc75f..7c6454a 100644 --- a/navigator/components/NavDragDrop.js +++ b/navigator/components/NavDragDrop.js @@ -27,16 +27,17 @@ export class NavDragDrop { * @param {NavWindow} nav_window_ref */ constructor(drop_area, nav_window_ref) { - drop_area.addEventListener("dragenter", this); - drop_area.addEventListener("dragover", this); - drop_area.addEventListener("dragleave", this); - drop_area.addEventListener("drop", this); + drop_area.addEventListener("dragenter", this, false); + drop_area.addEventListener("dragover", this, false); + drop_area.addEventListener("dragleave", this, false); + drop_area.addEventListener("drop", this, false); this.drop_area = drop_area; this.nav_window_ref = nav_window_ref; } - - handleEvent(e) { + + async handleEvent(e) { e.preventDefault(); + e.stopPropagation(); switch(e.type){ case "dragenter": this.drop_area.classList.add("drag-enter"); @@ -47,45 +48,72 @@ export class NavDragDrop { this.drop_area.classList.remove("drag-enter"); break; case "drop": - if (e.dataTransfer.items) { - for (let item of e.dataTransfer.items) { - if (item.kind === 'file') { - var file = item.getAsFile(); - if (file.type === "" && file.size !== 0) { - this.nav_window_ref.modal_prompt.alert(file.name + ": Cannot upload folders."); - continue; - } - if (file.size === 0) { - var proc = cockpit.spawn( - ["/usr/share/cockpit/navigator/scripts/touch.py3", this.nav_window_ref.pwd().path_str() + "/" + file.name], - {superuser: "try", err: "out"} - ); - proc.done(() => { - this.nav_window_ref.refresh(); - }); - proc.fail((e, data) => { - this.nav_window_ref.modal_prompt.alert(data); - }); - } else { - var uploader = new FileUpload(file, this.nav_window_ref); - uploader.upload(); - } - } + let uploads = []; + // console.log(e); + // console.log(e.dataTransfer.files); + // if (e.dataTransfer.items) { + // for (let item of e.dataTransfer.items) { + // if (item.kind === 'file') { + // var file = item.getAsFile(); + // if (file.type === "" && file.size !== 0) { + // await this.nav_window_ref.modal_prompt.alert(file.name + ": Cannot upload folders."); + // continue; + // } + // if (file.size === 0) { + // var proc = cockpit.spawn( + // ["/usr/share/cockpit/navigator/scripts/touch.py3", this.nav_window_ref.pwd().path_str() + "/" + file.name], + // {superuser: "try", err: "out"} + // ); + // proc.done(() => { + // this.nav_window_ref.refresh(); + // }); + // } else { + // uploads.push(new FileUpload(file, this.nav_window_ref)); + // } + // } + // } + // } else { + for (let file of e.dataTransfer.files) { + console.log(file); + // if (file.type === "") + // continue; + uploads.push(new FileUpload(file, this.nav_window_ref)); } - } else { - for (let file of ev.dataTransfer.files) { - if (file.type === "") - continue; - var uploader = new FileUpload(file, this.nav_window_ref); - uploader.upload(); + // } + this.drop_area.classList.remove("drag-enter"); + if (uploads.length === 0) + break; + let keepers = []; + let requests = {}; + for (let upload of uploads) { + if (!await upload.check_if_exists()) { + keepers.push(upload.filename); + continue; + } + let request = {}; + request.label = upload.filename; + request.type = "checkbox"; + let id = upload.filename; + requests[id] = request; + } + if (Object.keys(requests).length > 0) { + let responses = await this.nav_window_ref.modal_prompt.prompt( + "Conflicts found while uploading. Replace?", + requests + ) + if (responses === null) + break; + for (let key of Object.keys(responses)) { + if (responses[key]) + keepers.push(key); } } - this.drop_area.classList.remove("drag-enter"); + uploads = uploads.filter((upload) => {return keepers.includes(upload.filename)}); + uploads.forEach((upload) => {upload.upload()}); break; default: this.drop_area.classList.remove("drag-enter"); break; } - e.stopPropagation(); } } diff --git a/navigator/scripts/write-chunks.py3 b/navigator/scripts/write-chunks.py3 index 8beaf37..36494cf 100755 --- a/navigator/scripts/write-chunks.py3 +++ b/navigator/scripts/write-chunks.py3 @@ -32,27 +32,29 @@ import sys import json def write_chunk(chunk, file): + if not file: + path = sys.argv[1] + try: + file = open(path, "wb") + except Exception as e: + print(e) + sys.exit(1) seek = chunk["seek"] data = base64.b64decode(chunk["chunk"]) file.seek(seek) file.write(data) def main(): + file = None if len(sys.argv) != 2: print("Invalid number of arguments.") sys.exit(1) - path = sys.argv[1] - try: - file = open(path, "wb") - except Exception as e: - print(e) - sys.exit(1) while True: try: json_in = input() except EOFError: break - json_list = json_in.split("\n") + json_list = json_in.split("\n") # need to split in case writes happen faster than reads for json_obj in json_list: try: obj_in = json.loads(json_obj) @@ -63,7 +65,8 @@ def main(): log.close() sys.exit(1) write_chunk(obj_in, file) - file.close() + if file: + file.close() sys.exit(0) if __name__ == "__main__": From 913461feee970f9b80bbc683f976a1a70526ab04 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 16:49:26 -0300 Subject: [PATCH 06/10] create parent directories if they don't exist --- navigator/scripts/write-chunks.py3 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/navigator/scripts/write-chunks.py3 b/navigator/scripts/write-chunks.py3 index 36494cf..3ccb8df 100755 --- a/navigator/scripts/write-chunks.py3 +++ b/navigator/scripts/write-chunks.py3 @@ -34,6 +34,12 @@ import json def write_chunk(chunk, file): if not file: path = sys.argv[1] + parent_path = os.path.dirname(path) + if not os.path.exists(parent_path): + os.makedirs(parent_path, exist_ok=True) + elif os.path.isfile(parent_path): + print(parent_path + ": exists and is not a directory.") + sys.exit(1) try: file = open(path, "wb") except Exception as e: From 7d6490cd15f44db04713a967f847c42e25b0d86a Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 16:50:05 -0300 Subject: [PATCH 07/10] use webkit file uploading to allow directories --- navigator/components/NavDragDrop.js | 172 ++++++++++++++++++---------- 1 file changed, 114 insertions(+), 58 deletions(-) diff --git a/navigator/components/NavDragDrop.js b/navigator/components/NavDragDrop.js index 7c6454a..4b72dfb 100644 --- a/navigator/components/NavDragDrop.js +++ b/navigator/components/NavDragDrop.js @@ -18,6 +18,7 @@ */ import {FileUpload} from "./FileUpload.js"; +import { ModalPrompt } from "./ModalPrompt.js"; import {NavWindow} from "./NavWindow.js"; export class NavDragDrop { @@ -33,82 +34,137 @@ export class NavDragDrop { drop_area.addEventListener("drop", this, false); this.drop_area = drop_area; this.nav_window_ref = nav_window_ref; + this.modal_prompt = new ModalPrompt(); + } + + /** + * + * @param {FileSystemEntry} item + * @param {string} path + * @returns {Promise} + */ + async scan_files(item, path) { + let new_uploads = []; + if (item.isDirectory) { + if (!path && !await this.modal_prompt.confirm(`Copy whole directory: ${item.fullPath}?`, "", true)) + return new_uploads; + let directoryReader = item.createReader(); + let promise = new Promise((resolve, reject) => { + directoryReader.readEntries(async (entries) => { + for (const entry of entries) { + new_uploads.push(... await this.scan_files(entry, path + item.name + "/")); + } + resolve(); + }); + }) + await promise; + } else { + let promise = new Promise((resolve, reject) => { + item.file((file) => { + resolve(file); + }) + }); + new_uploads.push(new FileUpload(await promise, this.nav_window_ref, path)); + } + return new_uploads; + } + + /** + * + * @param {DataTransferItemList} items + * @returns {Promise} + */ + handle_drop_advanced(items) { + return new Promise(async (resolve, reject) => { + let uploads = []; + for (let i = 0; i < items.length; i++) { + let item = items[i]?.webkitGetAsEntry?.() ?? items[i]?.getAsEntry?.() ?? null; + let path = ""; + if (item) { + let new_uploads = await this.scan_files(item, path); + console.log(new_uploads); + uploads.push(... new_uploads); + } else { + reject(); + } + } + resolve(uploads); + }) + } + + /** + * + * @param {FileUpload[]} uploads + * @returns {FileUpload[]} + */ + async handle_conflicts(uploads) { + let keepers = []; + let requests = {}; + for (let upload of uploads) { + if (!await upload.check_if_exists()) { + keepers.push(upload.filename); + continue; + } + let request = {}; + request.label = upload.filename; + request.type = "checkbox"; + let id = upload.filename; + requests[id] = request; + } + if (Object.keys(requests).length > 0) { + let responses = await this.nav_window_ref.modal_prompt.prompt( + "Conflicts found while uploading. Replace?", + requests + ) + if (responses === null) + return null; + for (let key of Object.keys(responses)) { + if (responses[key]) + keepers.push(key); + } + } + return uploads.filter((upload) => keepers.includes(upload.filename)); } + /** + * + * @param {Event} e + */ async handleEvent(e) { - e.preventDefault(); - e.stopPropagation(); switch(e.type){ case "dragenter": + e.preventDefault(); + e.stopPropagation(); this.drop_area.classList.add("drag-enter"); break; case "dragover": + e.preventDefault(); + e.stopPropagation(); break; case "dragleave": + e.preventDefault(); + e.stopPropagation(); this.drop_area.classList.remove("drag-enter"); break; case "drop": - let uploads = []; - // console.log(e); - // console.log(e.dataTransfer.files); - // if (e.dataTransfer.items) { - // for (let item of e.dataTransfer.items) { - // if (item.kind === 'file') { - // var file = item.getAsFile(); - // if (file.type === "" && file.size !== 0) { - // await this.nav_window_ref.modal_prompt.alert(file.name + ": Cannot upload folders."); - // continue; - // } - // if (file.size === 0) { - // var proc = cockpit.spawn( - // ["/usr/share/cockpit/navigator/scripts/touch.py3", this.nav_window_ref.pwd().path_str() + "/" + file.name], - // {superuser: "try", err: "out"} - // ); - // proc.done(() => { - // this.nav_window_ref.refresh(); - // }); - // } else { - // uploads.push(new FileUpload(file, this.nav_window_ref)); - // } - // } - // } - // } else { + let uploads; + let items = e.dataTransfer.items; + e.preventDefault(); + e.stopPropagation(); + try { + uploads = await this.handle_drop_advanced(items); + } catch { + uploads = []; for (let file of e.dataTransfer.files) { - console.log(file); - // if (file.type === "") - // continue; - uploads.push(new FileUpload(file, this.nav_window_ref)); + let uploader = new FileUpload(file, this.nav_window_ref); + uploader.using_webkit = false; + uploads.push(uploader); } - // } + } this.drop_area.classList.remove("drag-enter"); if (uploads.length === 0) break; - let keepers = []; - let requests = {}; - for (let upload of uploads) { - if (!await upload.check_if_exists()) { - keepers.push(upload.filename); - continue; - } - let request = {}; - request.label = upload.filename; - request.type = "checkbox"; - let id = upload.filename; - requests[id] = request; - } - if (Object.keys(requests).length > 0) { - let responses = await this.nav_window_ref.modal_prompt.prompt( - "Conflicts found while uploading. Replace?", - requests - ) - if (responses === null) - break; - for (let key of Object.keys(responses)) { - if (responses[key]) - keepers.push(key); - } - } - uploads = uploads.filter((upload) => {return keepers.includes(upload.filename)}); + uploads = await this.handle_conflicts(uploads); uploads.forEach((upload) => {upload.upload()}); break; default: From abc082a9c99a392fbd92a6a488a240d8d6ba3cef Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 16:50:49 -0300 Subject: [PATCH 08/10] support empty files and fail on directories --- navigator/components/FileUpload.js | 47 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/navigator/components/FileUpload.js b/navigator/components/FileUpload.js index 6497d59..c27bf75 100644 --- a/navigator/components/FileUpload.js +++ b/navigator/components/FileUpload.js @@ -26,22 +26,24 @@ export class FileUpload { * * @param {File|Blob} file * @param {NavWindow} nav_window_ref + * @param {string|undefined} path_prefix */ - constructor(file, nav_window_ref) { + constructor(file, nav_window_ref, path_prefix = "") { try { this.chunk_size = (parseInt(cockpit.info.version) > 238)? 1048576 : 65536; } catch(e) { console.log(e); this.chunk_size = 65536; } - this.filename = file.name; + this.filename = path_prefix + file.name; this.nav_window_ref = nav_window_ref; - this.path = nav_window_ref.pwd().path_str() + "/" + file.name; + this.path = nav_window_ref.pwd().path_str() + "/" + this.filename; this.reader = new FileReader(); this.chunks = this.slice_file(file); this.chunk_index = 0; this.timestamp = Date.now(); this.modal_prompt = new ModalPrompt(); + this.using_webkit = true; } check_if_exists() { @@ -111,7 +113,7 @@ export class FileUpload { /** * * @param {File|Blob} file - * @returns {Array} + * @returns {Blob[]} */ slice_file(file) { var offset = 0; @@ -137,27 +139,38 @@ export class FileUpload { this.proc.done((data) => { this.nav_window_ref.refresh(); }) - this.reader.onload = (function(uploader_ref) { - return async function(evt) { - uploader_ref.write_to_file(evt, uploader_ref.chunk_index * uploader_ref.chunk_size); - uploader_ref.chunk_index++; - uploader_ref.progress.value = uploader_ref.chunk_index; - if (uploader_ref.chunk_index < uploader_ref.num_chunks) - uploader_ref.reader.readAsArrayBuffer(uploader_ref.chunks[uploader_ref.chunk_index]); - else { - uploader_ref.done(); - } - }; - })(this); + this.reader.onerror = (evt) => { + this.modal_prompt.alert("Failed to read file: " + this.filename, "Upload of directories not supported."); + this.done(); + } + this.reader.onload = (evt) => { + this.write_to_file(evt, this.chunk_index * this.chunk_size); + this.chunk_index++; + this.progress.value = this.chunk_index; + if (this.chunk_index < this.num_chunks) + this.reader.readAsArrayBuffer(this.chunks[this.chunk_index]); + else { + this.done(); + } + }; try { this.reader.readAsArrayBuffer(this.chunks[0]); } catch { this.reader.onload = () => {}; + if (this.using_webkit) { + this.proc.input(JSON.stringify({seek: 0, chunk: ""}), true); + } else { + this.modal_prompt.alert("Failed to read file: " + this.filename, "Upload of directories and empty files not supported."); + } this.done(); - this.modal_prompt.alert("Failed to read file: " + this.filename, "Upload of directories and empty files not supported."); } } + /** + * + * @param {ArrayBuffer} buffer + * @returns + */ arrayBufferToBase64(buffer) { let binary = ''; let bytes = new Uint8Array(buffer); From 16f310e6d6439eb29b7a7ec4d36af67aa571b512 Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 16:57:50 -0300 Subject: [PATCH 09/10] prepare for packaging --- CHANGELOG.md | 6 +++--- manifest.json | 4 ++-- packaging/el7/main.spec | 3 +++ packaging/el8/main.spec | 3 +++ packaging/focal/changelog | 7 +++++++ 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e41764b..caf37f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Cockpit Navigator 0.5.1-1 +## Cockpit Navigator 0.5.2-1 -* Allow modal popups to scroll if overflowing past page. -* Moves focus to next input in modal popup when enter is pressed. \ No newline at end of file +* Implement uploading of entire directories. +* Add cancel option to in-progress file uploads. \ No newline at end of file diff --git a/manifest.json b/manifest.json index fb36ddc..cda581c 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "cockpit-navigator", "title": "Cockpit Navigator", "prerelease": false, - "version": "0.5.1", + "version": "0.5.2", "buildVersion": "1", "author": "Josh Boudreau ", "url": "https://github.com/45Drives/cockpit-navigator", @@ -54,7 +54,7 @@ ], "changelog": { "urgency": "medium", - "version": "0.5.1", + "version": "0.5.2", "buildVersion": "1", "ignore": [], "date": null, diff --git a/packaging/el7/main.spec b/packaging/el7/main.spec index f0811a7..82e65a8 100644 --- a/packaging/el7/main.spec +++ b/packaging/el7/main.spec @@ -32,6 +32,9 @@ rm -rf %{buildroot} /usr/share/cockpit/navigator/* %changelog +* Fri Jul 16 2021 Josh Boudreau 0.5.2-1 +- Implement uploading of entire directories. +- Add cancel option to in-progress file uploads. * Thu Jul 15 2021 Josh Boudreau 0.5.1-1 - Allow modal popups to scroll if overflowing past page. - Moves focus to next input in modal popup when enter is pressed. diff --git a/packaging/el8/main.spec b/packaging/el8/main.spec index f0811a7..82e65a8 100644 --- a/packaging/el8/main.spec +++ b/packaging/el8/main.spec @@ -32,6 +32,9 @@ rm -rf %{buildroot} /usr/share/cockpit/navigator/* %changelog +* Fri Jul 16 2021 Josh Boudreau 0.5.2-1 +- Implement uploading of entire directories. +- Add cancel option to in-progress file uploads. * Thu Jul 15 2021 Josh Boudreau 0.5.1-1 - Allow modal popups to scroll if overflowing past page. - Moves focus to next input in modal popup when enter is pressed. diff --git a/packaging/focal/changelog b/packaging/focal/changelog index b9508fd..09e4eb4 100644 --- a/packaging/focal/changelog +++ b/packaging/focal/changelog @@ -1,3 +1,10 @@ +cockpit-navigator (0.5.2-1focal) focal; urgency=medium + + * Implement uploading of entire directories. + * Add cancel option to in-progress file uploads. + + -- Josh Boudreau Fri, 16 Jul 2021 13:56:55 -0300 + cockpit-navigator (0.5.1-1focal) focal; urgency=medium * Allow modal popups to scroll if overflowing past page. From 58383c64265160203f9fc91cc03b7878d6011d3f Mon Sep 17 00:00:00 2001 From: joshuaboud Date: Fri, 16 Jul 2021 17:03:24 -0300 Subject: [PATCH 10/10] update install URLs --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 45c420c..5f98d48 100644 --- a/README.md +++ b/README.md @@ -23,17 +23,17 @@ With no command line use needed, you can: # Installation ## From Github Release ### Ubuntu -1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.1/cockpit-navigator_0.5.1-1focal_all.deb` -1. `# apt install ./cockpit-navigator_0.5.1-1focal_all.deb` +1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.2/cockpit-navigator_0.5.2-1focal_all.deb` +1. `# apt install ./cockpit-navigator_0.5.2-1focal_all.deb` ### EL7 -1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.1/cockpit-navigator-0.5.1-1.el7.noarch.rpm` +1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.2/cockpit-navigator-0.5.2-1.el7.noarch.rpm` ### EL8 -1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.1/cockpit-navigator-0.5.1-1.el8.noarch.rpm` +1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.2/cockpit-navigator-0.5.2-1.el8.noarch.rpm` ## From Source 1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`, `zip`. 1. `$ git clone https://github.com/45Drives/cockpit-navigator.git` 1. `$ cd cockpit-navigator` -1. `$ git checkout ` (v0.5.1 is latest) +1. `$ git checkout ` (v0.5.2 is latest) 1. `# make install` ## From 45Drives Repositories ### Ubuntu