mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-07-31 01:24:37 +02:00
commit
fd82f854fc
11
README.md
11
README.md
@ -19,16 +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.0/cockpit-navigator_0.2.0-1focal_all.deb`
|
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.1/cockpit-navigator_0.2.1-1focal_all.deb`
|
||||||
1. `# apt install ./cockpit-navigator_0.2.0-1focal_all.deb`
|
1. `# apt install ./cockpit-navigator_0.2.1-1focal_all.deb`
|
||||||
### EL7
|
### EL7
|
||||||
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.0/cockpit-navigator-0.2.0-1.el7.noarch.rpm`
|
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.1/cockpit-navigator-0.2.1-1.el7.noarch.rpm`
|
||||||
### EL8
|
### EL8
|
||||||
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.0/cockpit-navigator-0.2.0-1.el8.noarch.rpm`
|
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.1/cockpit-navigator-0.2.1-1.el8.noarch.rpm`
|
||||||
## From Source
|
## From Source
|
||||||
|
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.0 is latest)
|
1. `$ git checkout <version>` (v0.2.1 is latest)
|
||||||
1. `# make install`
|
1. `# make install`
|
||||||
## From 45Drives Repositories
|
## From 45Drives Repositories
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
|
12
debian/changelog
vendored
12
debian/changelog
vendored
@ -1,3 +1,14 @@
|
|||||||
|
cockpit-navigator (0.2.1-1focal) focal; urgency=medium
|
||||||
|
|
||||||
|
* Rename "Move" to "Cut" in right click context menu.
|
||||||
|
* Improve pasting files after copying/cutting with a custom python
|
||||||
|
script to handle checking for file conflicts before calling rsync.
|
||||||
|
* Control+S saves file that's open for editing.
|
||||||
|
* Moved renaming file from properties to right click menu with prompt.
|
||||||
|
* Creating files now fails verbosely if the destination exists.
|
||||||
|
|
||||||
|
-- Josh Boudreau <jboudreau@45drives.com> Wed, 02 Jun 2021 12:09:00 -0300
|
||||||
|
|
||||||
cockpit-navigator (0.2.0-1focal) focal; urgency=medium
|
cockpit-navigator (0.2.0-1focal) focal; urgency=medium
|
||||||
|
|
||||||
* Allow for batch editing permissions and deletion by
|
* Allow for batch editing permissions and deletion by
|
||||||
@ -7,7 +18,6 @@ cockpit-navigator (0.2.0-1focal) focal; urgency=medium
|
|||||||
|
|
||||||
-- Josh Boudreau <jboudreau@45drives.com> Tue, 01 Jun 2021 13:46:00 -0300
|
-- Josh Boudreau <jboudreau@45drives.com> Tue, 01 Jun 2021 13:46:00 -0300
|
||||||
|
|
||||||
|
|
||||||
cockpit-navigator (0.1.0-1focal) focal; urgency=medium
|
cockpit-navigator (0.1.0-1focal) focal; urgency=medium
|
||||||
|
|
||||||
* Initial packaging of cockpit-navigator for Ubuntu Focal.
|
* Initial packaging of cockpit-navigator for Ubuntu Focal.
|
||||||
|
2
debian/control
vendored
2
debian/control
vendored
@ -9,5 +9,5 @@ Homepage: https://github.com/45Drives/cockpit-navigator
|
|||||||
Package: cockpit-navigator
|
Package: cockpit-navigator
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends},
|
Depends: ${shlibs:Depends}, ${misc:Depends},
|
||||||
cockpit, python3
|
cockpit, python3, rsync
|
||||||
Description: A File System Browser for Cockpit.
|
Description: A File System Browser for Cockpit.
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
Name: cockpit-navigator
|
Name: cockpit-navigator
|
||||||
Version: 0.2.0
|
Version: 0.2.1
|
||||||
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+
|
||||||
URL: github.com/45drives/cockpit-navigator/blob/main/README.md
|
URL: github.com/45drives/cockpit-navigator/blob/main/README.md
|
||||||
Source0: %{name}-%{version}.tar.gz
|
Source0: %{name}-%{version}.tar.gz
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
Requires: cockpit python3
|
Requires: cockpit python3 rsync
|
||||||
|
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
||||||
|
|
||||||
@ -32,6 +32,13 @@ rm -rf %{buildroot}
|
|||||||
/usr/share/cockpit/navigator/*
|
/usr/share/cockpit/navigator/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Wed Jun 02 2021 Josh Boudreau <jboudreau@45drives.com> 0.2.1-1
|
||||||
|
- Rename "Move" to "Cut" in right click context menu.
|
||||||
|
- Improve pasting files after copying/cutting with a custom python
|
||||||
|
script to handle checking for file conflicts before calling rsync.
|
||||||
|
- Control+S saves file that's open for editing.
|
||||||
|
- Moved renaming file from properties to right click menu with prompt.
|
||||||
|
- Creating files now fails verbosely if the destination exists.
|
||||||
* Tue Jun 01 2021 Josh Boudreau <jboudreau@45drives.com> 0.2.0-1
|
* Tue Jun 01 2021 Josh Boudreau <jboudreau@45drives.com> 0.2.0-1
|
||||||
- Allow for batch editing permissions and deletion by
|
- Allow for batch editing permissions and deletion by
|
||||||
holding shift or control while clicking to select multiple
|
holding shift or control while clicking to select multiple
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
<div class="nav-info-column-filename"></div>
|
<div class="nav-info-column-filename"></div>
|
||||||
<div class="nav-property-pair">
|
<div class="nav-property-pair">
|
||||||
<span class="nav-property-pair-key">Name</span>
|
<span class="nav-property-pair-key">Name</span>
|
||||||
<input type="text" class="nav-property-pair-value" id="nav-edit-filename"></input>
|
<span class="nav-property-pair-value" id="nav-edit-filename"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-property-pair">
|
<div class="nav-property-pair">
|
||||||
<span class="nav-property-pair-key">Owner</span>
|
<span class="nav-property-pair-key">Owner</span>
|
||||||
|
@ -323,7 +323,7 @@ class NavEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
populate_edit_fields() {
|
populate_edit_fields() {
|
||||||
document.getElementById("nav-edit-filename").value = this.filename();
|
document.getElementById("nav-edit-filename").innerText = this.filename();
|
||||||
var mode_bits = [
|
var mode_bits = [
|
||||||
"other-exec", "other-write", "other-read",
|
"other-exec", "other-write", "other-read",
|
||||||
"group-exec", "group-write", "group-read",
|
"group-exec", "group-write", "group-read",
|
||||||
@ -360,9 +360,9 @@ class NavFile extends NavEntry {
|
|||||||
handleEvent(e) {
|
handleEvent(e) {
|
||||||
switch(e.type){
|
switch(e.type){
|
||||||
case "click":
|
case "click":
|
||||||
if(this.double_click)
|
if (this.double_click)
|
||||||
this.show_edit_file_contents();
|
this.show_edit_file_contents();
|
||||||
else{ // single click
|
else { // single click
|
||||||
this.double_click = true;
|
this.double_click = true;
|
||||||
if(this.timeout)
|
if(this.timeout)
|
||||||
clearTimeout(this.timeout)
|
clearTimeout(this.timeout)
|
||||||
@ -371,6 +371,13 @@ class NavFile extends NavEntry {
|
|||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "keydown":
|
||||||
|
if (e.keyCode === 83 && e.ctrlKey === true) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.write_to_file();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
super.handleEvent(e);
|
super.handleEvent(e);
|
||||||
}
|
}
|
||||||
@ -405,7 +412,9 @@ class NavFile extends NavEntry {
|
|||||||
window.alert(e.message);
|
window.alert(e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.getElementById("nav-edit-contents-textarea").value = contents;
|
var text_area = document.getElementById("nav-edit-contents-textarea");
|
||||||
|
text_area.value = contents;
|
||||||
|
text_area.addEventListener("keydown", this);
|
||||||
document.getElementById("nav-cancel-edit-contents-btn").onclick = this.hide_edit_file_contents.bind(this);
|
document.getElementById("nav-cancel-edit-contents-btn").onclick = this.hide_edit_file_contents.bind(this);
|
||||||
document.getElementById("nav-continue-edit-contents-btn").onclick = this.write_to_file.bind(this);
|
document.getElementById("nav-continue-edit-contents-btn").onclick = this.write_to_file.bind(this);
|
||||||
document.getElementById("nav-edit-contents-header").innerText = "Editing " + this.path_str();
|
document.getElementById("nav-edit-contents-header").innerText = "Editing " + this.path_str();
|
||||||
@ -425,6 +434,7 @@ class NavFile extends NavEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide_edit_file_contents() {
|
hide_edit_file_contents() {
|
||||||
|
document.getElementById("nav-edit-contents-textarea").removeEventListener("keydown", this);
|
||||||
document.getElementById("nav-edit-contents-view").style.display = "none";
|
document.getElementById("nav-edit-contents-view").style.display = "none";
|
||||||
document.getElementById("nav-contents-view").style.display = "flex";
|
document.getElementById("nav-contents-view").style.display = "flex";
|
||||||
this.nav_window_ref.enable_buttons();
|
this.nav_window_ref.enable_buttons();
|
||||||
@ -490,7 +500,9 @@ class NavFileLink extends NavFile{
|
|||||||
window.alert(e.message);
|
window.alert(e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.getElementById("nav-edit-contents-textarea").value = contents;
|
var text_area = document.getElementById("nav-edit-contents-textarea");
|
||||||
|
text_area.value = contents;
|
||||||
|
text_area.addEventListener("keydown", this);
|
||||||
document.getElementById("nav-cancel-edit-contents-btn").onclick = this.hide_edit_file_contents.bind(this);
|
document.getElementById("nav-cancel-edit-contents-btn").onclick = this.hide_edit_file_contents.bind(this);
|
||||||
document.getElementById("nav-continue-edit-contents-btn").onclick = this.write_to_file.bind(this);
|
document.getElementById("nav-continue-edit-contents-btn").onclick = this.write_to_file.bind(this);
|
||||||
document.getElementById("nav-edit-contents-header").innerHTML = "Editing " + this.path_str() + ' <i class="fas fa-long-arrow-alt-right"></i> ' + this.get_link_target_path();
|
document.getElementById("nav-edit-contents-header").innerHTML = "Editing " + this.path_str() + ' <i class="fas fa-long-arrow-alt-right"></i> ' + this.get_link_target_path();
|
||||||
@ -714,7 +726,7 @@ class NavContextMenu {
|
|||||||
this.hide();
|
this.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
var functions = ["paste", "new_dir", "new_file", "new_link", "properties", "copy", "move", "delete"];
|
var functions = ["new_dir", "new_file", "new_link", "cut", "copy", "paste", "rename", "delete", "properties"];
|
||||||
for (let func of functions) {
|
for (let func of functions) {
|
||||||
var elem = document.createElement("div");
|
var elem = document.createElement("div");
|
||||||
var name_list = func.split("_");
|
var name_list = func.split("_");
|
||||||
@ -729,11 +741,6 @@ class NavContextMenu {
|
|||||||
this.menu_options["paste"].hidden = true;
|
this.menu_options["paste"].hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
paste() {
|
|
||||||
this.nav_window_ref.paste_clipboard();
|
|
||||||
this.hide_paste();
|
|
||||||
}
|
|
||||||
|
|
||||||
new_dir() {
|
new_dir() {
|
||||||
this.nav_window_ref.mkdir();
|
this.nav_window_ref.mkdir();
|
||||||
}
|
}
|
||||||
@ -746,27 +753,46 @@ class NavContextMenu {
|
|||||||
this.nav_window_ref.ln();
|
this.nav_window_ref.ln();
|
||||||
}
|
}
|
||||||
|
|
||||||
properties() {
|
cut() {
|
||||||
this.nav_window_ref.show_edit_selected();
|
this.nav_window_ref.clip_board = [...this.nav_window_ref.selected_entries];
|
||||||
this.hide();
|
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.clip_board = [...this.nav_window_ref.selected_entries];
|
||||||
this.menu_options["paste"].hidden = false;
|
|
||||||
this.nav_window_ref.copy_or_move = "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;
|
||||||
}
|
}
|
||||||
|
|
||||||
move() {
|
paste() {
|
||||||
this.nav_window_ref.clip_board = [...this.nav_window_ref.selected_entries];
|
this.nav_window_ref.paste_clipboard();
|
||||||
this.menu_options["paste"].hidden = false;
|
this.hide_paste();
|
||||||
this.nav_window_ref.copy_or_move = "move";
|
}
|
||||||
|
|
||||||
|
rename() {
|
||||||
|
this.hide();
|
||||||
|
var new_name = window.prompt("New Name: ");
|
||||||
|
if (new_name === null)
|
||||||
|
return;
|
||||||
|
if (new_name.includes("/")) {
|
||||||
|
window.alert("File name can't contain `/`.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.target.mv(new_name);
|
||||||
|
this.nav_window_ref.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.nav_window_ref.delete_selected();
|
this.nav_window_ref.delete_selected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
properties() {
|
||||||
|
this.nav_window_ref.show_edit_selected();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
@ -781,13 +807,14 @@ class NavContextMenu {
|
|||||||
}
|
}
|
||||||
if (target === this.nav_window_ref.pwd()) {
|
if (target === this.nav_window_ref.pwd()) {
|
||||||
this.menu_options["copy"].hidden = true;
|
this.menu_options["copy"].hidden = true;
|
||||||
this.menu_options["move"].hidden = true;
|
this.menu_options["cut"].hidden = true;
|
||||||
this.menu_options["delete"].hidden = true;
|
this.menu_options["delete"].hidden = true;
|
||||||
} else {
|
} else {
|
||||||
this.menu_options["copy"].hidden = false;
|
this.menu_options["copy"].hidden = false;
|
||||||
this.menu_options["move"].hidden = false;
|
this.menu_options["cut"].hidden = false;
|
||||||
this.menu_options["delete"].hidden = false;
|
this.menu_options["delete"].hidden = false;
|
||||||
}
|
}
|
||||||
|
this.target = target;
|
||||||
this.dom_element.hidden = false;
|
this.dom_element.hidden = false;
|
||||||
this.dom_element.style.left = event.clientX + "px";
|
this.dom_element.style.left = event.clientX + "px";
|
||||||
this.dom_element.style.top = event.clientY + "px";
|
this.dom_element.style.top = event.clientY + "px";
|
||||||
@ -1035,7 +1062,6 @@ class NavWindow {
|
|||||||
this.selected_entry().populate_edit_fields();
|
this.selected_entry().populate_edit_fields();
|
||||||
document.getElementById("selected-files-list-header").innerText = "";
|
document.getElementById("selected-files-list-header").innerText = "";
|
||||||
document.getElementById("selected-files-list").innerText = "";
|
document.getElementById("selected-files-list").innerText = "";
|
||||||
document.getElementById("nav-edit-filename").disabled = false;
|
|
||||||
} else {
|
} else {
|
||||||
for (let field of ["owner", "group"]) {
|
for (let field of ["owner", "group"]) {
|
||||||
document.getElementById("nav-edit-" + field).value = "";
|
document.getElementById("nav-edit-" + field).value = "";
|
||||||
@ -1056,6 +1082,7 @@ class NavWindow {
|
|||||||
}
|
}
|
||||||
this.update_permissions_preview();
|
this.update_permissions_preview();
|
||||||
this.changed_mode = false;
|
this.changed_mode = false;
|
||||||
|
document.getElementById("nav-mode-preview").innerText = "unchanged";
|
||||||
document.getElementById("nav-edit-properties").style.display = "flex";
|
document.getElementById("nav-edit-properties").style.display = "flex";
|
||||||
document.getElementById("nav-show-properties").style.display = "none";
|
document.getElementById("nav-show-properties").style.display = "none";
|
||||||
}
|
}
|
||||||
@ -1093,7 +1120,6 @@ class NavWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async apply_edit_selected() {
|
async apply_edit_selected() {
|
||||||
// do mv last so the rest don't fail from not finding path
|
|
||||||
var new_owner = document.getElementById("nav-edit-owner").value;
|
var new_owner = document.getElementById("nav-edit-owner").value;
|
||||||
var new_group = document.getElementById("nav-edit-group").value;
|
var new_group = document.getElementById("nav-edit-group").value;
|
||||||
var new_perms = this.get_new_permissions();
|
var new_perms = this.get_new_permissions();
|
||||||
@ -1109,12 +1135,6 @@ class NavWindow {
|
|||||||
await entry.chmod(new_perms).catch(/*ignore, caught in chmod*/);
|
await entry.chmod(new_perms).catch(/*ignore, caught in chmod*/);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.selected_entries.size === 1) {
|
|
||||||
var new_name = document.getElementById("nav-edit-filename").value;
|
|
||||||
if (new_name !== this.selected_entry().filename()) {
|
|
||||||
await this.selected_entry().mv(new_name).catch(/*ignore, caught in mv*/);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
this.hide_edit_selected();
|
this.hide_edit_selected();
|
||||||
}
|
}
|
||||||
@ -1163,7 +1183,7 @@ class NavWindow {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var proc = cockpit.spawn(
|
var proc = cockpit.spawn(
|
||||||
["touch", this.pwd().path_str() + "/" + new_file_name],
|
["/usr/share/cockpit/navigator/scripts/touch.py", this.pwd().path_str() + "/" + new_file_name],
|
||||||
{superuser: "try", err: "out"}
|
{superuser: "try", err: "out"}
|
||||||
);
|
);
|
||||||
proc.fail((e, data) => {
|
proc.fail((e, data) => {
|
||||||
@ -1197,32 +1217,36 @@ class NavWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async paste_clipboard() {
|
async paste_clipboard() {
|
||||||
|
this.start_load();
|
||||||
this.context_menu.hide_paste();
|
this.context_menu.hide_paste();
|
||||||
var cmd = [];
|
var cmd = ["/usr/share/cockpit/navigator/scripts/paste.py"];
|
||||||
var dest = this.pwd().path_str();
|
var dest = this.pwd().path_str();
|
||||||
switch (this.copy_or_move) {
|
if (this.copy_or_move === "move") {
|
||||||
case "copy":
|
cmd.push("-m");
|
||||||
cmd = ["cp", "-an"];
|
|
||||||
break;
|
|
||||||
case "move":
|
|
||||||
cmd = ["mv", "-n"];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
cmd.push(this.paste_cwd);
|
||||||
for (let item of this.clip_board) {
|
for (let item of this.clip_board) {
|
||||||
cmd.push(item.path_str());
|
cmd.push(item.path_str());
|
||||||
}
|
}
|
||||||
cmd.push(dest);
|
cmd.push(dest);
|
||||||
console.log(cmd);
|
|
||||||
var proc = cockpit.spawn(
|
var proc = cockpit.spawn(
|
||||||
cmd,
|
cmd,
|
||||||
{superuser: "try", err: "out"}
|
{superuser: "try", err: "ignore"}
|
||||||
);
|
);
|
||||||
|
proc.stream((data) => {
|
||||||
|
var payload = JSON.parse(data);
|
||||||
|
if (payload["wants-response"]) {
|
||||||
|
var user_response = window.confirm(payload["message"]);
|
||||||
|
proc.input(JSON.stringify(user_response) + "\n", true);
|
||||||
|
} else {
|
||||||
|
window.alert(payload["message"]);
|
||||||
|
}
|
||||||
|
});
|
||||||
proc.fail((e, data) => {
|
proc.fail((e, data) => {
|
||||||
window.alert(data);
|
window.alert("Paste failed.");
|
||||||
})
|
});
|
||||||
await proc;
|
await proc;
|
||||||
|
this.stop_load();
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cockpit Navigator - A File System Browser for Cockpit.
|
||||||
|
Copyright (C) 2021 Josh Boudreau <jboudreau@45drives.com>
|
||||||
|
Copyright (C) 2021 Sam Silver <ssilver@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 sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
114
navigator/scripts/paste.py
Executable file
114
navigator/scripts/paste.py
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
#!/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: `paste.py [-m] <cwd of copy> <list of source files> <destination directory>`
|
||||||
|
all full paths
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from optparse import OptionParser
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def prompt_user(message, wants_response):
|
||||||
|
payload = {
|
||||||
|
"wants-response": wants_response,
|
||||||
|
"message": message
|
||||||
|
}
|
||||||
|
print(json.dumps(payload) + "\n")
|
||||||
|
if wants_response:
|
||||||
|
response = input()
|
||||||
|
return json.loads(response)
|
||||||
|
return
|
||||||
|
|
||||||
|
def split_paths_at_cwd(paths, cwd):
|
||||||
|
response = []
|
||||||
|
for path in paths:
|
||||||
|
response.append(cwd + "/./" + os.path.relpath(path, cwd))
|
||||||
|
return response
|
||||||
|
|
||||||
|
def recursive_get_conflicts(input_array, cwd, dest):
|
||||||
|
conflicts = []
|
||||||
|
non_conflicts = []
|
||||||
|
for source in input_array:
|
||||||
|
if os.path.isdir(source):
|
||||||
|
child_nodes = os.listdir(source)
|
||||||
|
child_paths = []
|
||||||
|
for node in child_nodes:
|
||||||
|
child_paths.append(source + "/" + node)
|
||||||
|
(more_conflicts, more_non_conflicts) = recursive_get_conflicts(child_paths, cwd, dest)
|
||||||
|
conflicts += more_conflicts
|
||||||
|
non_conflicts += more_non_conflicts
|
||||||
|
continue
|
||||||
|
dest_path = dest + "/" + os.path.relpath(source, cwd)
|
||||||
|
if os.path.exists(dest_path):
|
||||||
|
conflicts.append((source, dest_path))
|
||||||
|
else:
|
||||||
|
non_conflicts.append(source)
|
||||||
|
return (conflicts, non_conflicts)
|
||||||
|
|
||||||
|
def filter_existing(args, cwd):
|
||||||
|
sources = args[:-1]
|
||||||
|
dest = args[-1]
|
||||||
|
(conflicts, non_conflicts) = recursive_get_conflicts(sources, cwd, dest)
|
||||||
|
if len(conflicts):
|
||||||
|
check_continue = prompt_user("Conflicts were found while pasting. `Cancel` to abort operation, `OK` to overwrite selectively.", True)
|
||||||
|
if not check_continue:
|
||||||
|
sys.exit(0)
|
||||||
|
for conflict in conflicts:
|
||||||
|
if prompt_user("Overwrite " + conflict[1] + "?", True):
|
||||||
|
non_conflicts.append(conflict[0])
|
||||||
|
if not len(non_conflicts):
|
||||||
|
sys.exit(0) # exit if nothing to copy
|
||||||
|
filtered_args = [*split_paths_at_cwd(non_conflicts, cwd), dest]
|
||||||
|
return filtered_args
|
||||||
|
|
||||||
|
def paste(cmd, args):
|
||||||
|
try:
|
||||||
|
child = subprocess.Popen(
|
||||||
|
[*cmd, *args],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
prompt_user(str(e), False)
|
||||||
|
sys.exit(1)
|
||||||
|
child.wait()
|
||||||
|
if child.returncode:
|
||||||
|
stdout, stderr = child.communicate()
|
||||||
|
prompt_user(stdout + stderr, False)
|
||||||
|
sys.exit(child.returncode)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-m", "--move", help="remove source files", action="store_true", dest="move", default=False)
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
cwd = args[0]
|
||||||
|
filtered_args = filter_existing(args[1:], cwd)
|
||||||
|
if options.move:
|
||||||
|
paste(["rsync", "-aI", "--relative", "--remove-source-files"], filtered_args)
|
||||||
|
else:
|
||||||
|
paste(["rsync", "-aI", "--relative"], filtered_args)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
33
navigator/scripts/touch.py
Executable file
33
navigator/scripts/touch.py
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/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 len(sys.argv) != 2:
|
||||||
|
print("No or too many arguments provided.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
open(sys.argv[1], "x").close()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
sys.exit(0)
|
Loading…
x
Reference in New Issue
Block a user