Merge pull request #15 from 45Drives/dev-josh

Implement list view and fix opening symlinks to files for editing
This commit is contained in:
Josh Boudreau 2021-06-07 17:31:52 -03:00 committed by GitHub
commit eb50adbad4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 203 additions and 35 deletions

View File

@ -22,12 +22,12 @@ With no command line use needed, you can:
# Installation
## From Github Release
### Ubuntu
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.4/cockpit-navigator_0.4.1-1focal_all.deb`
1. `# apt install ./cockpit-navigator_0.4.1-1focal_all.deb`
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.4/cockpit-navigator_0.4.2-1focal_all.deb`
1. `# apt install ./cockpit-navigator_0.4.2-1focal_all.deb`
### EL7
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.4/cockpit-navigator-0.4.1-1.el7.noarch.rpm`
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.4/cockpit-navigator-0.4.2-1.el7.noarch.rpm`
### EL8
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.4/cockpit-navigator-0.4.1-1.el8.noarch.rpm`
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.4/cockpit-navigator-0.4.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`

7
debian/changelog vendored
View File

@ -1,3 +1,10 @@
cockpit-navigator (0.4.2-1focal) focal; urgency=low
* Implement list view.
* Fix opening symlinks to files for editing.
-- Josh Boudreau <jboudreau@45drives.com> Mon, 07 Jun 2021 17:27:00 -0300
cockpit-navigator (0.4.1-1focal) focal; urgency=low
* Use smaller chunk size while uploading for older versions of Cockpit.

View File

@ -1,5 +1,5 @@
Name: cockpit-navigator
Version: 0.4.1
Version: 0.4.2
Release: 1%{?dist}
Summary: A File System Browser for Cockpit.
License: GPL-3.0+
@ -32,6 +32,9 @@ rm -rf %{buildroot}
/usr/share/cockpit/navigator/*
%changelog
* Mon Jun 07 2021 Josh Boudreau <jboudreau@45drives.com> 0.4.2-1
- Implement list view.
- Fix opening symlinks to files for editing.
* Mon Jun 07 2021 Josh Boudreau <jboudreau@45drives.com> 0.4.1-1
- Use smaller chunk size while uploading for older versions of Cockpit.
* Mon Jun 07 2021 Josh Boudreau <jboudreau@45drives.com> 0.4.0-1

View File

@ -22,7 +22,7 @@
--border: #bebebe;
--navigation: #fff;
--font: #1c1c1c;
--selected: #f8f8f8;
--selected: #eeeeee;
--toggle-light: #151515;
--toggle-dark: #ccc;
--scrollbar-thumb: var(--border);
@ -34,6 +34,7 @@
--nav-entry-color: #555F6E;
--nav-border-radius: 4px;
--symlink-symbol-color: var(--navigation);
--list-view-header: var(--selected);
}
[data-theme="dark"] {
@ -51,6 +52,7 @@
--logo-45: #fff;
--nav-entry-color: #555F6E;
--symlink-symbol-color: var(--navigation);
--list-view-header: var(--container);
}
html {
@ -200,17 +202,25 @@ input[type="text"] {
background-color: var(--navigation);
border: 1px solid var(--border);
border-radius: var(--nav-border-radius);
padding: 0.5em;
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-items: flex-start;
align-content: flex-start;
overflow: auto;
position: relative;
}
.nav-item {
.contents-view-grid {
flex-flow: row wrap;
align-items: flex-start;
padding: 0.5em;
}
.contents-view-list {
flex-flow: column nowrap;
align-items: stretch;
}
.contents-view-grid > .nav-item {
margin: 2px;
padding: 3px;
flex: 0;
@ -221,20 +231,12 @@ input[type="text"] {
box-sizing: border-box;
}
.nav-item-selected {
background-color: var(--selected);
border: 1px solid var(--border);
border-radius: var(--nav-border-radius);
box-sizing: border-box;
padding: 2px;
}
.nav-item .nav-item-title {
.contents-view-grid > .nav-item > .nav-item-title {
text-align: center;
overflow-wrap: anywhere;
}
.nav-item .nav-item-icon {
.contents-view-grid > .nav-item > .nav-item-icon {
position: relative;
text-align: center;
width: 100px;
@ -242,22 +244,103 @@ input[type="text"] {
color: var(--nav-entry-color);
}
.nav-item-symlink-symbol-dir {
.contents-view-grid > .nav-item-selected {
background-color: var(--selected);
border: 1px solid var(--border);
border-radius: var(--nav-border-radius);
box-sizing: border-box;
padding: 2px;
}
.contents-view-grid > .nav-item > .nav-item-icon > .nav-item-symlink-symbol-dir {
position: absolute;
color: var(--symlink-symbol-color);
font-size: 12pt;
font-size: 20%;
bottom: 19%;
right: 15%;
}
.nav-item-symlink-symbol-file {
.contents-view-grid > .nav-item > .nav-item-icon > .nav-item-symlink-symbol-file {
position: absolute;
color: var(--symlink-symbol-color);
font-size: 12pt;
font-size: 20%;
bottom: 6%;
right: 25%;
}
.contents-view-grid > .contents-view-list-header {
display: none;
}
.contents-view-list-header {
background-color: var(--list-view-header);
cursor: default !important;
}
.contents-view-list > .nav-item {
padding: 3px;
flex: 0;
display: flex;
flex: row nowrap;
align-items: baseline;
justify-content: flex-start;
cursor: pointer;
box-sizing: border-box;
}
.contents-view-list > .nav-item:nth-child(even) {
background-color: var(--selected);
}
.contents-view-list > .nav-item > .nav-item-title {
margin-left: 5px;
padding-right: 5px;
}
.contents-view-list > .nav-item > .nav-item-icon {
position: relative;
text-align: center;
width: 20px;
color: var(--nav-entry-color);
}
.contents-view-list > .nav-item-selected {
background-color: var(--selected);
border: 1px solid var(--border);
border-radius: var(--nav-border-radius);
box-sizing: border-box;
padding: 2px;
}
.contents-view-list > .nav-item > .nav-item-icon > .nav-item-symlink-symbol-dir {
position: absolute;
color: var(--symlink-symbol-color);
font-size: 40%;
bottom: 19%;
right: 15%;
}
.contents-view-list > .nav-item > .nav-item-icon > .nav-item-symlink-symbol-file {
position: absolute;
color: var(--symlink-symbol-color);
font-size: 40%;
bottom: 6%;
right: 25%;
}
.contents-view-list > .nav-item > div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-basis: 0;
flex-grow: 1;
}
.contents-view-list > .nav-item > div:first-of-type {
flex-basis: 0;
flex-grow: 2;
}
.nav-info-column {
background-color: var(--container);
flex-basis: 0;
@ -471,6 +554,10 @@ input:checked + .slider:before {
left: -30%;
}
.clickable {
cursor: pointer;
}
.nav-context-menu {
display: none;
position: absolute;

View File

@ -64,7 +64,15 @@
</div>
<div class="vertical-spacer"></div>
<div class="flex-row inner-container">
<div class="contents-view" id="nav-contents-view">
<div class="contents-view contents-view-grid" id="nav-contents-view">
<div class="contents-view-list-header nav-item">
<i class="nav-item-icon"></i>
<div class="nav-item-title">Name</div>
<div>Mode</div>
<div>Owner</div>
<div>Group</div>
<div>Size</div>
</div>
<div class="nav-notifications" id="nav-notifications">
</div>
</div>
@ -157,6 +165,10 @@
<img src="branding/logo-light.svg" id="logo-45d"><span class="logo-45">45</span><span class="logo-drives">Drives</span>
</a>
<div class="flex-grow"></div>
<div id="nav-item-display-btn" class="clickable">
<i class="fas fa-list" id="nav-item-display-icon"></i>
</div>
<div class="horizontal-spacer"></div>
<div class="nav-toggle">
<div class="nav-btn-group">
<i class="fas fa-low-vision" id="nav-show-hidden-icon"></i>

View File

@ -111,6 +111,18 @@ function load_hidden_file_state(nav_window) {
}
}
/**
*
* @param {NavWindow} nav_window
*/
function load_item_display_state(nav_window) {
const state = localStorage.getItem('item-display');
if (state === 'list') {
nav_window.switch_item_display();
}
}
function set_last_theme_state() {
var toggle_switch = document.getElementById("toggle-theme");
var state = localStorage.getItem("houston-theme-state");
@ -226,6 +238,20 @@ class NavEntry {
if (this.is_hidden_file)
icon.style.opacity = 0.5;
this.dom_element.title = this.filename();
if (nav_window_ref && nav_window_ref.item_display === "list") {
let mode = document.createElement("div");
let owner = document.createElement("div");
let group = document.createElement("div");
let size = document.createElement("div");
mode.title = mode.innerText = this.stat["mode-str"];
owner.title = owner.innerText = this.stat["owner"];
group.title = group.innerText = this.stat["group"];
size.title = size.innerText = format_bytes(this.stat["size"]);
this.dom_element.appendChild(mode);
this.dom_element.appendChild(owner);
this.dom_element.appendChild(group);
this.dom_element.appendChild(size);
}
}
/**
@ -550,6 +576,8 @@ class NavFileLink extends NavFile{
this.double_click = false;
this.link_target = link_target;
this.dom_element.nav_item_title.style.fontStyle = "italic";
if (nav_window_ref.item_display === "list")
this.dom_element.nav_item_title.innerHTML += " &#8594; " + this.link_target;
}
show_properties() {
@ -572,22 +600,27 @@ class NavFileLink extends NavFile{
return target;
}
async open() {
var target_path = this.get_link_target_path();
var proc_output = await cockpit.spawn(["file", "--mime-type", target_path], {superuser: "try"});
var fields = proc_output.split(/:(?=[^:]+$)/); // ensure it's the last : with lookahead
var type = fields[1].trim();
if ((/^text/.test(type) || /^inode\/x-empty$/.test(type) || this.stat["size"] === 0)) {
this.show_edit_file_contents();
} else {
console.log("Unknown mimetype: " + type);
window.alert("Can't open " + this.filename() + " for editing.");
}
}
async show_edit_file_contents() {
this.nav_window_ref.disable_buttons_for_editing();
document.getElementById("pwd").disabled = true;
var target_path = this.get_link_target_path();
var proc_output = await cockpit.spawn(["file", "--mime-type", target_path], {superuser: "try"});
var fields = proc_output.split(':');
var type = fields[1].trim();
if (!(/^text/.test(type) || /^inode\/x-empty$/.test(type) || this.stat["size"] === 0)) {
if (!window.confirm("File is of type `" + type + "`. Are you sure you want to edit it?")) {
this.nav_window_ref.enable_buttons();
return;
}
}
var contents = "";
try {
contents = await cockpit.file(this.path_str(), {superuser: "try"}).read();
contents = await cockpit.file(target_path, {superuser: "try"}).read();
} catch(e) {
this.nav_window_ref.enable_buttons();
window.alert(e.message);
@ -825,6 +858,8 @@ class NavDirLink extends NavDir{
this.double_click = false;
this.link_target = link_target;
this.dom_element.nav_item_title.style.fontStyle = "italic";
if (nav_window_ref.item_display === "list")
this.dom_element.nav_item_title.innerHTML += " &#8594; " + this.link_target;
}
/**
@ -1259,6 +1294,7 @@ class NavDragDrop {
class NavWindow {
constructor() {
this.item_display = "grid";
this.path_stack = (localStorage.getItem('navigator-path') ?? '/').split('/');
this.path_stack = this.path_stack.map((_, index) => new NavDir([...this.path_stack.slice(0, index + 1)].filter(part => part != ''), this));
@ -1942,6 +1978,27 @@ class NavWindow {
}
}
}
async switch_item_display() {
var button = document.getElementById("nav-item-display-icon");
if (this.item_display === "grid") {
this.item_display = "list";
await this.refresh();
this.window.classList.remove("contents-view-grid");
this.window.classList.add("contents-view-list");
button.classList.remove("fa-list");
button.classList.add("fa-th");
} else {
this.item_display = "grid";
await this.refresh();
this.window.classList.remove("contents-view-list");
this.window.classList.add("contents-view-grid");
button.classList.remove("fa-th");
button.classList.add("fa-list");
}
localStorage.setItem("item-display", this.item_display);
}
}
let nav_window = new NavWindow();
@ -1967,11 +2024,13 @@ function set_up_buttons() {
document.getElementById("pwd").addEventListener("keydown", nav_window.nav_bar_event_handler.bind(nav_window));
document.getElementById("toggle-theme").addEventListener("change", switch_theme, false);
document.getElementById("nav-show-hidden").addEventListener("change", nav_window.toggle_show_hidden.bind(nav_window));
document.getElementById("nav-item-display-btn").addEventListener("click", nav_window.switch_item_display.bind(nav_window));
}
async function main() {
set_last_theme_state();
load_hidden_file_state(nav_window);
load_item_display_state(nav_window);
var get_users = nav_window.get_system_users();
var get_groups = nav_window.get_system_groups();
var refresh = nav_window.refresh();