UNPKG

globular_admin

Version:
775 lines (656 loc) 23.8 kB
import { Panel } from "./panel"; import { PermissionExplorer, PermissionPanel } from "./permissionPanel"; import { getAllActions, getErrorMessage, readAllActionPermission, setActionPermission, removeActionPermission, globular, getRessources, eventHub, removeRessource } from "./backend"; import { randomUUID, rgbToHsl } from "./utility"; import { Ressource } from "globular-web-client/lib/ressource/ressource_pb"; let emptyPath = "..."; /** * Panel to be use to manage ressource access other than files. * A ressource is somthing with a path. A path is a string with hierachy * where the symbol '/' is use as level marquer. */ export class RessourceManager extends Panel { private actionPermissionManager: ActionPermissionManager; private pathNavigator: RessourcePathNavigator; private ressourcePanel: RessourcesPanel; private permissionExplorer: PermissionExplorer; constructor() { super("ressource_manager"); // create the ressource action permission manager. this.actionPermissionManager = new ActionPermissionManager(); this.actionPermissionManager.setParent(this.div); // Create the ressource path navigator. this.pathNavigator = new RessourcePathNavigator(); this.pathNavigator.setParent(this.div); // Create the ressource panel. this.ressourcePanel = new RessourcesPanel(); this.ressourcePanel.setParent(this.div); // Create the permission explorer. this.permissionExplorer = new PermissionExplorer("ressource_permission_explorer", this.div); // Emit when user click on the path eventHub.subscribe( "set_ressource_path_event", (uuid: string) => { }, (evt: any) => { // Set the dir to display. // Here I must retreive the directory from the given path. let dir = this.ressourcePanel.getRessourceDir(evt.path) this.pathNavigator.setPath(evt.path) this.permissionExplorer.setRessource(dir); }, true ); eventHub.subscribe( "delete_ressource_event", (uuid: string) => { }, (evt: any) => { // Set the dir to display. this.pathNavigator.setPath("") this.permissionExplorer.setRessource(null); }, true ); // Emit when user change the ressource directory. eventHub.subscribe( "set_ressource_event", (uuid: string) => { }, (evt: any) => { // Set the dir to display. // Here I must retreive the directory from the given path. let dir = this.ressourcePanel.getRessourceDir(evt.ressource.path) if (dir != undefined) { this.pathNavigator.setPath(evt.ressource.path) } }, true ); } } /** * That panel is use to set ressource action permission. * The action permission can be READ | WRITE | DELETE or any combination of those permission. */ class ActionPermissionManager extends Panel { // Set editable. private editable: boolean; // The ressource manager constructor() { super("ressource_action_permission_manager"); this.editable = false; this.displayPermissions(); } // Here I will react to login information... onlogin(data: any) { // overide... this.editable = true; this.displayPermissions(); } onlogout() { // overide... this.editable = false; this.displayPermissions(); } displayPermissions() { this.div.removeAllChilds(); // Display the header. let content = this.div.appendElement({ tag: "div", class: "card col s12", style: "padding:10px;" }).down() content.appendElement({ tag: "div", class: "row hide-on-small-only" }).down() .appendElement({ tag: "div", class: "col m6 l8", style: "border-right: 1px solid lightgray;", innerHtml: "Action(s)" }) .appendElement({ tag: "div", class: "col m6 l4", innerHtml: "Permission(s)" }) if (this.editable) { // In that case I will append the action selector. getAllActions( (actions: Array<string>) => { // In that case I wil get the list of all operations. // Here I will append the actions list. let uuid = randomUUID() let action_input = content .prependElement({ tag: "div", class: "row" }) .down() .appendElement({ tag: "div", class: "input-field col s8" }) .down() .appendElement({ tag: "input", class: "autocomplete", placeholder: "New Action", id: uuid }) .down(); let data: any; data = {}; for (var i = 0; i < actions.length; i++) { data[actions[i]] = null; } // The action call on auto complete... let onAutocomplete = () => { let action = action_input.element.value; setActionPermission(action, 0, () => { M.toast({ html: "Action pemission " + action + " has been added!", displayLength: 2000 }); this.displayPermissions() }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }); } // call after the ressource actions are retreived. let callback = () => { M.Autocomplete.init(document.getElementById(uuid), { data: data, onAutocomplete: onAutocomplete }); } readAllActionPermission( (actionPermission: Array<any>) => { // remove existing permission... for (var i = 0; i < actionPermission.length; i++) { delete data[actionPermission[i].action] } callback() // done remove already existing values. }, (err: any) => { callback() // simply go to callback. }) }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }); } // Display all permissions. readAllActionPermission( (actionPermission: Array<any>) => { // remove existing permission... for (var i = 0; i < actionPermission.length; i++) { let action = actionPermission[i].action; let permission = actionPermission[i].permission; let div = content.appendElement({ tag: "div", class: "row", style: "padding: 5px 10px 5px 10px; margin: 0px;" }).down(); div.appendElement({ tag: "div", id: "permission_div_0", class: "input-field col s12 m6 l8", style: "padding: 0px; margin-left: 10px; margin: 0px;", innerHtml: action }) let permissionDiv = div.appendElement({ tag: "div", id: "permission_div_1", class: "input-field col s12 m6 l4", style: "padding: 0px; margin: 0px; display: flex; margin-bottom: 5px;" }).down() // Mouse over to make reading little easier. div.getChildById("permission_div_0").element.onmouseenter = div.getChildById("permission_div_1").element.onmouseenter = () => { div.getChildById("permission_div_0").element.style.backgroundColor = "#fafafa" div.getChildById("permission_div_1").element.style.backgroundColor = "#fafafa" } div.getChildById("permission_div_0").element.onmouseleave = div.getChildById("permission_div_1").element.onmouseleave = () => { div.getChildById("permission_div_0").element.style.backgroundColor = "" div.getChildById("permission_div_1").element.style.backgroundColor = "" } // Set the permission panel. new PermissionPanel(randomUUID(), permissionDiv, permission, // on change permission (permission_number: number) => { setActionPermission(action, permission_number, () => { this.displayPermissions() M.toast({ html: action + " permission was change!", displayLength: 2000 }); }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }) }, // On delete callback () => { removeActionPermission(action, () => { this.displayPermissions() M.toast({ html: action + " permission was deleted!", displayLength: 2000 }); }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }) }, this.editable) } }, (err: any) => { /** nothing to do here... */ }) } } /** * */ class RessourcePathNavigator extends Panel { private content: any; private path: string; constructor() { super(randomUUID()); // Set the div style. // Set the content. this.content = this.div.appendElement({ tag: "nav", class: "row card col s12 /*m10 offset-m1*/ indigo darken-4" }).down() .appendElement({ tag: "div", class: "nav-wrapper" }).down() .appendElement({ tag: "div" }).down() .appendElement({ tag: "div", class: "col s12" }).down() // Set root path... this.setPath(""); } // The the path nivagator path. setPath(path: string) { this.content.removeAllChilds() this.path = "path" let values = path.split("/"); values.unshift(emptyPath) let div = this.content.appendElement({ tag: "div", class: "col s12" }).down() for (var i = 0; i < values.length; i++) { if (values[i].length > 0) { let lnk = div .appendElement({ tag: "a", innerHtml: values[i], class: "breadcrumb" }) .down(); lnk.element.onmouseenter = function () { this.style.cursor = "pointer"; }; lnk.element.onmouseleave = function () { this.style.cursor = "default"; }; let index = i + 1; lnk.element.onclick = () => { let path_ = ""; for (var j = 1; j < index; j++) { path_ += values[j]; if (j < index - 1) { path_ += "/"; } } eventHub.publish("set_ressource_path_event", { path: path_ }, true); }; } } } } /** * A recursive tree structure to display and and manage ressource as tree. */ class RessourceDir { // Keep sub ressource dir. private _ressourceDirs: Map<string, RessourceDir>; public get ressourceDirs(): Map<string, RessourceDir> { return this._ressourceDirs; } // The list of ressource contain in the dir. private _ressources: Map<string, Ressource>; public get ressources(): Map<string, Ressource> { return this._ressources; } private _parent: RessourceDir; public get parent(): RessourceDir { return this._parent; } public set parent(value: RessourceDir) { this._parent = value; } // Return the name of the Ressource dir public get name(): string { return this.path.split("/")[this.path.split("/").length - 1] } // Keep ressource tree. constructor(public path: string) { // initialyse internal structure. this._ressourceDirs = new Map<string, RessourceDir>(); this._ressources = new Map<string, Ressource>(); this.parent = null; } appendRessource(ressource: Ressource) { // set the ressource. this._ressources.set(ressource.getName(), ressource); } appendRessourceDir(dir: RessourceDir) { dir.parent = this this._ressourceDirs.set(dir.path, dir); } } /** * Ressource panel is use to display and manage ressources. A ressource can be anything * that has a name propertie and a path. */ class RessourcesPanel extends Panel { // The list of ressources... private ressources: Map<string, RessourceDir>; private editable: boolean; constructor() { super(randomUUID()) // That will contain all ressource dir by path. this.ressources = new Map<string, RessourceDir>(); // init the ressources and display it after. this.initRessources(() => { // Set the editable parameter. this.displayRessources(null); }) // Display the div. eventHub.subscribe( "delete_ressource_event", (uuid: string) => { }, (evt: any) => { // Set the dir to display. this.initRessources(() => { // Set the editable parameter. this.displayRessources(null); }) }, true ); // Display the div. eventHub.subscribe( "set_ressource_event", (uuid: string) => { }, (evt: any) => { // Set the dir to display. if (this.ressources.has(evt.ressource.path)) { this.displayRessources(this.ressources.get(evt.ressource.path)); } }, true ); // Emit when user click on the path eventHub.subscribe( "set_ressource_path_event", (uuid: string) => { }, (evt: any) => { this.displayRessources(this.ressources.get(evt.path)); }, true ); } // Here I will react to login information... onlogin(data: any) { // overide... this.editable = true; this.div.getChildById("name_title").element.className = "col m6" } onlogout() { // overide... this.editable = false; this.div.getChildById("name_title").element.className = "col m7" } /** * Init a ressource and create it dir if not already exist. * @param ressource */ initRessource(ressource: Ressource) { // I will create ressource dir from path and append it to it parent if it exist... let path = ressource.getPath() // Remove leading and trailling / symbols. if (path.startsWith("/")) { path = path.substr(1) } if (path.endsWith("/")) { path = path.substr(0, path.length - 1) } let paths = path.split("/") let path_ = ""; let parent = ""; for (var j = 0; j < paths.length; j++) { parent = path_ path_ += "/" + paths[j] if (!this.ressources.has(path_)) { let dir = new RessourceDir(path_) this.ressources.set(path_, dir); } // append the dir. if (this.ressources.has(parent)) { this.ressources.get(parent).appendRessourceDir(this.ressources.get(path_)) } } // append the ressource if (this.ressources.has(ressource.getPath())) { this.ressources.get(ressource.getPath()).appendRessource(ressource) } } initRessources(callback: () => void) { this.ressources.clear(); this.div.removeAllChilds(); getRessources("", "", (ressources: Array<Ressource>) => { // So here I will inialyse the ressources. for (var i = 0; i < ressources.length; i++) { this.initRessource(ressources[i]) } callback() // end of ressource initialisation. }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }); } getRessourceDir(dir: string) { return this.ressources.get(dir) } /** * display a ressources from a given path. * @param path */ displayRessources(dir: RessourceDir) { // I will retreive all ressources. this.div.removeAllChilds(); // remove all childs... let content = this.div.appendElement({ tag: "div", class: "card col s12", style: "padding: 10px;" }).down(); content.appendElement({ tag: "div", class: "row hide-on-small-only" }).down() .appendElement({ tag: "div", id: "name_title", class: "col m7", style: "border-right: 1px solid lightgray;", innerHtml: "Name" }) .appendElement({ tag: "div", id: "date_title", class: "col m3", style: "border-right: 1px solid lightgray;", innerHtml: "Date modified" }) .appendElement({ tag: "div", id: "size_title", class: "col m2", innerHtml: "Size" }) if (this.editable) { this.div.getChildById("name_title").element.className = "col m6" } else { this.div.getChildById("name_title").element.className = "col m7" } // Now I will retreive all ressources from the server. if (dir == undefined) { // Here I will display all dir with not parent... this.ressources.forEach((dir: RessourceDir) => { if (dir.parent == null) { let ressourcePanel = new RessourcePanel(content, this.editable) ressourcePanel.setRessourceDir(dir) } }) } else { // Set dir dir.ressourceDirs.forEach((dir: RessourceDir) => { let ressourcePanel = new RessourcePanel(content, this.editable) ressourcePanel.setRessourceDir(dir) }) // Set ressource dir.ressources.forEach((r: Ressource) => { let ressourcePanel = new RessourcePanel(content, this.editable) ressourcePanel.setRessource(r) }) } } } class RessourcePanel extends Panel { private dir: RessourceDir; private ressource: Ressource; private ico: any; private editable: boolean; constructor(parent: any, editable: boolean) { super(randomUUID()) this.editable = editable; this.div = parent .appendElement({ tag: "div", class: "row", style: "margin: 0px; padding: 5px;" }) .down(); eventHub.subscribe( "set_ressource_dir_event", (uuid: string) => { }, (evt: any) => { // Set the dir to display. this.setRessourceDir(evt.dir); }, true ); } // Here I will display a ressource dir... setRessourceDir(dir: RessourceDir) { // clear the div. this.div.removeAllChilds() this.dir = dir; this.ico = this.div .appendElement({ tag: "i", class: "Small material-icons col s1", innerHtml: "folder" }) .down(); this.ico.element.onclick = () => { eventHub.publish("set_ressource_event", { id: "ressource_permission_explorer", ressource: { path: dir.path, name: dir.name } }, true); } this.ico.element.onmouseenter = function () { this.style.cursor = "pointer" } this.ico.element.onmouseleave = function () { this.style.cursor = "default" } // The file name link... if (this.editable) { this.div .appendElement({ tag: "span", innerHtml: dir.name, class: "col s10" }) .down(); // I will append a delete button in that particular case. let deleteFileBtn = this.div .appendElement({ tag: "i", class: "Small material-icons col s1", innerHtml: "delete", style: "text-align: right;" }) .down(); deleteFileBtn.element.onmouseenter = function () { this.style.cursor = "pointer" } deleteFileBtn.element.onmouseleave = function () { this.style.cursor = "default" } deleteFileBtn.element.onclick = () => { removeRessource(dir.path, "", () => { eventHub.publish("delete_ressource_event", {}, true); M.toast({ html: "ressource for " + dir.path + " have been deleted!", displayLength: 2000 }); }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }); } } else { this.div .appendElement({ tag: "span", innerHtml: dir.name, class: "col s11" }) .down(); } } // Here I will display a ressource. setRessource(ressource: Ressource) { this.ressource = ressource; this.div.removeAllChilds(); this.ico = this.div .appendElement({ tag: "i", class: "Small material-icons col s1", innerHtml: "insert_drive_file" }) .down(); this.ico.element.onclick = () => { eventHub.publish("set_ressource_event", { id: "ressource_permission_explorer", ressource: { path: ressource.getPath() + "/" + ressource.getName(), name: ressource.getName() } }, true); } this.ico.element.onmouseenter = function () { this.style.cursor = "pointer" } this.ico.element.onmouseleave = function () { this.style.cursor = "default" } let nameSpan = this.div .appendElement({ tag: "span", innerHtml: ressource.getName(), class: "col s11 m6" }) .down(); let lastModified = new Date(ressource.getModified() * 1000); this.div.appendElement({ tag: "div", class: "col s6 m3", innerHtml: lastModified.toLocaleDateString() + " " + lastModified.toLocaleTimeString() }); let ressourceSizeDiv = this.div .appendElement({ tag: "div", class: "col s6 m2" }) .down(); if (ressource.getSize() > 1024) { if (ressource.getSize() > 1024 * 1024) { if (ressource.getSize() > 1024 * 1024 * 1024) { let fileSize = ressource.getSize() / (1024 * 1024 * 1024); ressourceSizeDiv.element.innerHTML = fileSize.toFixed(0) + " Gb"; } else { let fileSize = ressource.getSize() / (1024 * 1024); ressourceSizeDiv.element.innerHTML = fileSize.toFixed(0) + " Mb"; } } else { let fileSize = ressource.getSize() / 1024; ressourceSizeDiv.element.innerHTML = fileSize.toFixed(0) + " Kb"; } } else { ressourceSizeDiv.element.innerHTML = ressource.getSize() + " bytes"; } if (this.editable) { // give space to delete button. nameSpan.element.className = "col s10 m5" // I will append a delete button in that particular case. let deleteFileBtn = this.div .appendElement({ tag: "i", class: "Small material-icons col s1", innerHtml: "delete", style: "text-align: right;" }) .down(); deleteFileBtn.element.onmouseenter = function () { this.style.cursor = "pointer" } deleteFileBtn.element.onmouseleave = function () { this.style.cursor = "default" } deleteFileBtn.element.onclick = () => { // removeActionPermission removeRessource(ressource.getPath(), ressource.getName(), () => { eventHub.publish("delete_ressource_event", {}, true); M.toast({ html: "ressource for " + ressource.getName() + " have been deleted!", displayLength: 2000 }); }, (err: any) => { M.toast({ html: getErrorMessage(err.message), displayLength: 2000 }); }); } } } onlogin(data: any) { // overide... this.editable = true; if (this.ressource != undefined) { this.setRessource(this.ressource) } else { this.setRessourceDir(this.dir) } } onlogout() { // overide... this.editable = false; if (this.ressource != undefined) { this.setRessource(this.ressource) } else { this.setRessourceDir(this.dir) } } }