mirror of https://github.com/MISP/MISP
chg: [galaxy:fork_tree] Added fork tree visualisation - WiP
parent
d3eb28f980
commit
4b8e2c3007
|
@ -413,4 +413,51 @@ class GalaxiesController extends AppController
|
|||
$this->set('object', $object[0]);
|
||||
$this->render('/Events/ajax/ajaxGalaxies');
|
||||
}
|
||||
|
||||
public function forkTree($galaxyId)
|
||||
{
|
||||
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array('conditions' => array('GalaxyCluster.galaxy_id' => $galaxyId)));
|
||||
if (empty($clusters)) {
|
||||
throw new MethodNotAllowedException('Invalid Galaxy.');
|
||||
}
|
||||
foreach ($clusters as $k => $cluster) {
|
||||
$clusters[$k] = $this->Galaxy->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters[$k]);
|
||||
$clusters[$k] = $this->Galaxy->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $clusters[$k]);
|
||||
}
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $galaxyId)
|
||||
));
|
||||
$tree = array();
|
||||
$lookup = array();
|
||||
foreach ($clusters as $i => $cluster) {
|
||||
$clusters[$i]['children'] = array();
|
||||
$lookup[$cluster['GalaxyCluster']['id']] = &$clusters[$i];
|
||||
}
|
||||
foreach ($clusters as $i => $cluster) {
|
||||
if (!empty($cluster['GalaxyCluster']['extended_from'])) {
|
||||
$parent = $cluster['GalaxyCluster']['extended_from'];
|
||||
$lookup[$parent['GalaxyCluster']['id']]['children'][] = &$clusters[$i];
|
||||
} else {
|
||||
$tree[] = &$clusters[$i];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($tree as $i => $node) {
|
||||
if (empty($node['children'])) {
|
||||
unset($tree[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$tree = array(array(
|
||||
'Galaxy' => $galaxy['Galaxy'],
|
||||
'children' => array_values($tree)
|
||||
));
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($tree, $this->response->type());
|
||||
}
|
||||
$this->set('tree', $tree);
|
||||
$this->set('galaxy', $galaxy);
|
||||
$this->set('galaxy_id', $galaxyId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ class GalaxyClustersController extends AppController
|
|||
if ($temp === null) {
|
||||
throw new NotFoundException('Invalid galaxy cluster');
|
||||
}
|
||||
$id = $temp['Galaxy']['id'];
|
||||
$id = $temp['GalaxyCluster']['id'];
|
||||
} elseif (!is_numeric($id)) {
|
||||
throw new NotFoundException(__('Invalid galaxy cluster'));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
<div>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-small btn-inverse" href="<?= sprintf('%s/galaxies/view/%s/context:all', $baseurl, $galaxy_id) ?>"><?= __('Back to galaxy') ?></a>
|
||||
</div>
|
||||
<h2><?= sprintf(__('%s galaxy cluster extensions'), h($galaxy['Galaxy']['name'])) ?></h2>
|
||||
<svg id="treeSVG" style="width: 100%; height: 100%; min-height: 600px;"></svg>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
echo $this->element('genericElements/assetLoader', array(
|
||||
'js' => array('d3')
|
||||
));
|
||||
?>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var data = <?= json_encode($tree) ?>;
|
||||
var margin = {top: 10, right: 10, bottom: 10, left: 10};
|
||||
var width, height;
|
||||
$(document).ready(function () {
|
||||
var $tree = $('#treeSVG');
|
||||
width = $tree.width() - margin.right - margin.left;
|
||||
height = $tree.height() - margin.top - margin.bottom;
|
||||
buildTree();
|
||||
});
|
||||
|
||||
function buildTree() {
|
||||
data[0].isRoot = true;
|
||||
var tree = d3.layout.tree(data)
|
||||
.size([height, width]);
|
||||
|
||||
var diagonal = function link(d) {
|
||||
return "M" + d.source.y + "," + d.source.x
|
||||
+ "C" + (d.source.y + d.target.y) / 2 + "," + d.source.x
|
||||
+ " " + (d.source.y + d.target.y) / 2 + "," + d.target.x
|
||||
+ " " + d.target.y + "," + d.target.x;
|
||||
};
|
||||
|
||||
var svg = d3.select("#treeSVG")
|
||||
.attr("width", width + margin.right + margin.left)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
var root = data[0];
|
||||
root.x0 = height / 2;
|
||||
root.y0 = 0;
|
||||
var nodes = tree.nodes(root).reverse();
|
||||
var links = tree.links(nodes);
|
||||
var maxDepth = 0;
|
||||
var leftMaxTextLength = 0;
|
||||
nodes.forEach(function(d) {
|
||||
maxDepth = maxDepth > d.depth ? maxDepth : d.depth;
|
||||
if (d.GalaxyCluster !== undefined) {
|
||||
leftMaxTextLength = leftMaxTextLength > d.GalaxyCluster.value.length ? leftMaxTextLength : d.GalaxyCluster.value.length;
|
||||
}
|
||||
})
|
||||
var offsetLeafLength = leftMaxTextLength * 6.7; // font-size of body is 12px
|
||||
var ratioFactor = (width - offsetLeafLength) / maxDepth;
|
||||
nodes.forEach(function(d) { d.y = d.depth * ratioFactor; });
|
||||
|
||||
var node = svg.selectAll("g.node")
|
||||
.data(nodes, function(d) { return d.isRoot ? 'root' : d.GalaxyCluster.id; });
|
||||
|
||||
var nodeEnter = node.enter().append("g")
|
||||
.attr("class", "node")
|
||||
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
|
||||
.on("mouseover", nodeHover);
|
||||
|
||||
nodeEnter.append("circle")
|
||||
.attr("r", 6)
|
||||
.style("fill", function(d) { return d.isRoot ? "lightsteelblue" : "#fff"; })
|
||||
.style("stroke", "steelblue")
|
||||
.style("stroke-width", "3px");
|
||||
|
||||
nodeEnter.append("text")
|
||||
.attr("dy", function(d) { return d.children ? "1.5em" : ".35em"; })
|
||||
.attr("x", function(d) { return '1em'; })
|
||||
.attr("text-anchor", function(d) { return d.children ? (d.isRoot ? "start" : "middle") : "start"; })
|
||||
.text(function(d) { return d.isRoot ? d.Galaxy.name + ' galaxy' : d.GalaxyCluster.value; })
|
||||
.style("fill-opacity", 1)
|
||||
.style("font-weight", function(d) { return d.isRoot ? 'bold' : ''});
|
||||
|
||||
var link = svg.selectAll("path.link")
|
||||
.data(links, function(d) { return d.target.GalaxyCluster.id; });
|
||||
|
||||
link.enter().insert("path", "g")
|
||||
.attr("class", "link")
|
||||
.style("fill", "none")
|
||||
.style("stroke", "#ccc")
|
||||
.style("stroke-width", "2px")
|
||||
.attr("d", function(d) {
|
||||
return diagonal(d);
|
||||
});
|
||||
// .attr("d", d3.linkHorizontal()
|
||||
// .x(function(d) { return d.y; })
|
||||
// .y(function(d) { return d.x; }));
|
||||
}
|
||||
|
||||
function nodeHover(d) {
|
||||
var $d3Element = $(this);
|
||||
$d3Element.tooltip({
|
||||
html: true,
|
||||
container: 'body',
|
||||
title: generate_tooltip(d)
|
||||
}).tooltip('show')
|
||||
}
|
||||
|
||||
function generate_tooltip(d) {
|
||||
var tooltipText = d.isRoot ? d.Galaxy.name : d.GalaxyCluster.description;
|
||||
return $('<div></div>').append(
|
||||
$('<span></span>').text(tooltipText)
|
||||
)[0].outerHTML
|
||||
}
|
||||
</script>
|
|
@ -33,12 +33,14 @@
|
|||
$(document).ready(function () {
|
||||
<?php
|
||||
$uri = "/galaxy_clusters/index/" . $galaxy['Galaxy']['id'];
|
||||
if (isset($passedArgsArray) && isset($passedArgsArray['context'])) {
|
||||
if (isset($passedArgsArray) && isset($passedArgsArray['context']) && $passedArgsArray['context'] == 'fork_tree') {
|
||||
$uri = '/galaxies/forkTree/' . $galaxy['Galaxy']['id'];
|
||||
} elseif (isset($passedArgsArray) && isset($passedArgsArray['context'])) {
|
||||
$uri .= '/context:' . $passedArgsArray['context'];
|
||||
}
|
||||
if (isset($passedArgsArray) && isset($passedArgsArray['searchall'])) {
|
||||
$uri .= '/searchall:' . $passedArgsArray['searchall'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
$.get("<?php echo h($uri);?>", function(data) {
|
||||
$("#clusters_div").html(data);
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
'active' => $context === 'org',
|
||||
'url' => sprintf('%s/galaxies/view/%s/context:org', $baseurl, $galaxy_id),
|
||||
'text' => __('My Galaxy Clusters'),
|
||||
),
|
||||
array(
|
||||
'active' => $context === 'fork_tree',
|
||||
'url' => sprintf('%s/galaxies/view/%s/context:fork_tree', $baseurl, $galaxy_id),
|
||||
'text' => __('View fork tree'),
|
||||
)
|
||||
)
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue