More node editor updates + delete edge
parent
10373487d9
commit
ccd5cf86e5
|
@ -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 />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue