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->set('object', $object[0]);
|
||||||
$this->render('/Events/ajax/ajaxGalaxies');
|
$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) {
|
if ($temp === null) {
|
||||||
throw new NotFoundException('Invalid galaxy cluster');
|
throw new NotFoundException('Invalid galaxy cluster');
|
||||||
}
|
}
|
||||||
$id = $temp['Galaxy']['id'];
|
$id = $temp['GalaxyCluster']['id'];
|
||||||
} elseif (!is_numeric($id)) {
|
} elseif (!is_numeric($id)) {
|
||||||
throw new NotFoundException(__('Invalid galaxy cluster'));
|
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,11 +33,13 @@
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
<?php
|
<?php
|
||||||
$uri = "/galaxy_clusters/index/" . $galaxy['Galaxy']['id'];
|
$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'];
|
$uri .= '/context:' . $passedArgsArray['context'];
|
||||||
}
|
if (isset($passedArgsArray) && isset($passedArgsArray['searchall'])) {
|
||||||
if (isset($passedArgsArray) && isset($passedArgsArray['searchall'])) {
|
$uri .= '/searchall:' . $passedArgsArray['searchall'];
|
||||||
$uri .= '/searchall:' . $passedArgsArray['searchall'];
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
$.get("<?php echo h($uri);?>", function(data) {
|
$.get("<?php echo h($uri);?>", function(data) {
|
||||||
|
|
|
@ -29,6 +29,11 @@
|
||||||
'active' => $context === 'org',
|
'active' => $context === 'org',
|
||||||
'url' => sprintf('%s/galaxies/view/%s/context:org', $baseurl, $galaxy_id),
|
'url' => sprintf('%s/galaxies/view/%s/context:org', $baseurl, $galaxy_id),
|
||||||
'text' => __('My Galaxy Clusters'),
|
'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