implement symbolic link handling

This commit is contained in:
joshuaboud 2021-05-28 14:50:06 -03:00
parent 0d5855852b
commit a3343bb7d4
No known key found for this signature in database
GPG Key ID: 17EFB59E2A8BF50E
3 changed files with 117 additions and 8 deletions

View File

@ -22,7 +22,7 @@
--border: #bebebe;
--navigation: #fff;
--font: #1c1c1c;
--selected: #fff;
--selected: #f8f8f8;
--toggle-light: #151515;
--toggle-dark: #ccc;
--scrollbar-thumb: var(--border);
@ -33,6 +33,7 @@
--logo-45: #333;
--nav-entry-color: #555F6E;
--nav-border-radius: 4px;
--symlink-symbol-color: var(--navigation);
}
[data-theme="dark"] {
@ -49,6 +50,7 @@
--textarea-bg: var(--navigation);
--logo-45: #fff;
--nav-entry-color: #555F6E;
--symlink-symbol-color: var(--navigation);
}
.pf-c-button:disabled[data-theme="dark"] {
@ -215,12 +217,29 @@ input[type="text"] {
}
.nav-item .nav-item-icon {
position: relative;
text-align: center;
width: 100px;
font-size: 80px;
color: var(--nav-entry-color);
}
.nav-item-symlink-symbol-dir {
position: absolute;
color: var(--symlink-symbol-color);
font-size: 12pt;
bottom: 18%;
right: 15%;
}
.nav-item-symlink-symbol-file {
position: absolute;
color: var(--symlink-symbol-color);
font-size: 12pt;
bottom: 8%;
right: 25%;
}
.nav-info-column {
background-color: var(--container);
flex-basis: 0;

View File

@ -300,6 +300,58 @@ class NavFile extends NavEntry {
}
}
class NavFileLink extends NavFile{
constructor(/*string or array*/ path, /*dict*/ stat, nav_window_ref, link_target) {
super(path, stat, nav_window_ref);
var link_icon = this.dom_element.nav_item_icon.link_icon = document.createElement("i");
link_icon.classList.add("fas", "fa-link", "nav-item-symlink-symbol-file");
this.dom_element.nav_item_icon.appendChild(link_icon);
this.double_click = false;
this.link_target = link_target;
}
show_properties() {
var extra_properties = property_entry_html("Link Target", this.link_target);
super.show_properties(extra_properties);
}
get_link_target_path() {
var target = "";
if(this.link_target.charAt(0) === '/')
target = this.link_target;
else
target = "/" + this.parent_dir().join("/") + "/" + this.link_target;
return target;
}
async show_edit_file_contents() {
for (let button of document.getElementsByTagName("button")) {
if (!button.classList.contains("editor-btn"))
button.disabled = true;
}
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(!(type.match(/^text/) || type.match(/^inode\/x-empty$/) || this.stat["size"] === 0)){
if(!window.confirm("File is of type `" + type + "`. Are you sure you want to edit it?"))
return;
}
var contents = await cockpit.spawn(["cat", target_path], {superuser: "try"});
document.getElementById("nav-edit-contents-textarea").value = contents;
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-edit-contents-header").innerText = "Editing " + this.path_str();
document.getElementById("nav-contents-view").style.display = "none";
document.getElementById("nav-edit-contents-view").style.display = "flex";
}
async write_to_file() {
var target_path = this.get_link_target_path();
var new_contents = document.getElementById("nav-edit-contents-textarea").value;
await cockpit.script("echo -n \"$1\" > $2", [new_contents, target_path], {superuser: "try"});
this.nav_window_ref.refresh();
this.hide_edit_file_contents();
}
}
class NavDir extends NavEntry {
constructor(/*string or array*/ path, /*dict*/ stat, nav_window_ref) {
super(path, stat, nav_window_ref);
@ -343,10 +395,20 @@ class NavDir extends NavEntry {
var filename = entry["filename"];
var path = (this.path.length >= 1 && this.path[0]) ? [...this.path, filename] : [filename];
var stat = entry["stat"];
if (entry["isdir"])
children.push(new NavDir(path, stat, nav_window_ref));
else
children.push(new NavFile(path, stat, nav_window_ref));
switch(stat["mode-str"].charAt(0)) {
case 'd':
children.push(new NavDir(path, stat, nav_window_ref));
break;
case 'l':
if(entry["isdir"])
children.push(new NavDirLink(path, stat, nav_window_ref, entry["link-target"]));
else
children.push(new NavFileLink(path, stat, nav_window_ref, entry["link-target"]));
break;
default:
children.push(new NavFile(path, stat, nav_window_ref));
break;
}
});
children.sort((first, second) => {
if (first.nav_type === second.nav_type) {
@ -379,8 +441,7 @@ class NavDir extends NavEntry {
return null;
}
}
async show_properties() {
var extra_properties = "";
async show_properties(/*string*/ extra_properties = "") {
if(!this.hasOwnProperty("ceph_stats"))
this.ceph_stats = await this.cephfs_dir_stats();
// See if a JSON object exists for folder we are currently looking at
@ -424,6 +485,31 @@ class NavDir extends NavEntry {
}
}
class NavDirLink extends NavDir{
constructor(/*string or array*/ path, /*dict*/ stat, nav_window_ref, /*string*/ link_target) {
super(path, stat, nav_window_ref);
var link_icon = this.dom_element.nav_item_icon.link_icon = document.createElement("i");
link_icon.classList.add("fas", "fa-link", "nav-item-symlink-symbol-dir");
this.dom_element.nav_item_icon.appendChild(link_icon);
this.double_click = false;
this.link_target = link_target;
}
async rm() {
var proc = cockpit.spawn(
["rm", "-f", this.path_str()],
{superuser: "try", err: "out"}
);
proc.fail((e, data) => {
window.alert(data);
});
await proc;
}
show_properties() {
var extra_properties = property_entry_html("Link Target", this.link_target);
super.show_properties(extra_properties);
}
}
class NavWindow {
constructor() {
this.path_stack = (localStorage.getItem('navigator-path') ?? '/').split('/');

View File

@ -18,7 +18,7 @@
"""
import os
from stat import S_ISDIR, filemode
from stat import S_ISDIR, S_ISLNK, filemode
import json
import sys
from pwd import getpwuid
@ -31,6 +31,9 @@ def get_stat(full_path, filename = '/'):
isdir = S_ISDIR(os.stat(full_path).st_mode)
except OSError:
pass
link_target = '?'
if S_ISLNK(stats.st_mode):
link_target = os.readlink(full_path)
owner = '?'
try:
owner = getpwuid(stats.st_uid).pw_name
@ -44,6 +47,7 @@ def get_stat(full_path, filename = '/'):
response = {
"filename": filename,
"isdir": isdir,
"link-target": link_target,
"stat": {
"mode": stats.st_mode,
"mode-str": filemode(stats.st_mode),