diff --git a/navigator/navigator.js b/navigator/navigator.js index 578175f..c3178a8 100644 --- a/navigator/navigator.js +++ b/navigator/navigator.js @@ -1,18 +1,15 @@ - - function property_entry_html(key, value) { var html = '
' + html += ' "; + html += ' "; + html += ""; return html; } function format_bytes(bytes) { - if(bytes === 0) - return "0 B"; + if (bytes === 0) return "0 B"; var units = [" B", " KiB", " MiB", " GiB", " TiB", " PiB"]; - var index = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1) + var index = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1); var pow = Math.pow(1024, index); var formatted = bytes / pow; return formatted.toFixed(2).toString() + units[index]; @@ -26,39 +23,69 @@ function format_time(timestamp) { function format_permissions(/*int*/ mode) { var bit_list = ["x", "w", "r"]; var result = ""; - for(let bit = 8; bit >= 0; bit--){ + for (let bit = 8; bit >= 0; bit--) { var test_bit = 1 << bit; var test_result = mode & test_bit; - if(test_result != 0){ + if (test_result != 0) { result += bit_list[bit % bit_list.length]; - }else{ + } else { result += "-"; } } return result; } +/* cephfs_dir_stats + * Receives: path to folder + * Does: Tries command with --json flag at path to folder. If + * command fails and gives an error then catch that error + * Returns: JSON object or nothing + */ +async function cephfs_dir_stats(path) { + try { + var proc = await cockpit.spawn(["cephfs-dir-stats", "-j", path], { + err: "ignore", + }); + return JSON.parse(proc.replace(/\[|\]/g, "")); + } catch { + return []; + } +} + +/* in_json + * Receives: A boolean to see if key is in JSON object + * and the JSON objects value if it exists + * Does: Checks if JSON key exists; if so, return the + * value of the key, if not, return the string "N/A" + * Returns: The value of key or "N/A" + */ +function in_json(is_key, value) { + if (is_key) { + return value; + } else { + return "N/A"; + } +} + class NavEntry { constructor(/*string or array*/ path, /*dict*/ stat, /*NavWindow*/ nav_window_ref) { this.nav_window_ref = nav_window_ref; - if(typeof path == 'string') - this.path = path.split('/').splice(1); - else - this.path = path; + if (typeof path == "string") this.path = path.split("/").splice(1); + else this.path = path; this.dom_element = document.createElement("div"); this.dom_element.classList.add("nav-item"); - let icon = this.dom_element.nav_item_icon = document.createElement("i"); + let icon = (this.dom_element.nav_item_icon = document.createElement("i")); icon.classList.add("nav-file-icon"); - let title = this.dom_element.nav_item_title = document.createElement("div"); + let title = (this.dom_element.nav_item_title = document.createElement("div")); title.classList.add("nav-item-title"); title.innerText = this.filename(); this.dom_element.appendChild(icon); this.dom_element.appendChild(title); this.stat = stat; - this.dom_element.addEventListener("click", this) + this.dom_element.addEventListener("click", this); } handleEvent(e) { - switch(e.type){ + switch (e.type) { case "click": this.show_properties(); this.nav_window_ref.set_selected(this); @@ -67,20 +94,18 @@ class NavEntry { } } destroy() { - while(this.dom_element.firstChild){ + while (this.dom_element.firstChild) { this.dom_element.removeChild(this.dom_element.firstChild); } - if(this.dom_element.parentElement) - this.dom_element.parentElement.removeChild(this.dom_element); + if (this.dom_element.parentElement) this.dom_element.parentElement.removeChild(this.dom_element); } filename() { - var name = this.path[this.path.length -1]; - if(name === "") - name = "/"; + var name = this.path[this.path.length - 1]; + if (name === "") name = "/"; return name; } path_str() { - return "/" + this.path.join('/'); + return "/" + this.path.join("/"); } show() { document.getElementById("nav-contents-view").appendChild(this.dom_element); @@ -92,20 +117,20 @@ class NavEntry { return this.stat["mode"] & 0o777; } async chmod(/*int*/ new_perms) { - var proc = cockpit.spawn( - ["chmod", (new_perms & 0o777).toString(8), this.path_str()], - {superuser: "try", err:"out"} - ); + var proc = cockpit.spawn(["chmod", (new_perms & 0o777).toString(8), this.path_str()], { + superuser: "try", + err: "out", + }); proc.fail((e, data) => { window.alert(data); }); await proc; } async chown(/*string*/ new_owner, /*string*/ new_group) { - var proc = cockpit.spawn( - ["chown", [new_owner, new_group].join(":"), this.path_str()], - {superuser: "try", err:"out"} - ); + var proc = cockpit.spawn(["chown", [new_owner, new_group].join(":"), this.path_str()], { + superuser: "try", + err: "out", + }); proc.fail((e, data) => { window.alert(data); }); @@ -113,8 +138,8 @@ class NavEntry { } async mv(/*string*/ new_path) { var proc = cockpit.spawn( - ["mv", "-n", this.path_str(), [this.nav_window_ref.pwd().path_str(), new_path].join('/')], - {superuser: "try", err:"out"} + ["mv", "-n", this.path_str(), [this.nav_window_ref.pwd().path_str(), new_path].join("/")], + { superuser: "try", err: "out" } ); proc.fail((e, data) => { window.alert(data); @@ -123,7 +148,7 @@ class NavEntry { } show_properties(/*string*/ extra_properties = "") { var selected_name_fields = document.getElementsByClassName("nav-info-column-filename"); - for(let elem of selected_name_fields){ + for (let elem of selected_name_fields) { elem.innerText = this.filename(); } var html = ""; @@ -139,13 +164,21 @@ class NavEntry { } populate_edit_fields() { document.getElementById("nav-edit-filename").value = this.filename(); - var mode_bits = ["other-exec", "other-write", "other-read", - "group-exec", "group-write", "group-read", - "owner-exec", "owner-write", "owner-read"]; - for(let i = 0; i < mode_bits.length; i++){ - var bit_check = (1 << i); + var mode_bits = [ + "other-exec", + "other-write", + "other-read", + "group-exec", + "group-write", + "group-read", + "owner-exec", + "owner-write", + "owner-read", + ]; + for (let i = 0; i < mode_bits.length; i++) { + var bit_check = 1 << i; var result = this.stat["mode"] & bit_check; - document.getElementById(mode_bits[i]).checked = (result != 0); + document.getElementById(mode_bits[i]).checked = result != 0; } document.getElementById("nav-edit-owner").value = this.stat["owner"]; document.getElementById("nav-edit-group").value = this.stat["group"]; @@ -162,10 +195,7 @@ class NavFile extends NavEntry { super.handleEvent(e); } async rm() { - var proc = cockpit.spawn( - ["rm", "-f", this.path_str()], - {superuser: "try", err:"out"} - ); + var proc = cockpit.spawn(["rm", "-f", this.path_str()], { superuser: "try", err: "out" }); proc.fail((e, data) => { window.alert(data); }); @@ -175,26 +205,21 @@ class NavFile extends NavEntry { class NavDir extends NavEntry { constructor(/*string or array*/ path, /*dict*/ stat, nav_window_ref) { - super(path, stat, nav_window_ref); + super(path, stat, nav_window_ref); // super = call parent this.nav_type = "dir"; this.dom_element.nav_item_icon.classList.add("fas", "fa-folder"); this.double_click = false; - /* - Sam: - add a method to NavDir that calls cephfs-dir-stats and call it here. - The function should save the results as a dictionary like the following: - this.cephfs_dir_stats = {key : "value", etc}; - */ + this.ceph_stats = []; + cephfs_dir_stats(this.path_str()).then((data) => (this.ceph_stats = data)); } handleEvent(e) { - switch(e.type){ + switch (e.type) { case "click": - if(this.double_click) - this.nav_window_ref.cd(this); - else{ // single click + if (this.double_click) this.nav_window_ref.cd(this); + else { + // single click this.double_click = true; - if(this.timeout) - clearTimeout(this.timeout) + if (this.timeout) clearTimeout(this.timeout); this.timeout = setTimeout(() => { this.double_click = false; }, 500); @@ -205,34 +230,30 @@ class NavDir extends NavEntry { } async get_children(nav_window_ref) { var children = []; - var data = await cockpit.spawn(["/usr/share/cockpit/navigator/scripts/ls.py", this.path_str()], {err:"ignore"}); + var data = await cockpit.spawn(["/usr/share/cockpit/navigator/scripts/ls.py", this.path_str()], { + err: "ignore", + }); var response = JSON.parse(data); this.stat = response["."]["stat"]; var entries = response["children"]; - entries.forEach(entry => { + entries.forEach((entry) => { var filename = entry["filename"]; - var path = (this.path.length >= 1 && this.path[0]) ? [... this.path, filename] : [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)); + if (entry["isdir"]) children.push(new NavDir(path, stat, nav_window_ref)); + else children.push(new NavFile(path, stat, nav_window_ref)); }); children.sort((first, second) => { - if(first.nav_type === second.nav_type){ + if (first.nav_type === second.nav_type) { return first.filename().localeCompare(second.filename()); } - if(first.nav_type === "dir") - return -1; + if (first.nav_type === "dir") return -1; return 1; - }) + }); return children; } async rm() { - var proc = cockpit.spawn( - ["rmdir", this.path_str()], - {superuser: "try", err:"out"} - ); + var proc = cockpit.spawn(["rmdir", this.path_str()], { superuser: "try", err: "out" }); proc.fail((e, data) => { window.alert(data); }); @@ -240,12 +261,44 @@ class NavDir extends NavEntry { } show_properties() { var extra_properties = ""; - /* - Sam: - Follow NavEntry.show_properties() as an example to put the cephfs-dir-stats results - into extra_properties as html elements. If cephfs-dir-stats failed, i.e. it's not in a - ceph filesystem, make sure extra_properties is an empty string. - */ + + // See if a JSON object exists for folder we are currently looking at + if (this.ceph_stats.length !== 0) { + extra_properties += + '