mirror of https://github.com/MISP/MISP
Merge branch '2.4' of github.com:MISP/MISP into 2.4
commit
a6b8a4a85b
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -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')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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
|
|
@ -141,3 +141,7 @@ label.center-in-network-header {
|
|||
.flushright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.btn[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue