mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-07-30 17:15:16 +02:00
commit
f4d6f3e29f
10
README.md
10
README.md
@ -19,17 +19,17 @@ With no command line use needed, you can:
|
|||||||
# Installation
|
# Installation
|
||||||
## From Github Release
|
## From Github Release
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.3/cockpit-navigator_0.2.3-1focal_all.deb`
|
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.3.0/cockpit-navigator_0.3.0-1focal_all.deb`
|
||||||
1. `# apt install ./cockpit-navigator_0.2.3-1focal_all.deb`
|
1. `# apt install ./cockpit-navigator_0.3.0-1focal_all.deb`
|
||||||
### EL7
|
### EL7
|
||||||
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.3/cockpit-navigator-0.2.3-1.el7.noarch.rpm`
|
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.3.0/cockpit-navigator-0.3.0-1.el7.noarch.rpm`
|
||||||
### EL8
|
### EL8
|
||||||
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.3/cockpit-navigator-0.2.3-1.el8.noarch.rpm`
|
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.3.0/cockpit-navigator-0.3.0-1.el8.noarch.rpm`
|
||||||
## From Source
|
## From Source
|
||||||
1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`.
|
1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`.
|
||||||
1. `$ git clone https://github.com/45Drives/cockpit-navigator.git`
|
1. `$ git clone https://github.com/45Drives/cockpit-navigator.git`
|
||||||
1. `$ cd cockpit-navigator`
|
1. `$ cd cockpit-navigator`
|
||||||
1. `$ git checkout <version>` (v0.2.3 is latest)
|
1. `$ git checkout <version>` (v0.3.0 is latest)
|
||||||
1. `# make install`
|
1. `# make install`
|
||||||
## From 45Drives Repositories
|
## From 45Drives Repositories
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
|
8
debian/changelog
vendored
8
debian/changelog
vendored
@ -1,3 +1,11 @@
|
|||||||
|
cockpit-navigator (0.3.0-1focal) focal; urgency=medium
|
||||||
|
|
||||||
|
* Add drag and drop uploading of files.
|
||||||
|
* Add event listeners for ctrl+a to select all, ctrl+x to cut,
|
||||||
|
ctrl+c to copy, ctrl+v to paste, and delete to remove a file.
|
||||||
|
|
||||||
|
-- Josh Boudreau <jboudreau@45drives.com> Thu, 03 Jun 2021 16:33:00 -0300
|
||||||
|
|
||||||
cockpit-navigator (0.2.3-1focal) focal; urgency=medium
|
cockpit-navigator (0.2.3-1focal) focal; urgency=medium
|
||||||
|
|
||||||
* Fix closing contextmenu in el7.
|
* Fix closing contextmenu in el7.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Name: cockpit-navigator
|
Name: cockpit-navigator
|
||||||
Version: 0.2.3
|
Version: 0.3.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A File System Browser for Cockpit.
|
Summary: A File System Browser for Cockpit.
|
||||||
License: GPL-3.0+
|
License: GPL-3.0+
|
||||||
@ -32,6 +32,10 @@ rm -rf %{buildroot}
|
|||||||
/usr/share/cockpit/navigator/*
|
/usr/share/cockpit/navigator/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Jun 03 2021 Josh Boudreau <jboudreau@45drives.com> 0.3.0-1
|
||||||
|
- Add drag and drop uploading of files.
|
||||||
|
- Add event listeners for ctrl+a to select all, ctrl+x to cut,
|
||||||
|
ctrl+c to copy, ctrl+v to paste, and delete to remove a file.
|
||||||
* Wed Jun 02 2021 Josh Boudreau <jboudreau@45drives.com> 0.2.3-1
|
* Wed Jun 02 2021 Josh Boudreau <jboudreau@45drives.com> 0.2.3-1
|
||||||
- Fix closing contextmenu in el7.
|
- Fix closing contextmenu in el7.
|
||||||
- Hide rename in right click menu with multiple selected entries.
|
- Hide rename in right click menu with multiple selected entries.
|
||||||
|
@ -207,6 +207,7 @@ input[type="text"] {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
@ -486,3 +487,45 @@ input:checked + .slider:before {
|
|||||||
.nav-context-menu-item:hover {
|
.nav-context-menu-item:hover {
|
||||||
background-color: var(--border);
|
background-color: var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drag-enter {
|
||||||
|
border: 1px dashed var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-notifications {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column-reverse nowrap;
|
||||||
|
align-items: stretch;
|
||||||
|
max-height: 50%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-notification {
|
||||||
|
margin: 5px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
align-items: stretch;
|
||||||
|
z-index: 10;
|
||||||
|
flex-grow: 0;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: var(--container);
|
||||||
|
border-radius: var(--nav-border-radius);
|
||||||
|
color: var(--font);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-notification-header {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-notification-header > progress {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="vertical-spacer"></div>
|
<div class="vertical-spacer"></div>
|
||||||
<div class="flex-row inner-container">
|
<div class="flex-row inner-container">
|
||||||
<div class="contents-view" id="nav-contents-view"></div>
|
<div class="contents-view" id="nav-contents-view">
|
||||||
|
<div class="nav-notifications" id="nav-notifications">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="edit-file-contents nav-hidden" id="nav-edit-contents-view">
|
<div class="edit-file-contents nav-hidden" id="nav-edit-contents-view">
|
||||||
<div class="editor-header" id="nav-edit-contents-header"></div>
|
<div class="editor-header" id="nav-edit-contents-header"></div>
|
||||||
<div class="vertical-spacer"></div>
|
<div class="vertical-spacer"></div>
|
||||||
@ -150,7 +153,7 @@
|
|||||||
<span id="nav-num-dirs">-</span> Directories, <span id="nav-num-files">-</span> Files (<span id="nav-num-bytes">-</span>)
|
<span id="nav-num-dirs">-</span> Directories, <span id="nav-num-files">-</span> Files (<span id="nav-num-bytes">-</span>)
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow"></div>
|
<div class="flex-grow"></div>
|
||||||
<a href="https://45drives.com" target="_blank" title="Visit 45Drives.com">
|
<a href="https://www.45drives.com/?utm_source=Houston&utm_medium=UI&utm_campaign=OS-Link" target="_blank" title="Visit 45Drives.com">
|
||||||
<img src="branding/logo-light.svg" id="logo-45d"><span class="logo-45">45</span><span class="logo-drives">Drives</span>
|
<img src="branding/logo-light.svg" id="logo-45d"><span class="logo-45">45</span><span class="logo-drives">Drives</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="flex-grow"></div>
|
<div class="flex-grow"></div>
|
||||||
|
@ -164,6 +164,8 @@ class NavEntry {
|
|||||||
this.dom_element.addEventListener("click", this);
|
this.dom_element.addEventListener("click", this);
|
||||||
this.dom_element.addEventListener("contextmenu", this);
|
this.dom_element.addEventListener("contextmenu", this);
|
||||||
this.is_hidden_file = this.filename().startsWith('.');
|
this.is_hidden_file = this.filename().startsWith('.');
|
||||||
|
if (this.is_hidden_file)
|
||||||
|
icon.style.opacity = 0.5;
|
||||||
this.dom_element.title = this.filename();
|
this.dom_element.title = this.filename();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -757,22 +759,15 @@ class NavContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cut() {
|
cut() {
|
||||||
this.nav_window_ref.clip_board = [...this.nav_window_ref.selected_entries];
|
this.nav_window_ref.cut();
|
||||||
this.nav_window_ref.copy_or_move = "move";
|
|
||||||
this.nav_window_ref.paste_cwd = this.nav_window_ref.pwd().path_str();
|
|
||||||
this.menu_options["paste"].hidden = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copy() {
|
copy() {
|
||||||
this.nav_window_ref.clip_board = [...this.nav_window_ref.selected_entries];
|
this.nav_window_ref.copy();
|
||||||
this.nav_window_ref.copy_or_move = "copy";
|
|
||||||
this.nav_window_ref.paste_cwd = this.nav_window_ref.pwd().path_str();
|
|
||||||
this.menu_options["paste"].hidden = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paste() {
|
paste() {
|
||||||
this.nav_window_ref.paste_clipboard();
|
this.nav_window_ref.paste();
|
||||||
this.hide_paste();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rename() {
|
rename() {
|
||||||
@ -809,18 +804,18 @@ class NavContextMenu {
|
|||||||
this.nav_window_ref.set_selected(target, false, false);
|
this.nav_window_ref.set_selected(target, false, false);
|
||||||
}
|
}
|
||||||
if (target === this.nav_window_ref.pwd()) {
|
if (target === this.nav_window_ref.pwd()) {
|
||||||
this.menu_options["copy"].hidden = true;
|
this.menu_options["copy"].style.display = "none";
|
||||||
this.menu_options["cut"].hidden = true;
|
this.menu_options["cut"].style.display = "none";
|
||||||
this.menu_options["delete"].hidden = true;
|
this.menu_options["delete"].style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
this.menu_options["copy"].hidden = false;
|
this.menu_options["copy"].style.display = "block";
|
||||||
this.menu_options["cut"].hidden = false;
|
this.menu_options["cut"].style.display = "block";
|
||||||
this.menu_options["delete"].hidden = false;
|
this.menu_options["delete"].style.display = "block";
|
||||||
}
|
}
|
||||||
if (this.nav_window_ref.selected_entries.size > 1) {
|
if (this.nav_window_ref.selected_entries.size > 1) {
|
||||||
this.menu_options["rename"].hidden = true;
|
this.menu_options["rename"].style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
this.menu_options["rename"].hidden = false;
|
this.menu_options["rename"].style.display = "block";
|
||||||
}
|
}
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.dom_element.style.display = "inline";
|
this.dom_element.style.display = "inline";
|
||||||
@ -833,7 +828,189 @@ class NavContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide_paste() {
|
hide_paste() {
|
||||||
this.menu_options["paste"].hidden = true;
|
this.menu_options["paste"].style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileUpload {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {File|Blob} file
|
||||||
|
* @param {Number} chunk_size
|
||||||
|
* @param {NavWindow} nav_window_ref
|
||||||
|
*/
|
||||||
|
constructor(file, chunk_size, nav_window_ref) {
|
||||||
|
this.chunk_size = chunk_size;
|
||||||
|
this.filename = file.name;
|
||||||
|
this.nav_window_ref = nav_window_ref;
|
||||||
|
this.path = nav_window_ref.pwd().path_str() + "/" + file.name;
|
||||||
|
this.reader = new FileReader();
|
||||||
|
this.chunks = this.slice_file(file);
|
||||||
|
this.chunk_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_if_exists() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/fail-if-exists.py", this.path], {superuser: "try"});
|
||||||
|
proc.done((data) => {resolve(false)});
|
||||||
|
proc.fail((e, data) => {resolve(true)});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
make_html_element() {
|
||||||
|
var notification = document.createElement("div");
|
||||||
|
notification.classList.add("nav-notification");
|
||||||
|
var header = document.createElement("div");
|
||||||
|
header.classList.add("nav-notification-header");
|
||||||
|
notification.appendChild(header);
|
||||||
|
header.innerText = "Uploading " + this.filename;
|
||||||
|
var progress = document.createElement("progress");
|
||||||
|
progress.max = this.num_chunks;
|
||||||
|
notification.appendChild(progress);
|
||||||
|
this.progress = progress;
|
||||||
|
this.html_elements = [progress, header, notification];
|
||||||
|
document.getElementById("nav-notifications").appendChild(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_html_element() {
|
||||||
|
for (let elem of this.html_elements) {
|
||||||
|
elem.parentElement.removeChild(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {File|Blob} file
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
slice_file(file) {
|
||||||
|
var offset = 0;
|
||||||
|
var chunks = [];
|
||||||
|
this.num_chunks = Math.ceil(file.size / this.chunk_size);
|
||||||
|
for (let i = 0; i < this.num_chunks; i++) {
|
||||||
|
var next_offset = Math.min(this.chunk_size * (i + 1), file.size);
|
||||||
|
chunks.push(file.slice(offset, next_offset));
|
||||||
|
offset = next_offset;
|
||||||
|
}
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
async upload() {
|
||||||
|
if (await this.check_if_exists()) {
|
||||||
|
window.alert(this.filename + ": File exists.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.make_html_element();
|
||||||
|
this.proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/write-chunks.py", this.path], {err: "out", superuser: "try"});
|
||||||
|
this.proc.fail((e, data) => {
|
||||||
|
this.reader.onload = () => {}
|
||||||
|
this.done();
|
||||||
|
window.alert(data);
|
||||||
|
})
|
||||||
|
this.proc.done((data) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
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.readAsArrayBuffer(this.chunks[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayBufferToBase64(buffer) {
|
||||||
|
let binary = '';
|
||||||
|
let bytes = new Uint8Array(buffer);
|
||||||
|
let len = bytes.byteLength;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i]);
|
||||||
|
}
|
||||||
|
return window.btoa(binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Event} evt
|
||||||
|
* @param {Number} offset
|
||||||
|
*/
|
||||||
|
write_to_file(evt, offset) {
|
||||||
|
var chunk_b64 = this.arrayBufferToBase64(evt.target.result);
|
||||||
|
const seek = this.chunk_index * this.chunk_size;
|
||||||
|
var obj = {
|
||||||
|
seek: seek,
|
||||||
|
chunk: chunk_b64
|
||||||
|
};
|
||||||
|
this.proc.input(JSON.stringify(obj) + "\n", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
done() {
|
||||||
|
this.proc.input(); // close stdin
|
||||||
|
this.nav_window_ref.refresh();
|
||||||
|
this.remove_html_element();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NavDragDrop {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {HTMLDivElement} drop_area
|
||||||
|
* @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);
|
||||||
|
this.drop_area = drop_area;
|
||||||
|
this.nav_window_ref = nav_window_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
switch(e.type){
|
||||||
|
case "dragenter":
|
||||||
|
this.drop_area.classList.add("drag-enter");
|
||||||
|
break;
|
||||||
|
case "dragover":
|
||||||
|
break;
|
||||||
|
case "dragleave":
|
||||||
|
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 === "") {
|
||||||
|
window.alert(file.name + ": Cannot upload folders.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var uploader = new FileUpload(file, 4096, this.nav_window_ref);
|
||||||
|
uploader.upload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let file of ev.dataTransfer.files) {
|
||||||
|
if (file.type === "")
|
||||||
|
continue;
|
||||||
|
var uploader = new FileUpload(file, 4096, this.nav_window_ref);
|
||||||
|
uploader.upload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.drop_area.classList.remove("drag-enter");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.drop_area.classList.remove("drag-enter");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,11 +1025,14 @@ class NavWindow {
|
|||||||
this.window = document.getElementById("nav-contents-view");
|
this.window = document.getElementById("nav-contents-view");
|
||||||
this.window.addEventListener("click", this);
|
this.window.addEventListener("click", this);
|
||||||
this.window.addEventListener("contextmenu", this);
|
this.window.addEventListener("contextmenu", this);
|
||||||
|
window.addEventListener("keydown", this);
|
||||||
this.last_selected_index = -1;
|
this.last_selected_index = -1;
|
||||||
|
|
||||||
this.context_menu = new NavContextMenu("nav-context-menu", this);
|
this.context_menu = new NavContextMenu("nav-context-menu", this);
|
||||||
|
|
||||||
this.clip_board = [];
|
this.clip_board = [];
|
||||||
|
|
||||||
|
this.uploader = new NavDragDrop(this.window, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -869,6 +1049,22 @@ class NavWindow {
|
|||||||
this.context_menu.show(e, this.pwd());
|
this.context_menu.show(e, this.pwd());
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
case "keydown":
|
||||||
|
if (e.keyCode === 46) {
|
||||||
|
this.delete_selected();
|
||||||
|
} else if (e.keyCode === 65 && e.ctrlKey) {
|
||||||
|
this.select_all();
|
||||||
|
e.preventDefault();
|
||||||
|
} else if (e.keyCode === 67 && e.ctrlKey) {
|
||||||
|
this.copy();
|
||||||
|
} else if (e.keyCode === 86 && e.ctrlKey) {
|
||||||
|
this.paste();
|
||||||
|
} else if (e.keyCode === 88 && e.ctrlKey) {
|
||||||
|
this.cut();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +1074,7 @@ class NavWindow {
|
|||||||
var num_dirs = 0;
|
var num_dirs = 0;
|
||||||
var num_files = 0;
|
var num_files = 0;
|
||||||
var bytes_sum = 0;
|
var bytes_sum = 0;
|
||||||
var show_hidden = document.getElementById("nav-show-hidden").checked;
|
this.show_hidden = document.getElementById("nav-show-hidden").checked;
|
||||||
this.start_load();
|
this.start_load();
|
||||||
var files = await this.pwd().get_children(this);
|
var files = await this.pwd().get_children(this);
|
||||||
while (this.entries.length) {
|
while (this.entries.length) {
|
||||||
@ -900,7 +1096,7 @@ class NavWindow {
|
|||||||
num_files++;
|
num_files++;
|
||||||
bytes_sum += file.stat["size"];
|
bytes_sum += file.stat["size"];
|
||||||
}
|
}
|
||||||
if(!file.is_hidden_file || show_hidden)
|
if(!file.is_hidden_file || this.show_hidden)
|
||||||
file.show();
|
file.show();
|
||||||
this.entries.push(file);
|
this.entries.push(file);
|
||||||
file.context_menu_ref = this.context_menu;
|
file.context_menu_ref = this.context_menu;
|
||||||
@ -1224,6 +1420,25 @@ class NavWindow {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cut() {
|
||||||
|
this.clip_board = [...this.selected_entries];
|
||||||
|
this.copy_or_move = "move";
|
||||||
|
this.paste_cwd = this.pwd().path_str();
|
||||||
|
this.context_menu.menu_options["paste"].style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
copy() {
|
||||||
|
this.clip_board = [...this.selected_entries];
|
||||||
|
this.copy_or_move = "copy";
|
||||||
|
this.paste_cwd = this.pwd().path_str();
|
||||||
|
this.context_menu.menu_options["paste"].style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
paste() {
|
||||||
|
this.paste_clipboard();
|
||||||
|
this.context_menu.hide_paste();
|
||||||
|
}
|
||||||
|
|
||||||
async paste_clipboard() {
|
async paste_clipboard() {
|
||||||
this.start_load();
|
this.start_load();
|
||||||
this.context_menu.hide_paste();
|
this.context_menu.hide_paste();
|
||||||
@ -1253,9 +1468,10 @@ class NavWindow {
|
|||||||
proc.fail((e, data) => {
|
proc.fail((e, data) => {
|
||||||
window.alert("Paste failed.");
|
window.alert("Paste failed.");
|
||||||
});
|
});
|
||||||
await proc;
|
proc.always(() => {
|
||||||
this.stop_load();
|
this.stop_load();
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1305,7 +1521,7 @@ class NavWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start_load() {
|
start_load() {
|
||||||
document.getElementById("nav-loader-container").hidden = false;
|
document.getElementById("nav-loader-container").style.display = "block";
|
||||||
var buttons = document.getElementsByTagName("button");
|
var buttons = document.getElementsByTagName("button");
|
||||||
for (let button of buttons) {
|
for (let button of buttons) {
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
@ -1313,7 +1529,7 @@ class NavWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stop_load() {
|
stop_load() {
|
||||||
document.getElementById("nav-loader-container").hidden = true;
|
document.getElementById("nav-loader-container").style.display = "none";
|
||||||
var buttons = document.getElementsByTagName("button");
|
var buttons = document.getElementsByTagName("button");
|
||||||
for (let button of buttons) {
|
for (let button of buttons) {
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
@ -1386,6 +1602,14 @@ class NavWindow {
|
|||||||
}
|
}
|
||||||
document.getElementById("pwd").disabled = false;
|
document.getElementById("pwd").disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select_all() {
|
||||||
|
for (let entry of this.entries) {
|
||||||
|
if (!entry.is_hidden_file || this.show_hidden) {
|
||||||
|
this.set_selected(entry, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let nav_window = new NavWindow();
|
let nav_window = new NavWindow();
|
||||||
|
26
navigator/scripts/fail-if-exists.py
Executable file
26
navigator/scripts/fail-if-exists.py
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cockpit Navigator - A File System Browser for Cockpit.
|
||||||
|
Copyright (C) 2021 Josh Boudreau <jboudreau@45drives.com>
|
||||||
|
|
||||||
|
This file is part of Cockpit Navigator.
|
||||||
|
Cockpit Navigator is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
Cockpit Navigator is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Cockpit Navigator. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if os.path.exists(sys.argv[1]):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
sys.exit(0)
|
70
navigator/scripts/write-chunks.py
Executable file
70
navigator/scripts/write-chunks.py
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cockpit Navigator - A File System Browser for Cockpit.
|
||||||
|
Copyright (C) 2021 Josh Boudreau <jboudreau@45drives.com>
|
||||||
|
|
||||||
|
This file is part of Cockpit Navigator.
|
||||||
|
Cockpit Navigator is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
Cockpit Navigator is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Cockpit Navigator. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
Synopsis: `write-chunks.py <newline delimited JSON objects>`
|
||||||
|
JSON objects are of form:
|
||||||
|
obj = {
|
||||||
|
seek: <byte offset>
|
||||||
|
chunk: <base64 encoded data chunk>
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
def write_chunk(chunk, file):
|
||||||
|
seek = chunk["seek"]
|
||||||
|
data = base64.b64decode(chunk["chunk"])
|
||||||
|
file.seek(seek)
|
||||||
|
file.write(data)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("Invalid number of arguments.")
|
||||||
|
sys.exit(1)
|
||||||
|
path = sys.argv[1]
|
||||||
|
try:
|
||||||
|
file = open(path, "xb")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
sys.exit(1)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
json_in = input()
|
||||||
|
except EOFError:
|
||||||
|
break
|
||||||
|
json_list = json_in.split("\n")
|
||||||
|
for json_obj in json_list:
|
||||||
|
try:
|
||||||
|
obj_in = json.loads(json_obj)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
log = open("/var/log/navigator.log", "w")
|
||||||
|
log.write(json_in)
|
||||||
|
log.close()
|
||||||
|
sys.exit(1)
|
||||||
|
write_chunk(obj_in, file)
|
||||||
|
file.close()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
x
Reference in New Issue
Block a user