Merge branch '2.4' of github.com:MISP/MISP into 2.4

pull/3483/head
chrisr3d 2018-07-12 18:29:45 +02:00
commit a6b8a4a85b
19 changed files with 1176 additions and 37 deletions

View File

@ -46,7 +46,7 @@ class AppController extends Controller {
public $helpers = array('Utility', 'OrgImg');
private $__queryVersion = '42';
private $__queryVersion = '43';
public $pyMispVersion = '2.4.93';
public $phpmin = '5.6.5';
public $phprec = '7.0.16';
@ -117,6 +117,8 @@ class AppController extends Controller {
$language = Configure::read('MISP.language');
if (!empty($language) && $language !== 'eng') {
Configure::write('Config.language', $language);
} else {
Configure::write('Config.language', 'eng');
}
//if fresh installation (salt empty) generate a new salt

View File

@ -481,6 +481,11 @@ class ACLComponent extends Component {
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'index' => array('*'),
),
'eventGraph' => array(
'view' => array('*'),
'add' => array('perm_add'),
'delete' => array('perm_modify'),
)
);

View File

@ -180,6 +180,13 @@ class RestResponseComponent extends Component {
'description' => "POST a body and a subject in a JSON to send an e-mail through MISP to the user ID given in the URL",
'mandatory' => array('subject', 'body')
)
),
'EventGraph' => array(
'add' => array(
'description' => "POST a network in JSON format to this API to to keep an history of it",
'mandatory' => array('event_id', 'network_json'),
'optional' => array('network_name')
)
)
);

View File

@ -0,0 +1,147 @@
<?php
App::uses('AppController', 'Controller');
class EventGraphController extends AppController {
public $components = array(
'Security',
'RequestHandler'
);
public function beforeFilter() {
parent::beforeFilter();
}
public function view($event_id = false) {
if ($event_id === false) throw new MethodNotAllowedException(__('No event ID set.'));
// retreive current org_id
$org_id = $this->_checkOrg();
// validate event
$this->loadModel('Event');
if (Validation::uuid($event_id)) {
$temp = $this->Event->find('first', array('recursive' => -1, 'fields' => array('Event.id'), 'conditions' => array('Event.uuid' => $eventId)));
if (empty($temp)) throw new NotFoundException('Invalid event');
$event_id = $temp['Event']['id'];
} else if (!is_numeric($event_id)) {
throw new NotFoundException(__('Invalid event'));
}
$event = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $event_id));
if (empty($event)) throw new NotFoundException('Invalid event');
// fetch eventGraphs
$eventGraphs = $this->EventGraph->find('all', array(
'order' => 'EventGraph.timestamp DESC',
'conditions' => array(
'EventGraph.event_id' => $event_id,
'EventGraph.org_id' => $org_id
),
'contain' => array(
'User' => array(
'fields' => array(
'User.email'
)
)
)
));
return $this->RestResponse->viewData($eventGraphs, $this->response->type());
}
public function add($event_id = false) {
if ($this->request->is('get')) {
if ($this->_isRest()) {
return $this->RestResponse->describe('EventGraph', 'add', false, $this->response->type());
}
$formURL = 'eventGraph_add_form';
if (!$this->_isSiteAdmin() && (!$this->userRole['perm_modify'] && !$this->userRole['perm_modify_org']) ) {
throw new NotFoundException(__('Invalid event'));
}
$this->set('action', 'add');
$this->set('event_id', $event_id);
$this->render('ajax/' . $formURL);
} else {
if (empty($event_id)) throw new MethodNotAllowedException(__('No event ID set.'));
$this->loadModel('Event');
$event = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $event_id));
if (empty($event)) throw new NotFoundException('Invalid event');
$eventGraph = array();
if (!$this->_isSiteAdmin() && ($event['Event']['orgc_id'] != $this->Auth->user('org_id') && !$this->userRole['perm_modify'])) {
throw new UnauthorizedException(__('You do not have permission to do that.'));
} else {
$eventGraph['EventGraph']['event_id'] = $event_id;
}
if (!isset($this->request->data['EventGraph']['network_json'])) {
throw new MethodNotAllowedException('No network data set');
} else {
$eventGraph['EventGraph']['network_json'] = $this->request->data['EventGraph']['network_json'];
}
if (!isset($this->request->data['EventGraph']['network_name'])) {
$eventGraph['EventGraph']['network_name'] = null;
} else {
$eventGraph['EventGraph']['network_name'] = $this->request->data['EventGraph']['network_name'];
}
if (isset($this->request->data['EventGraph']['preview_img'])) {
$eventGraph['EventGraph']['preview_img'] = $this->request->data['EventGraph']['preview_img'];
}
// Network pushed will be the owner of the authentication key
$eventGraph['EventGraph']['user_id'] = $this->Auth->user('id');
$eventGraph['EventGraph']['org_id'] = $this->Auth->user('org_id');
$result = $this->EventGraph->save($eventGraph, true, array(
'event_id',
'network_json',
'network_name',
'timestamp',
'user_id',
'org_id',
'preview_img',
)
);
if ($result) {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'eventGraph saved.')), 'status'=>200, 'type' => 'json'));
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'eventGraph could not be saved.')), 'status'=>200, 'type' => 'json'));
}
}
}
public function delete($id) {
if (!$this->request->is('post')) {
$this->set('id', $id);
$this->render('ajax/eventGraph_delete_form');
} else {
$this->set('id', $id);
$conditions = array('id' => $id);
if (!$this->_isSiteAdmin()) {
$conditions['org_id'] = $this->Auth->user('org_id');
}
$eventGraph = $this->EventGraph->find('first', array(
'conditions' => $conditions,
'recursive' => -1,
'fields' => array('id', 'event_id', 'user_id'),
));
if (empty($eventGraph)) throw new NotFoundException('Invalid EventGraph');
if ($this->request->is('post')) {
// only creator (or siteAdmin) can delete the eventGraph
if (($eventGraph['EventGraph']['user_id'] != $this->Auth->user()['id']) && !$this->_isSiteAdmin()) throw new MethodNotAllowedException('This eventGraph does not belong to you.');
$result = $this->EventGraph->delete($id);
if ($result) {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'EventGraph deleted.')), 'status'=>200, 'type' => 'json'));
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'EventGraph not deleted.')), 'status'=>200, 'type' => 'json'));
}
}
}
}
}

View File

@ -816,7 +816,7 @@ class ServersController extends AppController {
if ($tab == 'diagnostics' || $tab == 'download') {
$php_ini = php_ini_loaded_file();
$this->set('php_ini', $php_ini);
$advanced_attachments = shell_exec('python ' . APP . 'files/scripts/generate_file_objects.py -c');
$advanced_attachments = shell_exec('python3 ' . APP . 'files/scripts/generate_file_objects.py -c');
try {
$advanced_attachments = json_decode($advanced_attachments, true);
} catch (Exception $e) {

View File

@ -960,9 +960,26 @@ class AppModel extends Model {
INDEX `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
break;
case 13:
case 12:
$sqlArray[] = "ALTER TABLE `servers` ADD `skip_proxy` tinyint(1) NOT NULL DEFAULT 0;";
break;
case 13:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS event_graph (
`id` int(11) NOT NULL AUTO_INCREMENT,
`event_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`org_id` int(11) NOT NULL,
`timestamp` int(11) NOT NULL DEFAULT 0,
`network_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci,
`network_json` MEDIUMTEXT NOT NULL,
`preview_img` MEDIUMTEXT,
PRIMARY KEY (id),
INDEX `event_id` (`event_id`),
INDEX `user_id` (`user_id`),
INDEX `org_id` (`org_id`)
INDEX `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

55
app/Model/EventGraph.php Normal file
View File

@ -0,0 +1,55 @@
<?php
App::uses('AppModel', 'Model');
class EventGraph extends AppModel{
public $useTable = 'event_graph';
public $recursive = -1;
public $actsAs = array(
'Containable',
);
public $belongsTo = array(
'Organisation' => array(
'className' => 'Organisation',
'foreignKey' => 'org_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public $validate = array(
'network_json' => array(
'rule' => array('isValidJson'),
'message' => 'The provided eventGraph is not a valid json format',
'required' => true,
),
);
public function beforeValidate($options = array()) {
parent::beforeValidate();
$date = new DateTime();
$this->data['EventGraph']['timestamp'] = $date->getTimestamp();
return true;
}
public function isValidJson($fields) {
$text = $fields['network_json'];
$check = json_decode($text);
if ($check === null) {
return false;
}
return true;
}
}

View File

@ -3892,9 +3892,18 @@ class Server extends AppModel {
public function update($status) {
$final = '';
$cleanup_commands = array(
// (>^-^)> [hacky]
'git checkout app/composer.json 2>&1'
);
foreach ($cleanup_commands as $cleanup_command) {
$final .= $cleanup_command . "\n\n";
exec($cleanup_command, $output);
$final .= implode("\n", $output) . "\n\n";
}
$command1 = 'git pull origin ' . $status['branch'] . ' 2>&1';
$command2 = 'git submodule update --init --recursive 2>&1';
$final = $command1 . "\n\n";
$final .= $command1 . "\n\n";
exec($command1, $output);
$final .= implode("\n", $output) . "\n\n=================================\n\n";
$output = array();

View File

@ -271,7 +271,7 @@ class Sighting extends AppModel {
$tempFile->close();
$scriptFile = APP . "files" . DS . "scripts" . DS . "stixsighting2misp.py";
// Execute the python script and point it to the temporary filename
$result = shell_exec('python ' . $scriptFile . ' ' . $randomFileName);
$result = shell_exec('python3 ' . $scriptFile . ' ' . $randomFileName);
// The result of the script will be a returned JSON object with 2 variables: success (boolean) and message
// If success = 1 then the temporary output file was successfully written, otherwise an error message is passed along
$result = json_decode($result, true);

View File

@ -12,6 +12,8 @@
<label id="network-physic" class="btn center-in-network-header network-control-btn"><span class="useCursorPointer fa fa-space-shuttle" style="margin-right: 3px;"></span><?php echo __('Physics')?></label>
<label id="network-display" class="btn center-in-network-header network-control-btn"><span class="useCursorPointer fa fa-list-alt" style="margin-right: 3px;"></span><?php echo __('Display')?></label>
<label id="network-filter" class="btn center-in-network-header network-control-btn"><span class="useCursorPointer fa fa-filter" style="margin-right: 3px;"></span><?php echo __('Filters')?></label>
<label id="network-import" class="btn center-in-network-header network-control-btn"><span class="useCursorPointer fa fa-exchange" style="margin-right: 3px;"></span><?php echo __('Export')?></label>
<label id="network-history" class="btn center-in-network-header network-control-btn"><span class="useCursorPointer fa fa-history" style="margin-right: 3px;"></span><?php echo __('History')?></label>
<input type="text" id="network-typeahead" class="center-in-network-header network-typeahead flushright" data-provide="typeahead" size="20" placeholder="Search for an item">
</div>
@ -20,7 +22,7 @@
<span id="fullscreen-btn-eventgraph" class="fullscreen-btn btn btn-xs btn-primary" data-toggle="tooltip" data-placement="top" data-title="<?php echo __('Toggle fullscreen');?>"><span class="fa fa-desktop"></span></span>
<div id="eventgraph_shortcuts_background" class="eventgraph_network_background"></div>
<div id="eventgraph_network" class="eventgraph_network" data-event-id="<?php echo h($event['Event']['id']); ?>" data-user-manipulation="<?php echo $mayModify || $isSiteAdmin ? 'true' : 'false'; ?>" data-extended="<?php echo $extended; ?>"></div>
<div id="eventgraph_network" class="eventgraph_network" data-event-id="<?php echo h($event['Event']['id']); ?>" data-event-timestamp="<?php echo h($event['Event']['timestamp']); ?>" data-user-manipulation="<?php echo $mayModify || $isSiteAdmin ? 'true' : 'false'; ?>" data-extended="<?php echo $extended; ?>" data-user-email="<?php echo h($me['email']);?>" data-is-site-admin="<?php echo $isSiteAdmin ? 'true' : 'false'; ?>"></div>
<div class="loading-network-div" id="refecences_network_loading_div" style="display: none;">
<div class="spinner-network" data-original-title="" title=""></div>
<div class="loadingText-network" data-original-title="" title=""></div>

View File

@ -0,0 +1,24 @@
<?php
$url_params = $action == 'add' ? 'add/' . $event_id : 'edit/' . $eventGraph['id'];
echo $this->Form->create('EventGraph', array('url' => '/EventGraph/' . $url_params));
?>
<fieldset>
<legend><?php echo $action == 'add' ? __('Add EventGraph') : __('Edit EventGraph'); ?></legend>
<div class="add_eventgraph_fields">
<?php
echo $this->Form->hidden('event_id');
echo $this->Form->input('network_name', array(
'type' => 'text'
));
echo $this->Form->input('network_json', array(
'type' => 'textarea'
));
echo $this->Form->input('preview_img', array(
'type' => 'textarea'
));
echo $this->Form->button(__($action));
echo $this->Form->end();
?>
</div>
</fieldset>

View File

@ -0,0 +1,27 @@
<div class="confirmation">
<?php
echo $this->Form->create('EventGraph', array('style' => 'margin:0px;', 'id' => 'PromptForm'));
?>
<legend><?php echo __('EventGraph Deletion'); ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<?php
$message = __('Are you sure you want to delete eventGraph #%s? The eventGraph will be permanently deleted and unrecoverable.', h($id));
?>
<p><?php echo $message; ?></p>
<table>
<tr>
<td style="vertical-align:top">
<span id="PromptYesButton" title="<?php echo __('Delete'); ?>" role="button" tabindex="0" aria-label="<?php echo __('Delete'); ?>" class="btn btn-primary" onClick="submitDeletion(<?php echo 'scope_id'; ?>, 'delete', 'eventGraph', '<?php echo h($id) ;?>')"><?php echo __('Yes'); ?></span>
</td>
<td style="width:540px;">
</td>
<td style="vertical-align:top;">
<span class="btn btn-inverse" title="<?php echo __('No'); ?>" role="button" tabindex="0" aria-label="<?php echo __('No'); ?>" id="PromptNoButton" onClick="cancelPrompt();"><?php echo __('No'); ?></span>
</td>
</tr>
</table>
</div>
<?php
echo $this->Form->end();
?>
</div>

@ -1 +1 @@
Subproject commit b7d52a8bac7be7da93cd3c17b24114f918a6ff1f
Subproject commit 308774755c8d5b01bfd30ea0ab868c83fdfdd715

@ -1 +1 @@
Subproject commit 89e733022d47d7c3af1d085b9c29fbd95844a260
Subproject commit cc1910f1eef286f24fe2d5c3aefe88519d9d312c

View File

@ -141,3 +141,7 @@ label.center-in-network-header {
.flushright {
float: right;
}
.btn[disabled] {
cursor: not-allowed;
}

View File

@ -4,13 +4,35 @@ class ActionTable {
this.options = options;
this.id = options.id;
this.container = options.container;
this.classes = options.classes;
this.table_title = options.title;
this.header = options.header;
this.onAddition = options.onAddition;
this.onRemove = options.onRemove;
this.header.push("Action");
this.row_num = this.header.length;
this.data = options.data == undefined ? [] : options.data;
this.data = options.data === undefined ? [] : options.data;
this.tr_id_mapping = {};
this.control_items = options.control_items;
this.header_action_button = options.header_action_button === undefined ? {} : options.header_action_button;
if (options.header_action_button !== undefined) {
this.header_action_button_style = this.header_action_button.style === undefined ? {} : this.header_action_button.style;
this.additionEnabled = this.header_action_button.additionEnabled === undefined ? true : this.header_action_button.additionEnabled;
this.additionButtonDisabled = this.header_action_button.disabled === undefined ? false : this.header_action_button.disabled;
} else {
this.header_action_button_style = {};
this.additionEnabled = true;
this.additionButtonDisabled = false;
}
this.row_action_button = options.row_action_button === undefined ? {} : options.row_action_button;
if (options.row_action_button !== undefined) {
this.row_action_button_style = this.row_action_button.style === undefined ? {} : this.row_action_button.style;
this.removalEnabled = this.row_action_button.removalEnabled === undefined ? true : this.row_action_button.removalEnabled;
} else {
this.row_action_button_style = {};
this.removalEnabled = true;
}
this.selects = {};
@ -24,8 +46,9 @@ class ActionTable {
add_row(row) {
if (!this.__data_already_exists(row)) {
var id = this.__add_row(row);
this.tr_id_mapping[this.data.length] = id;
this.data.push(row);
this.__add_row(row);
}
}
@ -37,10 +60,38 @@ class ActionTable {
this.data.splice(data_index, 1);
}
delete_row_index(row_pos) {
var tr = this.get_DOM_row(row_pos);
var array = this.__get_array_from_DOM_row(tr);
var data_index = this.__find_array_index(array, this.data);
tr.outerHTML = "";
this.data.splice(data_index, 1);
}
get_DOM_row(row_pos) {
var row_id = this.tr_id_mapping[row_pos];
var tr = document.getElementById(row_id);
return tr;
}
get_data() {
return this.data;
}
clear_table() {
var dataLength = this.data.length;
for (var i=0; i<dataLength; i++) {
this.delete_row_index(i);
}
}
set_table_data(data) {
this.clear_table();
for (var i in data) {
this.add_row(data[i]);
}
}
add_options(id, values) {
var select = this.selects[id];
var selected_value = select.value;
@ -80,6 +131,11 @@ class ActionTable {
this.form = document.createElement('form');
this.table = document.createElement('table');
this.table.classList.add("table", "table-bordered", "action-table");
if (this.classes !== undefined) {
for (var i in this.classes) {
this.table.classList.add(this.classes[i]);
}
}
this.thead = document.createElement('thead');
this.tbody = document.createElement('tbody');
var trHead = document.createElement('tr');
@ -103,7 +159,7 @@ class ActionTable {
__add_row(row) {
var tr = document.createElement('tr');
tr.id = "tr_" + this.__get_uniq_index();
tr.id = "tr_" + this.__uuidv4();
for (var col of row) {
var td = document.createElement('td');
td.innerHTML = col;
@ -111,21 +167,39 @@ class ActionTable {
}
this.__add_action_button(tr);
this.tbody.appendChild(tr);
return tr.id;
}
__add_control_row() {
var tr = document.createElement('tr');
for (var item of this.control_items) {
for (var itemOption of this.control_items) {
var td = document.createElement('td');
var item = this.__add_control_item(item);
var item = this.__add_control_item(itemOption);
if (itemOption.colspan !== undefined) {
td.colSpan = itemOption.colspan;
}
td.appendChild(item);
tr.appendChild(td);
}
var td = document.createElement('td');
var btn = document.createElement('button');
btn.classList.add("btn", "btn-primary");
btn.innerHTML = '<span class="fa fa-plus-square"></span>';
var header_action_button_style = this.header_action_button.style === undefined ? {} : this.header_action_button.style;
if (header_action_button_style.type !== undefined) {
btn.classList.add("btn", "btn-"+header_action_button_style.type);
} else {
btn.classList.add("btn", "btn-primary");
}
if (header_action_button_style.tooltip !== undefined) {
btn.title = header_action_button_style.tooltip;
}
if (header_action_button_style.icon !== undefined) {
btn.innerHTML = '<span class="fa '+header_action_button_style.icon+'"></span>';
} else {
btn.innerHTML = '<span class="fa fa-plus-square"></span>';
}
btn.type = "button";
btn.disabled = this.additionButtonDisabled;
var that = this;
btn.addEventListener("click", function(evt) {
@ -135,13 +209,16 @@ class ActionTable {
data.push(elem.value);
}
}
that.add_row(data);
if (that.additionEnabled) {
that.add_row(data);
}
if (that.onAddition !== undefined) {
that.onAddition(data);
that.onAddition(data, that);
}
});
td.appendChild(btn);
tr.appendChild(td);
this.thead.appendChild(tr);
}
@ -156,6 +233,9 @@ class ActionTable {
case "input":
item = this.__create_input(options.item_options);
break;
case "empty":
item = this.__create_empty(options.item_options);
break;
default:
break;
}
@ -163,24 +243,92 @@ class ActionTable {
}
__add_action_button(tr) {
var that = this;
var td = document.createElement('td');
var btn = document.createElement('button');
btn.classList.add("btn", "btn-danger");
btn.innerHTML = '<span class="fa fa-trash-o"></span>';
btn.type = "button";
btn.setAttribute('rowID', tr.id);
if (that.row_action_button_style.tooltip !== undefined) {
btn.title = that.row_action_button_style.tooltip;
}
if (that.row_action_button_style.style !== undefined) {
btn.style = that.row_action_button_style.style;
}
var that = this;
btn.addEventListener("click", function(evt) {
that.delete_row(this.getAttribute('rowID'));
if (that.onRemove !== undefined) {
var tr = document.getElementById(this.getAttribute('rowID'));
var data = that.__get_array_from_DOM_row(tr);
that.onRemove(data, that);
}
if (that.removalEnabled) {
that.delete_row(this.getAttribute('rowID'));
}
});
td.appendChild(btn);
if (that.row_action_button.others !== undefined) {
for (var i in that.row_action_button.others) {
var newBtnOptions = that.row_action_button.others[i];
var btn_style = newBtnOptions.style !== undefined ? newBtnOptions.style : {};
var btn = document.createElement('button');
btn.type = "button";
if (btn_style.type !== undefined) {
btn.classList.add("btn", "btn-"+btn_style.type);
} else {
btn.classList.add("btn", "btn-primary");
}
if (btn_style.icon !== undefined) {
btn.innerHTML = '<span class="fa '+btn_style.icon+'"></span>';
} else {
btn.innerHTML = '<span class="fa fa-check"></span>';
}
if (btn_style.title !== undefined) {
btn.title = btn_style.title;
}
if (btn_style.style !== undefined) {
btn.style = btn_style.style+"margin-left: 3px";
} else {
btn.style = "margin-left: 3px";
}
btn.setAttribute('rowID', tr.id);
if (newBtnOptions.event !== undefined) {
btn.addEventListener("click", function(evt) {
var tr = document.getElementById(this.getAttribute('rowID'));
var data = that.__get_array_from_DOM_row(tr);
newBtnOptions.event(data, that);
});
}
td.appendChild(btn);
}
}
tr.appendChild(td);
}
__create_empty(options) {
var empty = document.createElement('span');
empty.classList.add("form-group");
empty.id = options.id !== undefined ? options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
return empty;
}
__create_input(options) {
var input = document.createElement('input');
input.classList.add("form-group");
input.id = options.id !== undefined ? options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
if (options.style !== undefined) {
input.style = options.style;
}
if (options.placeholder !== undefined) {
input.placeholder = options.placeholder;
}
if (options.disabled !== undefined) {
input.disabled = options.disabled;
}
if (options.typeahead !== undefined) {
var typeaheadOption = options.typeahead;
$('#'+input.id).typeahead(typeaheadOption);
@ -213,4 +361,11 @@ class ActionTable {
select.appendChild(option);
}
}
__uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}

View File

@ -54,6 +54,18 @@ class ContextualMenu {
this.items[select.id] = select;
}
add_select_button(options) {
this.__create_divider_if_needed('select_button');
var select_button = this.__create_select_button(options);
this.items[select_button.id] = select_button;
}
add_fileinput(options) {
this.__create_divider_if_needed('fileinput');
var fileinput = this.__create_fileinput(options);
this.items[fileinput.id] = fileinput;
}
add_action_table(options) {
this.__create_divider_if_needed('action_table');
var action_table = this.__create_action_table(options);
@ -110,9 +122,9 @@ class ContextualMenu {
if (this.right_click) {
this.trigger_container.addEventListener('contextmenu', function(evt) {
evt.preventDefault();
var offsetX = 0;
var offsetY = 1;
that.__toggleMenu(evt.pageX+offsetX, evt.pageY+offsetY);
var offsetX = $(that.trigger_container).offset().left;
var offsetY = $(that.trigger_container).offset().top-40;
that.__toggleMenu(evt.pageX-offsetX, evt.pageY-offsetY);
});
// hide the contextual menu on any click
document.getElementsByTagName("BODY")[0].addEventListener("click", function(evt) {
@ -210,6 +222,54 @@ class ContextualMenu {
return input;
}
__create_fileinput(options) {
var div = document.createElement('div');
var label = document.createElement('label');
label.innerHTML = options.label+":";
var input = document.createElement("input");
input.classList.add("form-group");
input.id = options.id === undefined ? 'contextualMenu_'+this.__get_uniq_index() : options.id;
if(options.tooltip !== undefined) {
input.title = options.tooltip;
}
input.type = "file";
input.accept = ".json";
var file_status = document.createElement('span');
file_status.id = input.id + "_status";
input.dataset.relatedStatusId = file_status.id;
if(options.event !== undefined) {
input.addEventListener("change", function(evtInput) {
var file = this.files[0];
if (file) {
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = function (evtReader) {
document.getElementById(evtInput.target.dataset.relatedStatusId).innerHTML = "File loaded";
var content = evtReader.target.result;
evtInput.target.value = '';
options.event(content);
};
reader.onerror = function (evtReader) {
document.getElementById(evtInput.target.dataset.relatedStatusId).innerHTML = "Error while reading the file";
};
reader.onprogress = function (evtReader) {
if (evtReader.lengthComputable) {
var loaded = (evtReader.loaded / evtReader.total)*100;
if (loaded < 100) {
document.getElementById(evtInput.target.dataset.relatedStatusId).innerHTML = "Reading file: "+loaded.toFixed(2)+"%";
}
}
};
}
});
}
div.appendChild(label);
div.appendChild(input);
div.appendChild(file_status);
this.menu.appendChild(div);
return input;
}
__create_slider(options) {
var div = document.createElement('div');
var label = document.createElement('label');
@ -235,6 +295,9 @@ class ContextualMenu {
options.event(evt.target.value);
});
}
$(slider).on('reflectOnSpan', function(evt) {
span.innerHTML = evt.target.value; // Update associated span
});
div.appendChild(label);
div.appendChild(span);
if (options.applyButton !== undefined) {
@ -285,6 +348,38 @@ class ContextualMenu {
return select;
}
__create_select_button(options) {
var div = document.createElement('div');
div.style = "width: inherit;";
var select = document.createElement('select');
var label = document.createElement('label');
var button = document.createElement('button');
button.classList.add("btn-dropdown", "btn", "btn-default");
button.style = "padding: 4px 12px; line-height: 20px; margin-left: 7px;";
button.id = options.id === undefined ? 'contextualMenu_'+this.__get_uniq_index() : options.id+"_btn";
button.innerHTML = options.textButton !== undefined ? options.textButton : "";
label.innerHTML = options.label+":";
label.title = options.tooltip;
select.id = options.id === undefined ? 'contextualMenu_'+this.__get_uniq_index() : options.id+"_select";
button.dataset.correspondingId = select.id;
this.__add_options_to_select(select, options.options);
if(options.default !== undefined) {
select.value = options.default;
}
div.appendChild(select);
div.appendChild(button);
this.menu.appendChild(label);
this.menu.appendChild(div);
if(options.event !== undefined) {
button.addEventListener("click", function(evt) {
var corresponding_select_id = evt.target.dataset.correspondingId;
var selected_value = $('#'+corresponding_select_id).val();
options.event(selected_value);
});
}
return button;
}
__add_options_to_select(select, options) {
for(var value of options) {
var option = document.createElement('option');

View File

@ -8,9 +8,12 @@ var nodes = new vis.DataSet();
var edges = new vis.DataSet();
var typeaheadDataSearch;
var event_last_change = $('#eventgraph_network').data('event-timestamp');
var scope_id = $('#eventgraph_network').data('event-id');
var user_email = $('#eventgraph_network').data('user-email');
var container = document.getElementById('eventgraph_network');
var user_manipulation = $('#eventgraph_network').data('user-manipulation');
var is_siteadmin = $('#eventgraph_network').data('is-site-admin');
var root_id_attr = "rootNode:attribute";
var root_id_object = "rootNode:object";
var root_id_tag = "rootNode:tag";
@ -51,6 +54,8 @@ class EventGraph {
this.menu_display = this.init_display_menu();
this.menu_filter = this.init_filter_menu();
this.menu_canvas = this.init_canvas_menu();
this.menu_import = this.init_import_menu();
this.menu_history = this.init_history_menu();
this.new_edges_for_unreferenced_nodes = [];
this.layout = 'default';
this.solver = 'barnesHut';
@ -61,7 +66,9 @@ class EventGraph {
nodes: this.nodes,
edges: this.edges
};
this.hiddenNode = new vis.DataSet();
this.object_templates = {};
this.canvasContext;
this.cluster_index = 0; // use to get uniq cluster ID
this.clusters = [];
@ -89,8 +96,10 @@ class EventGraph {
this.network.on("dragStart", function (params) {
eventGraph.physics_state(false);
eventGraph.physics_activate_physics_for_nodes(params.nodes);
});
this.network.on("dragEnd", function (params) {
eventGraph.physics_disable_physics_for_nodes(params.nodes);
eventGraph.physics_state($('#checkbox_physics_enable').prop("checked"));
});
@ -113,6 +122,17 @@ class EventGraph {
drawExtendedEventHull(ctx, nodes, chosen_color, "Event "+event_id);
}
});
this.network.on("afterDrawing", function (ctx) {
that.canvasContext = ctx;
});
this.network.on("oncontext", function (event) {
var node = that.network.getNodeAt({x: event.pointer.DOM.x, y: event.pointer.DOM.y});
if (node !== undefined) {
that.network.selectNodes([node]);
}
});
}
// Util
@ -149,7 +169,7 @@ class EventGraph {
trigger_container: document.getElementById("network-scope"),
bootstrap_popover: true,
style: "z-index: 1",
container: document.getElementById("eventgraph_div")
container: document.getElementById("eventgraph_div"),
});
menu_scope.add_select({
id: "select_graph_scope",
@ -327,7 +347,7 @@ class EventGraph {
{
DOMType: "select",
item_options: {
options: ["Contains", "Do not contain"]
options: ["Contains", "Do not contain"],
}
},
{
@ -431,7 +451,7 @@ class EventGraph {
if (selected_id === undefined) { // A node is selected
return;
}
eventGraph.nodes.remove(selected_id);
eventGraph.hideNode([selected_id]);
}
});
menu_canvas.add_button({
@ -456,6 +476,151 @@ class EventGraph {
eventGraph.collapse_node(selected_id);
}
});
return menu_canvas;
}
init_import_menu() {
var menu_import = new ContextualMenu({
trigger_container: document.getElementById("network-import"),
bootstrap_popover: true,
style: "z-index: 1",
container: document.getElementById("eventgraph_div")
});
menu_import.add_select_button({
id: "select_button_graph_import_export",
label: "Export",
tooltip: "Export graph",
textButton: "Export",
event: function(selected_value) {
if (selected_value == 'json') {
var jsonData = eventGraph.toJSON();
download_file(jsonData, 'json');
} else if (selected_value == 'png' || selected_value == 'jpeg') {
var dataURL = eventGraph.canvasContext.canvas.toDataURL('image/'+selected_value);
download_file(dataURL, selected_value);
} else if (selected_value == 'DOT Language') {
var hiddenNodeIds = [];
eventGraph.hiddenNode.forEach(function(node) {
hiddenNodeIds.push(node.id);
});
var nodePositions = eventGraph.network.getPositions();
var validNodes = eventGraph.nodes.get({ filter: function (nodeD) {
var nodeP = nodePositions[nodeD.id];
if (nodeP !== undefined) {
return true;
}
return false;
}});
var dotData = convert_to_dot_lang(validNodes, eventGraph.edges, hiddenNodeIds);
download_file(dotData, 'dot');
}
},
options: ["json", "png", "jpeg", "DOT Language"],
default: "json"
});
return menu_import;
}
init_history_menu() {
var menu_history= new ContextualMenu({
trigger_container: document.getElementById("network-history"),
bootstrap_popover: true,
style: "z-index: 1",
container: document.getElementById("eventgraph_div")
});
menu_history.add_action_table({
id: "table_graph_history_actiontable",
container: menu_history.menu,
title: "Network history",
header: ["Id", "Name", "Owner", "Date"],
control_items: [
{
DOMType: "input",
colspan: 4,
item_options: {
style: "width: 98%;",
placeholder: "Network's name",
id: "networkHistory_input_name_save",
disabled: !user_manipulation
}
}
],
header_action_button: {
additionEnabled: false,
style: {
type: "success",
icon: "fa-save",
tooltip: "Save network"
},
disabled: !user_manipulation
},
row_action_button: {
removalEnabled: false,
style: {
tooltip: "Delete saved network"
},
others: [
{
style: {
type: "success",
icon: "fa-share ",
tooltip: "Load saved network"
},
event: function(data) {
var network_id = data[0];
dataHandler.fetch_and_import_graph(network_id);
}
}
]
},
data: [],
onAddition: function(network_name, selfTable) {
var network_json = eventGraph.toJSON();
var preview = eventGraph.canvasContext.canvas.toDataURL('image/png', 0.1);
mispInteraction.save_network(network_json, network_name, preview);
$('#networkHistory_input_name_save').val('');
},
onRemove: function(data, selfTable) {
mispInteraction.delete_saved_network(data);
}
});
menu_history.items["table_graph_history_actiontable"].table.style.minWidth = "450px";
// fill history table
// has to do it manually here (not using reset_graph_history) because menu_history still not constructed yet
dataHandler.fetch_graph_history(function(history_formatted, network_previews) {
menu_history.items["table_graph_history_actiontable"].set_table_data(history_formatted);
for(var i=0; i<history_formatted.length; i++) {
var history = history_formatted[i];
var cur_email = history[2];
var tr = eventGraph.menu_history.items.table_graph_history_actiontable.get_DOM_row(i);
if (!(cur_email == user_email || is_siteadmin)) {
// disable delete button
var btn_del = $(tr).find('.btn-danger');
btn_del.prop('disabled', true);
}
// set tooltip preview
var preview = network_previews[i];
if (typeof preview == 'string') {
var btn_plot = $(tr).find('.btn-success');
btn_plot.data('network-preview', preview);
btn_plot.popover({
container: 'body',
content: function() { return '<img style="width: 500px; height: 150px;" src="' + $(this).data('network-preview') + '" />'; },
placement: 'right',
trigger: 'hover',
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content" style="width: 500px; height: 150px;"></div></div>',
html: true,
});
}
}
});
return menu_history;
}
get_filtering_rules() {
@ -730,6 +895,25 @@ class EventGraph {
that.solver = solver;
}
physics_disable_physics_for_nodes(nodes) {
var update = [];
nodes.forEach(function(nodeId) {
if (!eventGraph.network.isCluster(nodeId)) {
update.push({id: nodeId, fixed: {x: true, y: true}});
}
});
eventGraph.nodes.update(update);
}
physics_activate_physics_for_nodes(nodes) {
var update = [];
nodes.forEach(function(nodeId) {
if (!eventGraph.network.isCluster(nodeId)) {
update.push({id: nodeId, fixed: {x: false, y: false}});
}
});
eventGraph.nodes.update(update);
}
// state true: loading
// state false: finished
network_loading(state, message) {
@ -748,10 +932,12 @@ class EventGraph {
if(parent_id === undefined) { return; }
if (!(parent_id == root_id_attr || parent_id == root_id_object || parent_id == root_id_tag || parent_id == root_id_keyType)) { // Is not a root node
var node_group = this.nodes.get(parent_id).group;
var parent_node = this.nodes.get(parent_id);
var node_group = parent_node.group;
if (parent_id === undefined || node_group != 'object') { // No node selected or collapse not permitted
return
}
parent_node.expanded = false;
var connected_nodes_ids = this.network.getConnectedNodes(parent_id);
var connected_nodes = this.nodes.get(connected_nodes_ids);
for (var node of connected_nodes) {
@ -764,6 +950,7 @@ class EventGraph {
this.nodes.remove(node.id);
}
}
this.nodes.update(parent_node);
} else { // Is a root node
this.clusterize(parent_id);
}
@ -777,6 +964,7 @@ class EventGraph {
|| parent_node.group != "object") { // Cannot expand attribute
return;
}
parent_node.expanded = true;
var objAttributes = parent_node.Attribute;
var newNodes = [];
@ -824,6 +1012,7 @@ class EventGraph {
this.nodes.add(newNodes);
this.edges.add(newRelations);
this.nodes.update(parent_node);
} else { // is a cluster
if(this.network.getNodesInCluster(parent_id).length > cluster_expand_threshold) {
@ -836,6 +1025,26 @@ class EventGraph {
}
}
expand_previous_expansion(nodes) {
var that = this;
for (var id in nodes) {
if (nodes.hasOwnProperty(id)) {
var node = nodes[id];
if (node.expanded) {
eventGraph.expand_node(node.id);
}
}
}
}
hideNode(nodeIds) {
nodeIds.forEach(function(nodeId) {
var node = eventGraph.nodes.get(nodeId);
eventGraph.hiddenNode.add(node);
eventGraph.nodes.remove(nodeId);
});
}
link_not_referenced_nodes() {
// unlink previously linked
this.edges.remove(this.new_edges_for_unreferenced_nodes)
@ -874,7 +1083,6 @@ class EventGraph {
new_edge.from = root_id_tag;
that.nodes.update({id: nodeData.id, unreferenced: 'tag'});
}
} else if (that.scope_name == 'Correlation') {
} else { // specified key
if (cur_group == 'attribute' || cur_group == 'object') {
new_edge.from = root_id_keyType;
@ -1039,7 +1247,7 @@ class EventGraph {
that.network_loading(false, "");
}
destroy_and_redraw() {
destroy_and_redraw(callback) {
var that = eventGraph;
that.network.destroy();
that.network = null;
@ -1047,6 +1255,58 @@ class EventGraph {
that.network = new vis.Network(container, data, that.network_options);
that.init_clusterize();
that.bind_listener();
if (callback !== undefined) {
callback();
}
}
toJSON() {
var nodeData = [];
var nodePositions = eventGraph.network.getPositions();
eventGraph.nodes.get().forEach(function(nodeD) {
var nodeP = nodePositions[nodeD.id];
if (nodeP !== undefined && nodeD.group != 'obj_relation') {
var temp = {
id: nodeD.id,
x: nodeP.x,
y: nodeP.y,
};
if (nodeD.fixed !== undefined) {
temp.fixed = nodeD.fixed;
}
if (nodeD.expanded !== undefined) {
temp.expanded = nodeD.expanded;
}
nodeData.push(temp);
}
});
var hiddenNodeData = [];
eventGraph.hiddenNode.forEach(function(node) {
hiddenNodeData.push(node.id);
});
var data = {
eventId: scope_id,
eventLastChange: event_last_change,
nodes: nodeData,
hiddenNodes: hiddenNodeData,
scope: {
scope: eventGraph.scope_name,
keyType: eventGraph.scope_keyType
},
physics: {
solver: eventGraph.solver,
repulsion: parseInt($('#slider_physic_node_repulsion').val()),
enabled: $('#checkbox_physics_enable').prop("checked")
},
display: {
layout: eventGraph.layout,
label: dataHandler.selected_type_to_display,
charLength: parseInt($("#slider_display_max_char_num").val())
}
};
var jsonData = JSON.stringify(data);
return jsonData;
}
}
@ -1059,6 +1319,7 @@ class DataHandler {
this.mapping_uuid_to_template = new Map();
this.selected_type_to_display = "";
this.extended_event = $('#eventgraph_network').data('extended') == 1 ? true : false;
this.networkHistoryJsonData = new Map();
this.scope_name;
}
@ -1110,7 +1371,7 @@ class DataHandler {
eventGraph.menu_filter.items["table_attr_value"].add_options("table_control_select_attr_value", available_object_references);
}
fetch_data_and_update(stabilize) {
fetch_data_and_update(stabilize, callback) {
eventGraph.network_loading(true, loadingText_fetching);
$.when(this.fetch_objects_template()).done(function() {
var filtering_rules = eventGraph.get_filtering_rules();
@ -1152,6 +1413,9 @@ class DataHandler {
if ( stabilize === undefined || stabilize) {
eventGraph.reset_view_on_stabilized();
}
if (callback !== undefined) {
callback();
}
},
error: function( jqXhr, textStatus, errorThrown ){
console.log( errorThrown );
@ -1175,6 +1439,46 @@ class DataHandler {
});
}
// same event, same timestamp
validateImportedFile(data) {
if (scope_id != data.eventId) {
showMessage('fail', '<b>Failed</b> to import file: Event '+data.eventId+' not compatible with event '+scope_id);
return false;
}
if (parseInt(event_last_change) < parseInt(data.eventLastChange)) {
showMessage('fail', '<b>Fail</b>: Imported graph is newer than current event');
return false;
}
if (parseInt(event_last_change) > parseInt(data.eventLastChange)) {
showMessage('success', '<b>Warning</b>: Imported graph is not the latest version');
}
return true;
}
fetch_graph_history(callback) {
$.getJSON( "/eventGraph/view/"+scope_id, function( history ) {
var history_formatted = [];
var network_previews = [];
history.forEach(function(item) {
history_formatted.push([
item['EventGraph']['id'],
item['EventGraph']['network_name'],
item['User']['email'],
new Date(parseInt(item['EventGraph']['timestamp'])*1000).toLocaleString()
]);
dataHandler.networkHistoryJsonData.set(item['EventGraph']['id'], item['EventGraph']['network_json']);
network_previews.push(item['EventGraph']['preview_img']);
});
callback(history_formatted, network_previews);
});
}
fetch_and_import_graph(network_id) {
var data = dataHandler.networkHistoryJsonData.get(network_id);
var json = JSON.parse(data);
import_graph_from_json(json);
}
get_typeaheadData_search() {
var to_ret = []
for( var entry of this.mapping_value_to_nodeID) {
@ -1313,6 +1617,92 @@ class MispInteraction {
window.location = '/objects/edit/'+id;
}
}
save_network(network_json, network_name, network_preview) {
var network_json = eventGraph.toJSON();
this.quickSaveNetworkHistory(scope_id, network_json, network_name, network_preview, reset_graph_history);
}
delete_saved_network(data) {
var network_id = data[0];
var url = "/" + "eventGraph" + "/" + "delete" + "/" + network_id;
$.get(url, function(data) {
openPopup("#confirmation_box");
$("#confirmation_box").html(data);
});
}
quickSaveNetworkHistory(event_id, network_json, network_name, network_preview, callback) {
this.networkFetchForm('add', event_id, undefined, function(form) {
var container = $('#eventgraph_network');
// append the form somewhere
container.append(form);
var url = form.attr('action');
// locate wanted field and set the value
var field_network_json = form.find('#' + 'EventGraph' + 'NetworkJson');
field_network_json.val(network_json);
var field_network_name = form.find('#' + 'EventGraph' + 'NetworkName');
field_network_name.val(network_name);
var field_network_preview = form.find('#' + 'EventGraph' + 'PreviewImg');
field_network_preview.val(network_preview);
// submit the form
$.ajax({
data: form.serialize(),
cache: false,
beforeSend: function(XMLHttpRequest) {
$('.loading').show();
},
success: function(data, textStatus) {
showMessage('success', 'Network has been saved');
if (callback !== undefined) {
callback();
}
},
error: function( jqXhr, textStatus, errorThrown ){
showMessage('fail', 'Could not save network');
console.log( errorThrown );
},
complete: function() {
$(".loading").hide();
form.remove();
},
type: 'post',
url: url
});
});
}
networkFetchForm(type, event_id, network_id, callback) {
var url = '/' + 'EventGraph' + '/' + 'add' + '/' + event_id;
$.ajax({
beforeSend: function(XMLHttpRequest) {
$('.loading').show();
},
dataType: 'html',
cache: false,
success: function(data, textStatus) {
var form = $(data);
form.css('display', 'none');
if (callback !== undefined) {
callback(form);
} else {
return form;
}
},
error: function( jqXhr, textStatus, errorThrown ){
console.log( errorThrown );
},
complete: function() {
$(".loading").hide();
},
type: 'get',
url: url
});
}
}
@ -1447,6 +1837,194 @@ function genericPopupCallback(result) {
}
function download_file(data, type) {
var dataUri;
var filename = 'graphExport_'+ parseInt(new Date().getTime()/1000);
if (type == 'json') {
dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(data);
filename += '.json';
} else if (type == 'png' || type == 'jpeg') {
dataUri = data;
filename += type;
} else if (type == 'dot') {
dataUri = 'data:text/x-graphviz;charset=utf-8,' + encodeURIComponent(data);
filename += '.dot';
}
var a = document.createElement('a');
a.setAttribute('href', dataUri);
a.setAttribute('download', filename);
a.click();
}
function reset_graph_history() {
var table = eventGraph.menu_history.items["table_graph_history_actiontable"];
dataHandler.fetch_graph_history(function(history_formatted, network_previews) {
table.set_table_data(history_formatted);
for(var i=0; i<history_formatted.length; i++) {
var history = history_formatted[i];
var cur_email = history[2];
var tr = eventGraph.menu_history.items.table_graph_history_actiontable.get_DOM_row(i);
if (!(cur_email == user_email || is_siteadmin)) {
// disable delete button
var btn_del = $(tr).find('.btn-danger');
btn_del.prop('disabled', true);
}
// set tooltip preview
var preview = network_previews[i];
if (typeof preview == 'string') {
var btn_plot = $(tr).find('.btn-success');
btn_plot.data('network-preview', preview);
btn_plot.popover({
container: 'body',
content: function() { return '<img style="width: 500px; height: 150px;" src="' + $(this).data('network-preview') + '" />'; },
placement: 'right',
trigger: 'hover',
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content" style="width: 500px; height: 150px;"></div></div>',
html: true,
});
}
}
});
}
function import_graph_from_json(data) {
if (dataHandler.validateImportedFile(data)) {
// set options
eventGraph.scope_name = data.scope;
eventGraph.scope_keyType = data.scope.keyType;
eventGraph.update_scope(data.scope.scope)
var layoutVal;
switch(data.display.layout) {
case "default":
layoutVal = 'default';
break;
case "directed":
layoutVal = 'hierarchical.directed';
break;
case "hubsize":
layoutVal = 'hierarchical.hubsize';
break;
default:
layoutVal = 'default';
}
$('#select_display_layout').val(layoutVal);
eventGraph.change_layout_type(data.display.layout);
dataHandler.selected_type_to_display = data.display.label;
$('#select_display_object_field').val(data.display.label);
$("#slider_display_max_char_num").val(data.display.charLength);
$('#slider_display_max_char_num').trigger('reflectOnSpan');
eventGraph.solver = data.physics.solver;
eventGraph.physics_change_solver(data.physics.solver)
$('#select_physic_solver').val(data.physics.solver);
$('#slider_physic_node_repulsion').val(data.physics.repulsion);
$('#slider_physic_node_repulsion').trigger('reflectOnSpan');
eventGraph.physics_change_repulsion(data.physics.repulsion)
eventGraph.physics_state(data.physics.enabled)
$('#checkbox_physics_enable').prop('checked', data.physics.enabled);
// update data
dataHandler.fetch_data_and_update(false, function() {
eventGraph.nodes.update(data.nodes);
eventGraph.expand_previous_expansion(data.nodes);
eventGraph.hiddenNode.clear();
eventGraph.hideNode(data.hiddenNodes);
});
}
}
function escapeQuote(str) {
return str.replace(/"/g, '\\\"');
}
function convert_to_dot_lang(nodes, edges, hiddenNodeIds) {
var mappingStringDic = new Map(); // in case the id is not an int, map it to a letter
var dotNodes = [];
var validNodeId = {};
nodes.forEach(function(node) {
if (hiddenNodeIds.indexOf(node.id) != -1) return;
var nodeId = node.id;
if (node.id != parseInt(node.id, 10)) {
nodeId = 'autgenerated_id_'+mappingStringDic.size;
mappingStringDic.set(node.id, nodeId);
}
var dnode = {
id: nodeId,
shape: node.group == 'object' ? 'box' : 'ellipse',
label: escapeQuote(node.label),
style: 'filled',
};
switch(node.group) {
case 'object':
dnode.fillcolor = node.icon.color;
break;
case 'tag':
dnode.fillcolor = node.color.background;
break;
case 'keyType':
dnode.fillcolor = node.color.background;
break;
default:
dnode.fillcolor = '#f3a500';
break;
}
validNodeId[nodeId] = true;
dotNodes.push(dnode);
});
var dotNodesStr = "";
dotNodes.forEach(function(node) {
var nodeAttr = "";
for (var attr in node) {
if (!node.hasOwnProperty(attr)) continue;
if (attr=='id') continue;
nodeAttr += attr + "=\"" + node[attr] + "\" ";
}
dotNodesStr += node.id + " ["+nodeAttr+"];\n";
});
var dotEdges = [];
edges.forEach(function(edge) {
if (edge.to.includes("rootNode:")) return; // drop root nodes
if (edge.from.includes("rootNode:")) return; // drop root nodes
var from = edge.from;
if (edge.from != parseInt(edge.from, 10)) {
from = mappingStringDic.get(edge.from);
}
var to = edge.to;
if (edge.to != parseInt(edge.to, 10)) {
to = mappingStringDic.get(edge.to);
}
var dedge = {
from: from,
to: to,
label: edge.label !== undefined ? escapeQuote(edge.label) : "",
color: edge.color.color !== undefined ? edge.color.color : "#597ce9",
dirType: edge.label !== undefined ? "forward" : "none",
};
dotEdges.push(dedge);
});
var dotEdgesStr = "";
dotEdges.forEach(function(edge) {
if (hiddenNodeIds.indexOf(edge.from) != -1 || hiddenNodeIds.indexOf(edge.to) != -1) return;
var edgeAttr = "";
for (var attr in edge) {
if (!edge.hasOwnProperty(attr)) continue;
if (attr=='id' || attr=='from' || attr=='to') continue;
edgeAttr += attr + "=\"" + edge[attr] + "\" ";
}
dotEdgesStr += edge.from + " -> " + edge.to + " ["+edgeAttr+"];\n";
});
var dotLang = "digraph network_event_"+scope_id+" {\n";
dotLang += dotNodesStr;
dotLang += "\n";
dotLang += dotEdgesStr;
dotLang += "}";
return dotLang;
}
// Called when the user click on the 'Event graph' toggle
function enable_interactive_graph() {
@ -1490,6 +2068,9 @@ function enable_interactive_graph() {
}
return;
}
if (evt.target !== undefined && $(evt.target).is('input')) {
return;
}
switch(evt.keyCode) {
case 88: // x
var selected_id = eventGraph.network.getSelectedNodes()[0];
@ -1610,7 +2191,7 @@ var network_options = {
addNode: mispInteraction.add_item,
editNode: mispInteraction.edit_item,
deleteNode: mispInteraction.delete_item,
deleteEdge: mispInteraction.remove_reference
deleteEdge: mispInteraction.remove_reference,
},
physics: {
enabled: true,
@ -1701,7 +2282,6 @@ var network_options = {
color: 'white'
},
mass: 25
},
rootNodeObject: {
shape: 'icon',
@ -1713,7 +2293,8 @@ var network_options = {
size: 18, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
mass: 5,
physics: false
},
rootNodeAttribute: {
shape: 'icon',
@ -1725,7 +2306,8 @@ var network_options = {
size: 18, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
mass: 5,
physics: false
},
rootNodeKeyType: {
shape: 'icon',
@ -1737,7 +2319,8 @@ var network_options = {
size: 22, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
mass: 5,
physics: false
},
rootNodeTag: {
shape: 'icon',
@ -1749,7 +2332,8 @@ var network_options = {
size: 22, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
mass: 5,
physics: false
},
clustered_object: {
shape: 'icon',
@ -1761,7 +2345,8 @@ var network_options = {
size: 18, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
mass: 5,
physics: false
}
},
locales: {

View File

@ -85,8 +85,13 @@ function submitDeletion(context_id, action, type, id) {
},
data: formData,
success:function (data, textStatus) {
updateIndex(context_id, context);
handleGenericAjaxResponse(data);
if (type == 'eventGraph') {
showMessage('success', 'Network has been deleted');
reset_graph_history();
} else {
updateIndex(context_id, context);
handleGenericAjaxResponse(data);
}
},
complete:function() {
$(".loading").hide();