More node editor updates + delete edge

master
Rohan Vazarkar 2018-07-18 10:28:49 -04:00
parent 10373487d9
commit ccd5cf86e5
8 changed files with 234 additions and 26 deletions

View File

@ -3,6 +3,7 @@ import GraphContainer from "./components/Graph";
import SearchContainer from "./components/SearchContainer/SearchContainer";
import SpotlightContainer from "./components/Spotlight/SpotlightContainer";
import LogoutModal from "./components/Modals/LogoutModal";
import DeleteEdgeModal from "./components/Modals/DeleteEdgeModal";
import CancelUploadModal from "./components/Modals/CancelUploadModal";
import ClearWarnModal from "./components/Modals/ClearWarnModal";
import ClearConfirmModal from "./components/Modals/ClearConfirmModal";
@ -46,6 +47,7 @@ export default class AppContainer extends Component {
<GraphContainer />
<SearchContainer />
<LogoutModal />
<DeleteEdgeModal />
<ClearWarnModal />
<ClearConfirmModal />
<ClearingModal />

View File

@ -22,9 +22,11 @@ export default class NodeEditor extends Component {
},
handle: "#nodeEditOuter"
});
$(this.refs.outer).fadeToggle(false);
}
getNodeData(name, type) {
appStore.currentTooltip.close();
$(this.refs.outer).fadeIn();
let q = driver.session();
this.setState({ label: name, type: type });
@ -42,6 +44,7 @@ export default class NodeEditor extends Component {
let label = props.name;
delete props.name;
this.setState({ properties: props, label: label });
q.close()
}.bind(this)
);
}
@ -50,8 +53,75 @@ export default class NodeEditor extends Component {
$(this.refs.outer).fadeToggle(false);
}
addAttrib(){
let input = $(this.refs.newAttrName);
let typeinput = $(this.refs.newAttrType);
let val = input.val();
let type = typeinput.val();
if (val === ""){
input.css("border", "3px solid red")
return;
}
let newval;
if (type === "boolean"){
newval = false;
}else if (type === "number"){
newval = 0;
}else if (type === "string"){
newval = "placeholder";
}else{
newval = [];
}
let key;
if (this.state.type === "ou") {
key = "guid";
} else {
key = "name";
}
let q = driver.session();
let statement = "MATCH (n:{} {{}:{name}}) SET n.{}={newprop} RETURN n".format(this.state.type.toTitleCase(), key, val)
q.run(statement, {name: this.state.label, newprop: newval}).then(result => {
let props = result.records[0]._fields[0].properties;
let label = props.name;
delete props.name;
this.setState({ properties: props, label: label });
});
}
updateHandler(attrName, newVal) {
console.log(attrName, newVal)
let key;
if (this.state.type === "ou") {
key = "guid";
} else {
key = "name";
}
let statement;
if (attrName === "serviceprincipalnames" && this.state.type === "user"){
if (newVal[0] === "" && newVal.length === 1){
newVal = []
}
if (newVal.length > 0){
statement = "MATCH (n:{} {{}:{name}}) SET n.{}={newprop}, n.hasspn=true RETURN n".format(this.state.type.toTitleCase(), key, attrName)
}else{
statement = "MATCH (n:{} {{}:{name}}) SET n.{}={newprop}, n.hasspn=false RETURN n".format(this.state.type.toTitleCase(), key, attrName)
}
}else{
statement = "MATCH (n:{} {{}:{name}}) SET n.{}={newprop} RETURN n".format(this.state.type.toTitleCase(), key, attrName)
}
let q = driver.session();
q.run(statement, {name: this.state.label, newprop: newVal}).then(result => {
let props = result.records[0]._fields[0].properties;
let label = props.name;
delete props.name;
this.setState({ properties: props, label: label });
});
}
deletePropHandler(attrName) {
@ -73,10 +143,16 @@ export default class NodeEditor extends Component {
let label = props.name;
delete props.name;
this.setState({ properties: props, label: label });
q.close();
}.bind(this)
);
}
removeValidation(){
let input = $(this.refs.newAttrName);
input.css("border", "")
}
render() {
return (
<div ref="outer" className="nodeEditor panel panel-default">
@ -126,21 +202,24 @@ export default class NodeEditor extends Component {
</tbody>
</table>
</div>
<form className="form-inline pull-right">
<input
type="text"
className="form-control form-override"
ref="newAttrName"
/>
<select className="form-control" ref="newAttrType">
<option>Boolean</option>
<option>String</option>
<option>Number</option>
<option>Array</option>
</select>
<button className="form-control">
<span className="fa fa-plus" /> Add
</button>
<form onSubmit={x => x.preventDefault()} className="form-inline pull-right">
<div onFocus={this.removeValidation.bind(this)} className="form-group">
<input
type="text"
className="form-control form-override"
ref="newAttrName"
placeholder="New Attribute Name"
/>
<select className="form-control" ref="newAttrType">
<option>boolean</option>
<option>string</option>
<option>number</option>
<option>array</option>
</select>
<button className="form-control formButtonFix" onClick={this.addAttrib.bind(this)}>
<span className="fa fa-plus" /> Add
</button>
</div>
</form>
</div>
</div>

View File

@ -34,7 +34,7 @@ export default class NodeEditorRow extends Component {
} else if (input.is("textarea")){
let tempval = val.join("\n");
input.attr("disabled", "");
input.val(tempval);
input.val(tempval);
}else {
input.attr("disabled", "");
}
@ -42,18 +42,24 @@ export default class NodeEditorRow extends Component {
}
saveEdit() {
console.log(this.state);
let input = jQuery(this.refs.input);
let val;
if (input.is("div")) {
val = input.html();
input.removeAttr("contenteditable");
} else if (input.is("textarea")){
val = input.val();
let tempval = input.val();
val = tempval.split("\n");
input.attr("disabled", "");
} else {
val = this.state.val;
input.attr("disabled", "");
}
console.log(val);
if (this.state.valtype === "number"){
val = parseInt(val);
}
this.setState({editing: false});
this.props.updateHandler(this.props.attributeName, val)
}
enableEdit() {

View File

@ -21,15 +21,24 @@ export default class GraphContainer extends Component {
design: null,
dragged: false,
firstDraw: true,
template: null,
nodeTemplate: null,
edgeTemplate: null,
session: driver.session()
};
$.ajax({
url: "src/components/tooltip.html",
url: "src/components/nodeTooltip.html",
type: "GET",
success: function(response) {
this.setState({ template: response });
this.setState({ nodeTemplate: response });
}.bind(this)
});
$.ajax({
url: "src/components/edgeTooltip.html",
type: "GET",
success: function(response) {
this.setState({ edgeTemplate: response });
}.bind(this)
});
@ -140,6 +149,7 @@ export default class GraphContainer extends Component {
emitter.on("zoomOut", this.zoomOut.bind(this));
emitter.on("changeNodeLabels", this.changeNodeLabelMode.bind(this));
emitter.on("changeEdgeLabels", this.changeEdgeLabelMode.bind(this));
emitter.on("deleteEdgeConfirm", this.deleteEdge.bind(this));
}
componentDidMount() {
@ -164,6 +174,26 @@ export default class GraphContainer extends Component {
}
}
deleteEdge(id){
let instance = this.state.sigmaInstance;
let edge = instance.graph.edges(id);
let sourcenode = instance.graph.nodes(edge.source);
let targetnode = instance.graph.nodes(edge.target);
let sourcekey = sourcenode.type === "OU" ? 'guid' : 'name';
let targetkey = targetnode.type === "OU" ? 'guid' : 'name';
let statement = "MATCH (n:{} {{}:{sname}}) MATCH (m:{} {{}:{tname}}) MATCH (n)-[r:{}]->(m) DELETE r".format(sourcenode.type, sourcekey, targetnode.type, targetkey, edge.label);
instance.graph.dropEdge(edge.id);
instance.refresh();
let q = driver.session();
q.run(statement, {sname: sourcenode.label, tname: targetnode.label}).then(x => {q.close()})
}
reload(){
this.doQueryNative(this.state.currentQuery);
}
@ -858,6 +888,9 @@ export default class GraphContainer extends Component {
sigmaInstance.settings({
edgeColor: "default",
edgeHoverColor: "default",
defaultEdgeHoverColor: "#000",
enableEdgeHovering: true,
nodeColor: "default",
minEdgeSize: 1,
maxEdgeSize: 2.5,
@ -988,7 +1021,7 @@ export default class GraphContainer extends Component {
cssClass: "new-tooltip",
autoadjust: true,
renderer: function(node) {
var template = this.state.template;
var template = this.state.nodeTemplate;
node.expand = false;
node.collapse = false;
if (
@ -1008,7 +1041,18 @@ export default class GraphContainer extends Component {
return Mustache.render(template, node);
}.bind(this)
}
]
],
edge: [
{
show: "rightClickEdge",
cssClass: "new-tooltip",
autoadjust: true,
renderer: function(edge){
var template = this.state.edgeTemplate;
return Mustache.render(template, edge);
}.bind(this)
}
]
}
);

View File

@ -0,0 +1,64 @@
import React, { Component } from "react";
import { Modal } from "react-bootstrap";
export default class DeleteEdgeModal extends Component {
constructor() {
super();
this.state = {
open: false
};
}
closeModal() {
this.setState({ open: false });
}
confirmDelete() {
this.closeModal();
emitter.emit("deleteEdgeConfirm", this.state.id);
}
openModal(id) {
appStore.currentTooltip.close();
this.setState({ open: true, id: id });
}
componentDidMount() {
emitter.on("deleteEdge", this.openModal.bind(this));
}
render() {
return (
<Modal
show={this.state.open}
onHide={this.closeModal.bind(this)}
aria-labelledby="DeleteEdgeModalHeader"
>
<Modal.Header closeButton={true}>
<Modal.Title id="DeleteEdgeModalHeader">Delete Edge</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>Are you sure you want to delete this edge?</p>
</Modal.Body>
<Modal.Footer>
<button
type="button"
className="btn btn-danger"
onClick={this.confirmDelete.bind(this)}
>
Confirm
</button>
<button
type="button"
className="btn btn-primary"
onClick={this.closeModal.bind(this)}
>
Cancel
</button>
</Modal.Footer>
</Modal>
);
}
}

View File

@ -0,0 +1,8 @@
<div class="header">
{{label}}
</div>
<ul>
<li onclick="emitter.emit('deleteEdge', '{{id}}')">
<i class="glyphicon glyphicon-screenshot"></i> Delete Edge
</li>
</ul>

View File

@ -179,6 +179,11 @@ div.tooltip-inner-custom {
box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2);
}
.formButtonFix:focus{
border: 1px solid #ccc !important;
box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
}
.heighthack{
height: 1px;
}