mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-09-26 03:08:41 +02:00
implement symbolic link handling
This commit is contained in:
parent
0d5855852b
commit
a3343bb7d4
@ -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;
|
||||
|
@ -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('/');
|
||||
|
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user