mirror of https://github.com/MISP/MISP
new: [timeline/*-seen] Initial import of the timeline code from the
zoidberg branchpull/4743/head
parent
3bcd7c57a3
commit
e7f3d0d9df
|
@ -919,7 +919,7 @@ class AttributesController extends AppController
|
|||
$skipTimeCheck = true;
|
||||
}
|
||||
if ($skipTimeCheck || $this->request->data['Attribute']['timestamp'] > $existingAttribute['Attribute']['timestamp']) {
|
||||
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment');
|
||||
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'first_seen', 'last_seen');
|
||||
foreach ($recoverFields as $rF) {
|
||||
if (!isset($this->request->data['Attribute'][$rF])) {
|
||||
$this->request->data['Attribute'][$rF] = $existingAttribute['Attribute'][$rF];
|
||||
|
@ -958,6 +958,10 @@ class AttributesController extends AppController
|
|||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Could not update attribute, reason: ' . json_encode($this->Attribute->validationErrors))),'status' => 200, 'type' => 'json'));
|
||||
}
|
||||
} else {
|
||||
if (!$result) {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Could not update attribute, reason: ' . json_encode($this->Attribute->validationErrors))),'status' => 200, 'type' => 'json'));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($result) {
|
||||
|
@ -975,10 +979,11 @@ class AttributesController extends AppController
|
|||
}
|
||||
}
|
||||
if ($this->_isRest() || $this->response->type() === 'application/json') {
|
||||
$searchFields = array('id', 'type', 'to_ids', 'category', 'uuid', 'event_id', 'distribution', 'timestamp', 'comment', 'value', 'disable_correlation', 'first_seen', 'last_seen');
|
||||
$saved_attribute = $this->Attribute->find('first', array(
|
||||
'conditions' => array('id' => $this->Attribute->id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'type', 'to_ids', 'category', 'uuid', 'event_id', 'distribution', 'timestamp', 'comment', 'value', 'disable_correlation'),
|
||||
'fields' => $searchFields,
|
||||
));
|
||||
$response = array('response' => array('Attribute' => $saved_attribute['Attribute']));
|
||||
$this->set('response', $response);
|
||||
|
@ -1113,7 +1118,7 @@ class AttributesController extends AppController
|
|||
if (!$this->_isRest()) {
|
||||
$this->Attribute->Event->insertLock($this->Auth->user(), $this->Attribute->data['Attribute']['event_id']);
|
||||
}
|
||||
$validFields = array('value', 'category', 'type', 'comment', 'to_ids', 'distribution');
|
||||
$validFields = array('value', 'category', 'type', 'comment', 'to_ids', 'distribution', 'first_seen', 'last_seen');
|
||||
$changed = false;
|
||||
if (empty($this->request->data['Attribute'])) {
|
||||
$this->request->data = array('Attribute' => $this->request->data);
|
||||
|
@ -1721,7 +1726,7 @@ class AttributesController extends AppController
|
|||
unset($this->request->data['to_ids']);
|
||||
$this->request->data['ignore'] = 1;
|
||||
}
|
||||
$paramArray = array('value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags');
|
||||
$paramArray = array('value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags', 'first_seen', 'last_seen');
|
||||
$filterData = array(
|
||||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
|
@ -1934,7 +1939,7 @@ class AttributesController extends AppController
|
|||
'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
|
||||
'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
|
||||
'includeProposals', 'returnFormat', 'published', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
|
||||
'includeWarninglistHits', 'attackGalaxy', 'object_relation'
|
||||
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'first_seen', 'last_seen'
|
||||
);
|
||||
$filterData = array(
|
||||
'request' => $this->request,
|
||||
|
@ -2389,7 +2394,7 @@ class AttributesController extends AppController
|
|||
|
||||
public function fetchViewValue($id, $field = null)
|
||||
{
|
||||
$validFields = array('value', 'comment', 'type', 'category', 'to_ids', 'distribution', 'timestamp');
|
||||
$validFields = array('value', 'comment', 'type', 'category', 'to_ids', 'distribution', 'timestamp', 'first_seen', 'last_seen');
|
||||
if (!isset($field) || !in_array($field, $validFields)) {
|
||||
throw new MethodNotAllowedException(__('Invalid field requested.'));
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ class ACLComponent extends Component
|
|||
'getEventGraphReferences' => array('*'),
|
||||
'getEventGraphTags' => array('*'),
|
||||
'getEventGraphGeneric' => array('*'),
|
||||
'getEventTimeline' => array('*'),
|
||||
'genDistributionGraph' => array('*'),
|
||||
'getDistributionGraph' => array('*'),
|
||||
'getReferenceData' => array('*'),
|
||||
|
@ -250,6 +251,11 @@ class ACLComponent extends Component
|
|||
'edit' => array('perm_add'),
|
||||
'get_row' => array('perm_add'),
|
||||
'orphanedObjectDiagnostics' => array(),
|
||||
'editField' => array('perm_add'),
|
||||
'fetchEditForm' => array('perm_add'),
|
||||
'fetchViewValue' => array('*'),
|
||||
'quickAddAttributeForm' => array('perm_add'),
|
||||
'quickFetchTemplateWithValidObjectAttributes' => array('perm_add'),
|
||||
'proposeObjectsFromAttributes' => array('*'),
|
||||
'groupAttributesIntoObject' => array('perm_add'),
|
||||
'revise_object' => array('perm_add'),
|
||||
|
|
|
@ -20,13 +20,13 @@ class RestResponseComponent extends Component
|
|||
'add' => array(
|
||||
'description' => "POST a MISP Attribute JSON to this API to create an Attribute.",
|
||||
'mandatory' => array('value', 'type'),
|
||||
'optional' => array('category', 'to_ids', 'uuid', 'distribution', 'sharing_group_id', 'timestamp', 'comment'),
|
||||
'optional' => array('category', 'to_ids', 'uuid', 'distribution', 'sharing_group_id', 'timestamp', 'comment', 'first_seen', 'last_seen'),
|
||||
'params' => array('event_id')
|
||||
),
|
||||
'edit' => array(
|
||||
'description' => "POST a MISP Attribute JSON to this API to update an Attribute. If the timestamp is set, it has to be newer than the existing Attribute.",
|
||||
'mandatory' => array(),
|
||||
'optional' => array('value', 'type', 'category', 'to_ids', 'uuid', 'distribution', 'sharing_group_id', 'timestamp', 'comment'),
|
||||
'optional' => array('value', 'type', 'category', 'to_ids', 'uuid', 'distribution', 'sharing_group_id', 'timestamp', 'comment', 'first_seen', 'last_seen'),
|
||||
'params' => array('attribute_id')
|
||||
),
|
||||
'deleteSelected' => array(
|
||||
|
@ -382,6 +382,7 @@ class RestResponseComponent extends Component
|
|||
if (isset($this->__convertActionToMessage[$controller][$action['action']])) {
|
||||
$stringifiedAction = $this->__convertActionToMessage[$controller][$action['action']];
|
||||
}
|
||||
$response['saved'] = false;
|
||||
$response['name'] = 'Could not ' . $stringifiedAction . ' ' . Inflector::singularize($controller);
|
||||
$response['message'] = $response['name'];
|
||||
$response['url'] = $this->__generateURL($action, $controller, $id);
|
||||
|
@ -395,6 +396,7 @@ class RestResponseComponent extends Component
|
|||
if (!$message) {
|
||||
$message = Inflector::singularize($controller) . ' ' . $action['action'] . ((substr($action['action'], -1) == 'e') ? 'd' : 'ed');
|
||||
}
|
||||
$response['saved'] = true;
|
||||
$response['name'] = $message;
|
||||
$response['message'] = $response['name'];
|
||||
$response['url'] = $this->__generateURL($action, $controller, $id);
|
||||
|
|
|
@ -4752,6 +4752,32 @@ class EventsController extends AppController
|
|||
return $json;
|
||||
}
|
||||
|
||||
public function getEventTimeline($id, $type = 'event')
|
||||
{
|
||||
$validTools = array('event');
|
||||
if (!in_array($type, $validTools)) {
|
||||
throw new MethodNotAllowedException('Invalid type.');
|
||||
}
|
||||
|
||||
App::uses('EventTimelineTool', 'Tools');
|
||||
$grapher = new EventTimelineTool();
|
||||
$data = $this->request->is('post') ? $this->request->data : array();
|
||||
$dataFiltering = array_key_exists('filtering', $data) ? $data['filtering'] : array();
|
||||
|
||||
$extended = isset($this->params['named']['extended']) ? 1 : 0;
|
||||
|
||||
$grapher->construct($this->Event, $this->Auth->user(), $dataFiltering, $extended);
|
||||
$json = $grapher->get_timeline($id);
|
||||
|
||||
array_walk_recursive($json, function (&$item, $key) {
|
||||
if (!mb_detect_encoding($item, 'utf-8', true)) {
|
||||
$item = utf8_encode($item);
|
||||
}
|
||||
});
|
||||
$this->response->type('json');
|
||||
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
|
||||
}
|
||||
|
||||
public function getDistributionGraph($id, $type = 'event')
|
||||
{
|
||||
$extended = isset($this->params['named']['extended']) ? 1 : 0;
|
||||
|
|
|
@ -353,7 +353,7 @@ class ObjectsController extends AppController
|
|||
$this->set('element', $element);
|
||||
}
|
||||
|
||||
public function edit($id, $update_template_available=false)
|
||||
public function edit($id, $update_template_available=false, $onlyAddNewAttribute=false)
|
||||
{
|
||||
if (Validation::uuid($id)) {
|
||||
$conditions = array('Object.uuid' => $id);
|
||||
|
@ -532,7 +532,7 @@ class ObjectsController extends AppController
|
|||
$this->request->data = array('Attribute' => $this->request->data);
|
||||
}
|
||||
$objectToSave = $this->MispObject->attributeCleanup($this->request->data);
|
||||
$objectToSave = $this->MispObject->deltaMerge($object, $objectToSave);
|
||||
$objectToSave = $this->MispObject->deltaMerge($object, $objectToSave, $onlyAddNewAttribute);
|
||||
// we pre-validate the attributes before we create an object at this point
|
||||
// This allows us to stop the process and return an error (API) or return
|
||||
// to the add form
|
||||
|
@ -595,6 +595,303 @@ class ObjectsController extends AppController
|
|||
$this->render('add');
|
||||
}
|
||||
|
||||
// ajax edit - post a single edited field and this method will attempt to save it and return a json with the validation errors if they occur.
|
||||
public function editField($id)
|
||||
{
|
||||
if (Validation::uuid($id)) {
|
||||
$this->MispObject->recursive = -1;
|
||||
$temp = $this->MispObject->findByUuid($id);
|
||||
if ($temp == null) {
|
||||
throw new NotFoundException('Invalid object');
|
||||
}
|
||||
$id = $temp['Object']['id'];
|
||||
} elseif (!is_numeric($id)) {
|
||||
throw new NotFoundException(__('Invalid event id.'));
|
||||
}
|
||||
if ((!$this->request->is('post') && !$this->request->is('put'))) {
|
||||
throw new MethodNotAllowedException();
|
||||
}
|
||||
$this->MispObject->id = $id;
|
||||
if (!$this->MispObject->exists()) {
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'edit', false, 'Invalid object', $this->response->type());
|
||||
}
|
||||
$this->MispObject->recursive = -1;
|
||||
$this->MispObject->contain('Event');
|
||||
$object = $this->MispObject->read();
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
if ($this->MispObject->data['Event']['orgc_id'] == $this->Auth->user('org_id')
|
||||
&& (($this->userRole['perm_modify'] && $this->MispObject->data['Event']['user_id'] != $this->Auth->user('id'))
|
||||
|| $this->userRole['perm_modify_org'])) {
|
||||
// Allow the edit
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'edit', false, 'Invalid attribute', $this->response->type());
|
||||
}
|
||||
}
|
||||
$validFields = array('comment', 'distribution', 'first_seen', 'last_seen');
|
||||
$changed = false;
|
||||
if (empty($this->request->data['Object'])) {
|
||||
$this->request->data = array('Object' => $this->request->data);
|
||||
if (empty($this->request->data['Object'])) {
|
||||
throw new MethodNotAllowedException('Invalid input.');
|
||||
}
|
||||
}
|
||||
foreach ($this->request->data['Object'] as $changedKey => $changedField) {
|
||||
if (!in_array($changedKey, $validFields)) {
|
||||
throw new MethodNotAllowedException('Invalid field.');
|
||||
}
|
||||
if ($object['Object'][$changedKey] == $changedField) {
|
||||
$this->autoRender = false;
|
||||
return $this->RestResponse->saveSuccessResponse('Objects', 'edit', $id, $this->response->type(), 'nochange');
|
||||
}
|
||||
$object['Object'][$changedKey] = $changedField;
|
||||
$changed = true;
|
||||
}
|
||||
if (!$changed) {
|
||||
return $this->RestResponse->saveSuccessResponse('Objects', 'edit', $id, $this->response->type(), 'nochange');
|
||||
}
|
||||
$date = new DateTime();
|
||||
$object['Object']['timestamp'] = $date->getTimestamp();
|
||||
$this->MispObject->setObjectSeenMetaFromAttribute($object, true);
|
||||
if ($this->MispObject->save($object)) {
|
||||
$event = $this->MispObject->Event->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'published', 'timestamp', 'info', 'uuid'),
|
||||
'conditions' => array(
|
||||
'id' => $object['Object']['event_id'],
|
||||
)));
|
||||
$event['Event']['timestamp'] = $date->getTimestamp();
|
||||
$event['Event']['published'] = 0;
|
||||
$this->MispObject->Event->save($event, array('fieldList' => array('published', 'timestamp', 'info')));
|
||||
$this->autoRender = false;
|
||||
return $this->RestResponse->saveSuccessResponse('Objects', 'edit', $id, $this->response->type(), 'Field updated');
|
||||
} else {
|
||||
$this->autoRender = false;
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'edit', false, $this->MispObject->validationErrors, $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
public function fetchViewValue($id, $field = null)
|
||||
{
|
||||
$validFields = array('timestamp', 'comment', 'distribution', 'first_seen', 'last_seen');
|
||||
if (!isset($field) || !in_array($field, $validFields)) {
|
||||
throw new MethodNotAllowedException('Invalid field requested.');
|
||||
}
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException('This function can only be accessed via AJAX.');
|
||||
}
|
||||
$this->MispObject->id = $id;
|
||||
if (!$this->MispObject->exists()) {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
$params = array(
|
||||
'conditions' => array('Object.id' => $id),
|
||||
'fields' => array('id', 'distribution', 'event_id', $field),
|
||||
'contain' => array(
|
||||
'Event' => array(
|
||||
'fields' => array('distribution', 'id', 'org_id'),
|
||||
)
|
||||
),
|
||||
'flatten' => 1
|
||||
);
|
||||
$object = $this->MispObject->fetchObjectSimple($this->Auth->user(), $params);
|
||||
if (empty($object)) {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
$object = $object[0];
|
||||
$result = $object['Object'][$field];
|
||||
if ($field == 'distribution') {
|
||||
$result=$this->MispObject->shortDist[$result];
|
||||
}
|
||||
$this->set('value', $result);
|
||||
$this->layout = 'ajax';
|
||||
$this->render('ajax/objectViewFieldForm');
|
||||
}
|
||||
|
||||
public function fetchEditForm($id, $field = null)
|
||||
{
|
||||
$validFields = array('distribution', 'comment', 'first_seen', 'last_seen');
|
||||
if (!isset($field) || !in_array($field, $validFields)) {
|
||||
throw new MethodNotAllowedException('Invalid field requested.');
|
||||
}
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException('This function can only be accessed via AJAX.');
|
||||
}
|
||||
$this->MispObject->id = $id;
|
||||
if (!$this->MispObject->exists()) {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
$fields = array('id', 'distribution', 'event_id');
|
||||
$fields[] = $field;
|
||||
$params = array(
|
||||
'conditions' => array('Object.id' => $id),
|
||||
'fields' => $fields,
|
||||
'flatten' => 1,
|
||||
'contain' => array(
|
||||
'Event' => array(
|
||||
'fields' => array('distribution', 'id', 'user_id', 'orgc_id', 'org_id'),
|
||||
)
|
||||
)
|
||||
);
|
||||
$object = $this->MispObject->fetchObjectSimple($this->Auth->user(), $params);
|
||||
if (empty($object)) {
|
||||
throw new NotFoundException(__('Invalid attribute'));
|
||||
}
|
||||
$object = $object[0];
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
if ($object['Event']['orgc_id'] == $this->Auth->user('org_id')
|
||||
&& (($this->userRole['perm_modify'] && $object['Event']['user_id'] != $this->Auth->user('id'))
|
||||
|| $this->userRole['perm_modify_org'])) {
|
||||
// Allow the edit
|
||||
} else {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
}
|
||||
$this->layout = 'ajax';
|
||||
if ($field == 'distribution') {
|
||||
$distributionLevels = $this->MispObject->shortDist;
|
||||
unset($distributionLevels[4]);
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
}
|
||||
$this->set('object', $object['Object']);
|
||||
$fieldURL = ucfirst($field);
|
||||
$this->render('ajax/objectEdit' . $fieldURL . 'Form');
|
||||
}
|
||||
|
||||
// Construct a template with valid object attributes to add to an object
|
||||
public function quickFetchTemplateWithValidObjectAttributes($id) {
|
||||
$this->MispObject->id = $id;
|
||||
if (!$this->MispObject->exists()) {
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'add', false, 'Invalid object', $this->response->type());
|
||||
} else {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
}
|
||||
$fields = array('template_uuid', 'template_version', 'id');
|
||||
$params = array(
|
||||
'conditions' => array('Object.id' => $id),
|
||||
'fields' => $fields,
|
||||
'flatten' => 1,
|
||||
);
|
||||
// fetchObjects restrict access based on user
|
||||
$object = $this->MispObject->fetchObjects($this->Auth->user(), $params);
|
||||
if (empty($object)) {
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'add', false, 'Invalid object', $this->response->type());
|
||||
} else {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
} else {
|
||||
$object = $object[0];
|
||||
}
|
||||
// get object attributes already set
|
||||
$objectRelation = array();
|
||||
foreach($object['Attribute'] as $attr) {
|
||||
$objectRelation[$attr['object_relation']] = 1;
|
||||
}
|
||||
$objectRelation = array_keys($objectRelation);
|
||||
// get object attribute defined in the object's template
|
||||
$template = $this->MispObject->ObjectTemplate->find('first', array(
|
||||
'conditions' => array(
|
||||
'ObjectTemplate.uuid' => $object['Object']['template_uuid'],
|
||||
'ObjectTemplate.version' => $object['Object']['template_version'],
|
||||
),
|
||||
'recursive' => -1,
|
||||
'flatten' => 1,
|
||||
'contain' => 'ObjectTemplateElement'
|
||||
));
|
||||
if (empty($template)) {
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'add', false, 'Invalid template', $this->response->type());
|
||||
} else {
|
||||
throw new NotFoundException(__('Invalid template'));
|
||||
}
|
||||
}
|
||||
// unset object invalid object attribute
|
||||
foreach($template['ObjectTemplateElement'] as $i => $objAttr) {
|
||||
if (in_array($objAttr['object_relation'], $objectRelation) && !$objAttr['multiple']) {
|
||||
unset($template['ObjectTemplateElement'][$i]);
|
||||
}
|
||||
}
|
||||
if ($this->request->is('get') || $this->request->is('post')) {
|
||||
$this->set('template', $template);
|
||||
$this->set('objectId', $object['Object']['id']);
|
||||
$this->render('ajax/templateWithValidObjectAttributes');
|
||||
} else {
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET: Returns a form allowing to add a valid object attribute to an object
|
||||
* POST/PUT: Add the attribute to the object
|
||||
*/
|
||||
public function quickAddAttributeForm($id, $fieldName = null) {
|
||||
if ($this->request->is('GET')) {
|
||||
if (!isset($fieldName)) {
|
||||
throw new MethodNotAllowedException('No field requested.');
|
||||
}
|
||||
$this->MispObject->id = $id;
|
||||
if (!$this->MispObject->exists()) {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
$fields = array('template_uuid', 'template_version', 'id', 'event_id');
|
||||
$params = array(
|
||||
'conditions' => array('Object.id' => $id),
|
||||
'fields' => $fields,
|
||||
'flatten' => 1,
|
||||
);
|
||||
// fetchObjects restrict access based on user
|
||||
$object = $this->MispObject->fetchObjects($this->Auth->user(), $params);
|
||||
if (empty($object)) {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
} else {
|
||||
$object = $object[0];
|
||||
}
|
||||
$template = $this->MispObject->ObjectTemplate->find('first', array(
|
||||
'conditions' => array(
|
||||
'ObjectTemplate.uuid' => $object['Object']['template_uuid'],
|
||||
'ObjectTemplate.version' => $object['Object']['template_version'],
|
||||
),
|
||||
'recursive' => -1,
|
||||
'flatten' => 1,
|
||||
'contain' => array(
|
||||
'ObjectTemplateElement' => array('conditions' => array(
|
||||
'object_relation' => $fieldName
|
||||
))
|
||||
)
|
||||
));
|
||||
if (empty($template)) {
|
||||
throw new NotFoundException(__('Invalid object'));
|
||||
}
|
||||
if (empty($template['ObjectTemplateElement'])) {
|
||||
throw new NotFoundException(__('Invalid fields') . ' `' . h($fieldName) . '`');
|
||||
}
|
||||
|
||||
// check if fields can be added
|
||||
foreach($object['Attribute'] as $i => $objAttr) {
|
||||
$objectAttrFromTemplate = $template['ObjectTemplateElement'][0];
|
||||
if ($objAttr['object_relation'] == $fieldName && !$objectAttrFromTemplate['multiple']) {
|
||||
throw new NotFoundException(__('Invalid field'));
|
||||
}
|
||||
}
|
||||
$template = $this->MispObject->prepareTemplate($template, $object);
|
||||
$this->layout = 'ajax';
|
||||
$this->set('object', $object['Object']);
|
||||
$this->set('template', $template);
|
||||
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
|
||||
$this->set('distributionData', $distributionData);
|
||||
$info = array();
|
||||
foreach ($distributionData['levels'] as $key => $value) {
|
||||
$info['distribution'][$key] = array('key' => $value, 'desc' => $this->MispObject->Event->Attribute->distributionDescriptions[$key]['formdesc']);
|
||||
}
|
||||
$this->set('info', $info);
|
||||
$this->render('ajax/quickAddAttributeForm');
|
||||
} else if ($this->request->is('post') || $this->request->is('put')) {
|
||||
return $this->edit($this->request->data['Object']['id'], true);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($id, $hard = false)
|
||||
{
|
||||
if (!$this->userRole['perm_modify']) {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
private function __get_event($id)
|
||||
{
|
||||
$this->__json['available_rotation_key'] = $this->__authorized_JSON_key;
|
||||
$this->__json['available_pivot_key'] = $this->__authorized_JSON_key;
|
||||
|
||||
$fullevent = $this->__eventModel->fetchEvent($this->__user, array('eventid' => $id, 'flatten' => 0, 'includeTagRelations' => 1, 'extended' => $this->__extended_view));
|
||||
$event = array();
|
||||
|
@ -232,12 +232,12 @@
|
|||
$event = $this->__get_filtered_event($id);
|
||||
$this->__json['items'] = array();
|
||||
$this->__json['relations'] = array();
|
||||
|
||||
|
||||
$this->__json['existing_object_relation'] = array();
|
||||
if (empty($event)) {
|
||||
return $this->__json;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($event['Object'])) {
|
||||
$object = $event['Object'];
|
||||
} else {
|
||||
|
@ -312,7 +312,7 @@
|
|||
if (empty($event)) {
|
||||
return $this->__json;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($event['Object'])) {
|
||||
$object = $event['Object'];
|
||||
} else {
|
||||
|
@ -419,7 +419,7 @@
|
|||
if (empty($event)) {
|
||||
return $this->__json;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($event['Object'])) {
|
||||
$object = $event['Object'];
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
class EventTimelineTool
|
||||
{
|
||||
private $__lookupTables = array();
|
||||
private $__user = false;
|
||||
private $__json = array();
|
||||
private $__eventModel = false;
|
||||
private $__refModel = false;
|
||||
# Will be use latter on
|
||||
private $__related_events = array();
|
||||
private $__related_attributes = array();
|
||||
|
||||
public function construct($eventModel, $user, $filterRules, $extended_view=0)
|
||||
{
|
||||
$this->__eventModel = $eventModel;
|
||||
$this->__objectTemplateModel = $eventModel->Object->ObjectTemplate;
|
||||
$this->__user = $user;
|
||||
$this->__filterRules = $filterRules;
|
||||
$this->__json = array();
|
||||
$this->__extended_view = $extended_view;
|
||||
$this->__lookupTables = array(
|
||||
'analysisLevels' => $this->__eventModel->analysisLevels,
|
||||
'distributionLevels' => $this->__eventModel->Attribute->distributionLevels
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function construct_for_ref($refModel, $user)
|
||||
{
|
||||
$this->__refModel = $refModel;
|
||||
$this->__user = $user;
|
||||
$this->__json = array();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function __get_event($id)
|
||||
{
|
||||
$fullevent = $this->__eventModel->fetchEvent($this->__user, array('eventid' => $id, 'flatten' => 0, 'includeTagRelations' => 1, 'extended' => $this->__extended_view));
|
||||
$event = array();
|
||||
if (empty($fullevent)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if (!empty($fullevent[0]['Object'])) {
|
||||
$event['Object'] = $fullevent[0]['Object'];
|
||||
} else {
|
||||
$event['Object'] = array();
|
||||
}
|
||||
|
||||
if (!empty($fullevent[0]['Attribute'])) {
|
||||
$event['Attribute'] = $fullevent[0]['Attribute'];
|
||||
} else {
|
||||
$event['Attribute'] = array();
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
public function get_timeline($id)
|
||||
{
|
||||
$event = $this->__get_event($id);
|
||||
$this->__json['items'] = array();
|
||||
|
||||
if (empty($event)) {
|
||||
return $this->__json;
|
||||
}
|
||||
|
||||
if (!empty($event['Object'])) {
|
||||
$object = $event['Object'];
|
||||
} else {
|
||||
$object = array();
|
||||
}
|
||||
|
||||
if (!empty($event['Attribute'])) {
|
||||
$attribute = $event['Attribute'];
|
||||
} else {
|
||||
$attribute = array();
|
||||
}
|
||||
|
||||
// extract links and node type
|
||||
foreach ($attribute as $attr) {
|
||||
$toPush = array(
|
||||
'id' => $attr['id'],
|
||||
'uuid' => $attr['uuid'],
|
||||
'content' => $attr['value'],
|
||||
'event_id' => $attr['event_id'],
|
||||
'group' => 'attribute',
|
||||
'timestamp' => $attr['timestamp'],
|
||||
'first_seen' => $attr['first_seen'],
|
||||
'last_seen' => $attr['last_seen'],
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
}
|
||||
|
||||
foreach ($object as $obj) {
|
||||
$toPush_obj = array(
|
||||
'id' => $obj['id'],
|
||||
'uuid' => $obj['uuid'],
|
||||
'content' => $obj['name'],
|
||||
'group' => 'object',
|
||||
'meta-category' => $obj['meta-category'],
|
||||
'template_uuid' => $obj['template_uuid'],
|
||||
'event_id' => $obj['event_id'],
|
||||
'timestamp' => $obj['timestamp'],
|
||||
'Attribute' => array(),
|
||||
);
|
||||
|
||||
$toPush_obj['first_seen'] = $obj['first_seen'];
|
||||
$toPush_obj['last_seen'] = $obj['last_seen'];
|
||||
$toPush_obj['first_seen_overwrite'] = false;
|
||||
$toPush_obj['last_seen_overwrite'] = false;
|
||||
|
||||
foreach ($obj['Attribute'] as $obj_attr) {
|
||||
// replaced *_seen based on object attribute
|
||||
if ($obj_attr['object_relation'] == 'first-seen' && is_null($toPush_obj['first_seen'])) {
|
||||
$toPush_obj['first_seen'] = $obj_attr['value']; // replace first_seen of the object to seen of the element
|
||||
$toPush_obj['first_seen_overwrite'] = true;
|
||||
} elseif ($obj_attr['object_relation'] == 'last-seen' && is_null($toPush_obj['last_seen'])) {
|
||||
$toPush_obj['last_seen'] = $obj_attr['value']; // replace last_seen of the object to seen of the element
|
||||
$toPush_obj['last_seen_overwrite'] = true;
|
||||
}
|
||||
$toPush_attr = array(
|
||||
'id' => $obj_attr['id'],
|
||||
'uuid' => $obj_attr['uuid'],
|
||||
'content' => $obj_attr['value'],
|
||||
'contentType' => $obj_attr['object_relation'],
|
||||
'event_id' => $obj_attr['event_id'],
|
||||
'group' => 'object_attribute',
|
||||
'timestamp' => $obj_attr['timestamp'],
|
||||
);
|
||||
array_push($toPush_obj['Attribute'], $toPush_attr);
|
||||
}
|
||||
array_push($this->__json['items'], $toPush_obj);
|
||||
}
|
||||
|
||||
return $this->__json;
|
||||
}
|
||||
}
|
|
@ -79,6 +79,26 @@ class AppModel extends Model
|
|||
);
|
||||
|
||||
public $advanced_updates_description = array(
|
||||
'seenOnAttributeAndObject' => array(
|
||||
'title' => 'First seen/Last seen Attribute table',
|
||||
'description' => 'Update the Attribute table to support first_seen and last_seen feature, with a microsecond resolution.',
|
||||
'liveOff' => true, # should the instance be offline for users other than site_admin
|
||||
'recommendBackup' => true, # should the update recommend backup
|
||||
'exitOnError' => false, # should the update exit on error
|
||||
'requirements' => 'MySQL version must be >= 5.6', # message stating the requirements necessary for the update
|
||||
'record' => true, # should the update success be saved in the admin_table
|
||||
'preUpdate' => 'seenOnAttributeAndObjectPreUpdate', # Function to execute before the update. If it throws an error, it cancels the update
|
||||
'url' => '/servers/updateDatabase/seenOnAttributeAndObject/' # url pointing to the funcion performing the update
|
||||
),
|
||||
'testUpdate' => array(
|
||||
'title' => 'Test Update',
|
||||
'description' => 'Runs a test update',
|
||||
'liveOff' => true,
|
||||
'recommendBackup' => true,
|
||||
'exitOnError' => true,
|
||||
'preUpdate' => 'failingPreUpdate', # Function to execute before the update. If it returns false, cancel the update
|
||||
'url' => '/servers/updateDatabase/testUpdate/'
|
||||
)
|
||||
);
|
||||
public $actions_description = array(
|
||||
'verifyGnuPGkeys' => array(
|
||||
|
@ -1210,6 +1230,55 @@ class AppModel extends Model
|
|||
$sqlArray[] = 'ALTER TABLE `threads` DROP `org`;';
|
||||
$sqlArray[] = 'ALTER TABLE `users` DROP `org`;';
|
||||
break;
|
||||
case 'seenOnAttributeAndObject':
|
||||
$sqlArray[] =
|
||||
"ALTER TABLE `attributes`
|
||||
DROP INDEX uuid,
|
||||
DROP INDEX event_id,
|
||||
DROP INDEX sharing_group_id,
|
||||
DROP INDEX type,
|
||||
DROP INDEX category,
|
||||
DROP INDEX value1,
|
||||
DROP INDEX value2,
|
||||
DROP INDEX object_id,
|
||||
DROP INDEX object_relation,
|
||||
DROP INDEX deleted,
|
||||
;";
|
||||
$sqlArray[] =
|
||||
"ALTER TABLE `attributes`
|
||||
ADD COLUMN `first_seen` BIGINT(20) NULL DEFAULT NULL,
|
||||
ADD COLUMN `last_seen` BIGINT(20) NULL DEFAULT NULL,
|
||||
MODIFY comment TEXT COLLATE utf8_unicode_ci
|
||||
;";
|
||||
$sqlArray[] = "
|
||||
ALTER TABLE `attributes`
|
||||
ADD INDEX `uuid` (`uuid`),
|
||||
ADD INDEX `event_id` (`event_id`),
|
||||
ADD INDEX `sharing_group_id` (`sharing_group_id`),
|
||||
ADD INDEX `type` (`type`),
|
||||
ADD INDEX `category` (`category`),
|
||||
ADD INDEX `value1` (`value1`(255)),
|
||||
ADD INDEX `value2` (`value2`(255)),
|
||||
ADD INDEX `object_id` (`object_id`),
|
||||
ADD INDEX `object_relation` (`object_relation`),
|
||||
ADD INDEX `deleted` (`deleted`),
|
||||
ADD INDEX `first_seen` (`first_seen`),
|
||||
ADD INDEX `last_seen` (`last_seen`),
|
||||
ADD INDEX `comment` (`comment`(767))
|
||||
;";
|
||||
$sqlArray[] = "
|
||||
ALTER TABLE `objects`
|
||||
ADD `first_seen` BIGINT(20) NULL DEFAULT NULL,
|
||||
ADD `last_seen` BIGINT(20) NULL DEFAULT NULL
|
||||
;";
|
||||
$indexArray[] = array('objects', 'first_seen');
|
||||
$indexArray[] = array('objects', 'last_seen');
|
||||
break;
|
||||
case 'testUpdate':
|
||||
$sqlArray[] = "SELECT SLEEP(10);";
|
||||
$sqlArray[] = "SELECT SLEEPsdcfsac(4);";
|
||||
$sqlArray[] = "SELECT SLEEP(12);";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
|
@ -1481,6 +1550,29 @@ class AppModel extends Model
|
|||
return true;
|
||||
}
|
||||
|
||||
// Try to create a table with a BIGINT(20)
|
||||
public function seenOnAttributeAndObjectPreUpdate() {
|
||||
$sqlArray[] = "CREATE TABLE IF NOT EXISTS testtable (
|
||||
`testfield` BIGINT(6) NULL DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
|
||||
try {
|
||||
foreach($sqlArray as $i => $sql) {
|
||||
$this->query($sql);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('Pre update test failed: ' . PHP_EOL . $sql . PHP_EOL . ' The returned error is: ' . $e->getMessage());
|
||||
}
|
||||
// clean up
|
||||
$sqlArray[] = "DROP TABLE testtable;";
|
||||
foreach($sqlArray as $i => $sql) {
|
||||
$this->query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
public function failingPreUpdate() {
|
||||
throw new Exception('Yolo fail');
|
||||
}
|
||||
|
||||
public function runUpdates($verbose = false)
|
||||
{
|
||||
$this->AdminSetting = ClassRegistry::init('AdminSetting');
|
||||
|
|
|
@ -360,7 +360,9 @@ class Attribute extends AppModel
|
|||
'deleted',
|
||||
'disable_correlation',
|
||||
'object_id',
|
||||
'object_relation'
|
||||
'object_relation',
|
||||
'first_seen',
|
||||
'last_seen'
|
||||
);
|
||||
|
||||
public $searchResponseTypes = array(
|
||||
|
@ -508,6 +510,16 @@ class Attribute extends AppModel
|
|||
'rule' => array('inList', array('0', '1', '2', '3', '4', '5')),
|
||||
'message' => 'Options: Your organisation only, This community only, Connected communities, All communities, Sharing group, Inherit event',
|
||||
'required' => true
|
||||
),
|
||||
'first_seen' => array(
|
||||
'rule' => array('datetimeOrNull'),
|
||||
'required' => false,
|
||||
'message' => array('Invalid ISO 8601 format')
|
||||
),
|
||||
'last_seen' => array(
|
||||
'rule' => array('datetimeOrNull'),
|
||||
'required' => false,
|
||||
'message' => array('Invalid ISO 8601 format')
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -586,6 +598,22 @@ class Attribute extends AppModel
|
|||
if (isset($v['Attribute']['object_relation']) && $v['Attribute']['object_relation'] === null) {
|
||||
$results[$k]['Attribute']['object_relation'] = '';
|
||||
}
|
||||
if (!empty($v['Attribute']['first_seen'])) {
|
||||
$fs = $results[$k]['Attribute']['first_seen'];
|
||||
$fs_sec = $fs / 1000000; // $fs is in micro (10^6)
|
||||
$fs_micro = $fs % 1000000;
|
||||
$fs_micro = str_pad($fs_micro, 6, "0", STR_PAD_LEFT);
|
||||
$fs = $fs_sec . '.' . $fs_micro;
|
||||
$results[$k]['Attribute']['first_seen'] = DateTime::createFromFormat('U.u', $fs)->format('Y-m-d\TH:i:s.uP');
|
||||
}
|
||||
if (!empty($v['Attribute']['last_seen'])) {
|
||||
$ls = $results[$k]['Attribute']['last_seen'];
|
||||
$ls_sec = $ls / 1000000; // $ls is in micro (10^6)
|
||||
$ls_micro = $ls % 1000000;
|
||||
$ls_micro = str_pad($ls_micro, 6, "0", STR_PAD_LEFT);
|
||||
$ls = $ls_sec . '.' . $ls_micro;
|
||||
$results[$k]['Attribute']['last_seen'] = DateTime::createFromFormat('U.u', $ls)->format('Y-m-d\TH:i:s.uP');
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
@ -610,6 +638,27 @@ class Attribute extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
// convert into utc and micro sec
|
||||
if (!empty($this->data['Attribute']['first_seen'])) {
|
||||
$d = new DateTime($this->data['Attribute']['first_seen']);
|
||||
$d->setTimezone(new DateTimeZone('GMT'));
|
||||
$fs_sec = $d->format('U');
|
||||
$fs_micro = $d->format('u');
|
||||
$fs_micro = str_pad($fs_micro, 6, "0", STR_PAD_LEFT);
|
||||
$fs = $fs_sec . $fs_micro;
|
||||
$this->data['Attribute']['first_seen'] = $fs;
|
||||
}
|
||||
if (!empty($this->data['Attribute']['last_seen'])) {
|
||||
$d = new DateTime($this->data['Attribute']['last_seen']);
|
||||
$d->setTimezone(new DateTimeZone('GMT'));
|
||||
$ls_sec = $d->format('U');
|
||||
$ls_micro = $d->format('u');
|
||||
$ls_micro = str_pad($ls_micro, 6, "0", STR_PAD_LEFT);
|
||||
$ls = $ls_sec . $ls_micro;
|
||||
$this->data['Attribute']['last_seen'] = $ls;
|
||||
}
|
||||
|
||||
|
||||
// update correlation... (only needed here if there's an update)
|
||||
if ($this->id || !empty($this->data['Attribute']['id'])) {
|
||||
$this->__beforeSaveCorrelation($this->data['Attribute']);
|
||||
|
@ -807,6 +856,16 @@ class Attribute extends AppModel
|
|||
$date = new DateTime();
|
||||
$this->data['Attribute']['timestamp'] = $date->getTimestamp();
|
||||
}
|
||||
|
||||
// parse first_seen different formats
|
||||
if (isset($this->data['Attribute']['first_seen'])) {
|
||||
$this->data['Attribute']['first_seen'] = $this->data['Attribute']['first_seen'] === '' ? null : $this->data['Attribute']['first_seen'];
|
||||
}
|
||||
// parse last_seen different formats
|
||||
if (isset($this->data['Attribute']['last_seen'])) {
|
||||
$this->data['Attribute']['last_seen'] = $this->data['Attribute']['last_seen'] === '' ? null : $this->data['Attribute']['last_seen'];
|
||||
}
|
||||
|
||||
// TODO: add explanatory comment
|
||||
// TODO: i18n?
|
||||
$result = $this->runRegexp($this->data['Attribute']['type'], $this->data['Attribute']['value']);
|
||||
|
@ -939,6 +998,20 @@ class Attribute extends AppModel
|
|||
return $this->runValidation($value, $this->data['Attribute']['type']);
|
||||
}
|
||||
|
||||
// check whether the variable is null or datetime
|
||||
public function datetimeOrNull($fields)
|
||||
{
|
||||
$k = array_keys($fields)[0];
|
||||
$seen = $fields[$k];
|
||||
try {
|
||||
new DateTime($seen);
|
||||
$returnValue = true;
|
||||
} catch (Exception $e) {
|
||||
$returnValue = false;
|
||||
}
|
||||
return $returnValue || is_null($seen);
|
||||
}
|
||||
|
||||
private $__hexHashLengths = array(
|
||||
'authentihash' => 64,
|
||||
'md5' => 32,
|
||||
|
@ -3787,7 +3860,7 @@ class Attribute extends AppModel
|
|||
return true;
|
||||
}
|
||||
// If a field is not set in the request, just reuse the old value
|
||||
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id', 'object_id', 'object_relation');
|
||||
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id', 'object_id', 'object_relation', 'first_seen', 'last_seen');
|
||||
foreach ($recoverFields as $rF) {
|
||||
if (!isset($attribute[$rF])) {
|
||||
$attribute[$rF] = $existingAttribute['Attribute'][$rF];
|
||||
|
@ -3857,7 +3930,9 @@ class Attribute extends AppModel
|
|||
'comment',
|
||||
'sharing_group_id',
|
||||
'deleted',
|
||||
'disable_correlation'
|
||||
'disable_correlation',
|
||||
'first_seen',
|
||||
'last_seen'
|
||||
);
|
||||
if ($objectId) {
|
||||
$fieldList[] = 'object_id';
|
||||
|
|
|
@ -1993,7 +1993,7 @@ class Event extends AppModel
|
|||
|
||||
// do not expose all the data ...
|
||||
$fields = array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.date', 'Event.threat_level_id', 'Event.info', 'Event.published', 'Event.uuid', 'Event.attribute_count', 'Event.analysis', 'Event.timestamp', 'Event.distribution', 'Event.proposal_email_lock', 'Event.user_id', 'Event.locked', 'Event.publish_timestamp', 'Event.sharing_group_id', 'Event.disable_correlation', 'Event.extends_uuid');
|
||||
$fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment', 'Attribute.sharing_group_id', 'Attribute.deleted', 'Attribute.disable_correlation', 'Attribute.object_id', 'Attribute.object_relation');
|
||||
$fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment', 'Attribute.sharing_group_id', 'Attribute.deleted', 'Attribute.disable_correlation', 'Attribute.object_id', 'Attribute.object_relation', 'Attribute.first_seen', 'Attribute.last_seen');
|
||||
$fieldsObj = array('*');
|
||||
$fieldsShadowAtt = array('ShadowAttribute.id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.value', 'ShadowAttribute.to_ids', 'ShadowAttribute.uuid', 'ShadowAttribute.event_uuid', 'ShadowAttribute.event_id', 'ShadowAttribute.old_id', 'ShadowAttribute.comment', 'ShadowAttribute.org_id', 'ShadowAttribute.proposal_to_delete', 'ShadowAttribute.timestamp');
|
||||
$fieldsOrg = array('id', 'name', 'uuid');
|
||||
|
|
|
@ -46,6 +46,8 @@ class MispObject extends AppModel
|
|||
),
|
||||
);
|
||||
|
||||
public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' Sharing Group', 5 => 'Inherit');
|
||||
|
||||
public $validate = array(
|
||||
'uuid' => array(
|
||||
'uuid' => array(
|
||||
|
@ -60,6 +62,51 @@ class MispObject extends AppModel
|
|||
)
|
||||
);
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as $k => $v) {
|
||||
if (!empty($v[$this->alias]['first_seen'])) {
|
||||
$fs = $results[$k][$this->alias]['first_seen'];
|
||||
$fs_sec = intval($fs / 1000000); // $fs is in micro (10^6)
|
||||
$fs_micro = $fs % 1000000;
|
||||
$fs_micro = str_pad($fs_micro, 6, "0", STR_PAD_LEFT);
|
||||
$fs = $fs_sec . '.' . $fs_micro;
|
||||
$results[$k][$this->alias]['first_seen'] = DateTime::createFromFormat('U.u', $fs)->format('Y-m-d\TH:i:s.uP');
|
||||
}
|
||||
if (!empty($v[$this->alias]['last_seen'])) {
|
||||
$ls = $results[$k][$this->alias]['last_seen'];
|
||||
$ls_sec = intval($ls / 1000000); // $ls is in micro (10^6)
|
||||
$ls_micro = $ls % 1000000;
|
||||
$ls_micro = str_pad($ls_micro, 6, "0", STR_PAD_LEFT);
|
||||
$ls = $ls_sec . '.' . $ls_micro;
|
||||
$results[$k][$this->alias]['last_seen'] = DateTime::createFromFormat('U.u', $ls)->format('Y-m-d\TH:i:s.uP');
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function beforeSave($options = array()) {
|
||||
// convert into utc and micro sec
|
||||
if (!empty($this->data[$this->alias]['first_seen'])) {
|
||||
$d = new DateTime($this->data[$this->alias]['first_seen']);
|
||||
$d->setTimezone(new DateTimeZone('GMT'));
|
||||
$fs_sec = $d->format('U');
|
||||
$fs_micro = $d->format('u');
|
||||
$fs_micro = str_pad($fs_micro, 6, "0", STR_PAD_LEFT);
|
||||
$fs = $fs_sec . $fs_micro;
|
||||
$this->data[$this->alias]['first_seen'] = $fs;
|
||||
}
|
||||
if (!empty($this->data[$this->alias]['last_seen'])) {
|
||||
$d = new DateTime($this->data[$this->alias]['last_seen']);
|
||||
$d->setTimezone(new DateTimeZone('GMT'));
|
||||
$ls_sec = $d->format('U');
|
||||
$ls_micro = $d->format('u');
|
||||
$ls_micro = str_pad($ls_micro, 6, "0", STR_PAD_LEFT);
|
||||
$ls = $ls_sec . $ls_micro;
|
||||
$this->data[$this->alias]['last_seen'] = $ls;
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
parent::beforeValidate();
|
||||
|
@ -75,6 +122,14 @@ class MispObject extends AppModel
|
|||
$date = new DateTime();
|
||||
$this->data[$this->alias]['timestamp'] = $date->getTimestamp();
|
||||
}
|
||||
// parse first_seen different formats
|
||||
if (isset($this->data[$this->alias]['first_seen'])) {
|
||||
$this->data[$this->alias]['first_seen'] = $this->data[$this->alias]['first_seen'] === '' ? null : $this->data[$this->alias]['first_seen'];
|
||||
}
|
||||
// parse last_seen different formats
|
||||
if (isset($this->data[$this->alias]['last_seen'])) {
|
||||
$this->data[$this->alias]['last_seen'] = $this->data[$this->alias]['last_seen'] === '' ? null : $this->data[$this->alias]['last_seen'];
|
||||
}
|
||||
if (empty($this->data[$this->alias]['template_version'])) {
|
||||
$this->data[$this->alias]['template_version'] = 1;
|
||||
}
|
||||
|
@ -254,6 +309,27 @@ class MispObject extends AppModel
|
|||
return $conditions;
|
||||
}
|
||||
|
||||
public function fetchObjectSimple($user, $options = array())
|
||||
{
|
||||
$params = array(
|
||||
'conditions' => $this->buildConditions($user),
|
||||
'fields' => array(),
|
||||
'recursive' => -1
|
||||
);
|
||||
if (isset($options['conditions'])) {
|
||||
$params['conditions']['AND'][] = $options['conditions'];
|
||||
}
|
||||
if (isset($options['fields'])) {
|
||||
$params['fields'] = $options['fields'];
|
||||
}
|
||||
$results = $this->find('all', array(
|
||||
'conditions' => $params['conditions'],
|
||||
'recursive' => -1,
|
||||
'fields' => $params['fields'],
|
||||
'sort' => false
|
||||
));
|
||||
return $results;
|
||||
}
|
||||
|
||||
// Method that fetches all objects
|
||||
// very flexible, it's basically a replacement for find, with the addition that it restricts access based on user
|
||||
|
@ -493,7 +569,42 @@ class MispObject extends AppModel
|
|||
return $attributes;
|
||||
}
|
||||
|
||||
public function deltaMerge($object, $objectToSave)
|
||||
// EUH????? No sure we should do that. Maybe the other way around??? #FIXME
|
||||
// delete first/last-seen object attribute and set object's meta accordingly
|
||||
// set object's meta (fs/ls) and potentially delete objectAttributes
|
||||
public function setObjectSeenMetaFromAttribute($object, $delete=false) {
|
||||
if ( !$delete && isset($object['Object']['first-seen']) && isset($object['Object']['last-seen'])) {
|
||||
return;
|
||||
}
|
||||
if (isset($object['Attribute'])) {
|
||||
foreach($object['Attribute'] as $i => $attribute) {
|
||||
if ($attribute['object_relation'] == 'first-seen') {
|
||||
// set object meta if unset
|
||||
if (!isset($object['Object']['first-seen'])) {
|
||||
$object['Object']['first-seen'] = $attribute['value'];
|
||||
}
|
||||
if ($delete) {
|
||||
$object['Attribute'][$i]['deleted'] = 1;
|
||||
$this->Event->Attribute->save($object['Attribute'][$i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($attribute['object_relation'] == 'last-seen') {
|
||||
// set object meta if unset
|
||||
if (!isset($object['Object']['last-seen'])) {
|
||||
$object['Object']['last-seen'] = $attribute['value'];
|
||||
}
|
||||
if ($delete) {
|
||||
$object['Attribute'][$i]['deleted'] = 1;
|
||||
$this->Event->Attribute->save($object['Attribute'][$i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deltaMerge($object, $objectToSave, $onlyAddNewAttribute=false)
|
||||
{
|
||||
if (!isset($objectToSave['Object'])) {
|
||||
$dataToBackup = array('ObjectReferences', 'Attribute', 'ShadowAttribute');
|
||||
|
@ -512,67 +623,95 @@ class MispObject extends AppModel
|
|||
}
|
||||
unset($dataToBackup);
|
||||
}
|
||||
$object['Object']['comment'] = $objectToSave['Object']['comment'];
|
||||
$object['Object']['distribution'] = $objectToSave['Object']['distribution'];
|
||||
if ($object['Object']['distribution'] == 4) {
|
||||
$object['Object']['sharing_group_id'] = $objectToSave['Object']['sharing_group_id'];
|
||||
if (isset($objectToSave['Object']['comment'])) {
|
||||
$object['Object']['comment'] = $objectToSave['Object']['comment'];
|
||||
}
|
||||
if (isset($objectToSave['Object']['distribution'])) {
|
||||
$object['Object']['distribution'] = $objectToSave['Object']['distribution'];
|
||||
if ($object['Object']['distribution'] == 4) {
|
||||
$object['Object']['sharing_group_id'] = $objectToSave['Object']['sharing_group_id'];
|
||||
}
|
||||
}
|
||||
$date = new DateTime();
|
||||
$object['Object']['timestamp'] = $date->getTimestamp();
|
||||
if (isset($objectToSave['Object']['first_seen'])) {
|
||||
$object['Object']['first_seen'] = $objectToSave['Object']['first_seen'];
|
||||
}
|
||||
if (isset($objectToSave['Object']['last_seen'])) {
|
||||
$object['Object']['last_seen'] = $objectToSave['Object']['last_seen'];
|
||||
}
|
||||
$this->setObjectSeenMetaFromAttribute($object, true);
|
||||
$this->save($object);
|
||||
$checkFields = array('category', 'value', 'to_ids', 'distribution', 'sharing_group_id', 'comment', 'disable_correlation');
|
||||
foreach ($objectToSave['Attribute'] as $newKey => $newAttribute) {
|
||||
foreach ($object['Attribute'] as $origKey => $originalAttribute) {
|
||||
if (!empty($newAttribute['uuid'])) {
|
||||
if ($newAttribute['uuid'] == $originalAttribute['uuid']) {
|
||||
$different = false;
|
||||
foreach ($checkFields as $f) {
|
||||
if ($f == 'sharing_group_id' && empty($newAttribute[$f])) {
|
||||
$newAttribute[$f] = 0;
|
||||
|
||||
if (!$onlyAddNewAttribute) {
|
||||
$checkFields = array('category', 'value', 'to_ids', 'distribution', 'sharing_group_id', 'comment', 'disable_correlation');
|
||||
foreach ($objectToSave['Attribute'] as $newKey => $newAttribute) {
|
||||
foreach ($object['Attribute'] as $origKey => $originalAttribute) {
|
||||
if (!empty($newAttribute['uuid'])) {
|
||||
if ($newAttribute['uuid'] == $originalAttribute['uuid']) {
|
||||
$different = false;
|
||||
foreach ($checkFields as $f) {
|
||||
if ($f == 'sharing_group_id' && empty($newAttribute[$f])) {
|
||||
$newAttribute[$f] = 0;
|
||||
}
|
||||
if ($newAttribute[$f] != $originalAttribute[$f]) {
|
||||
$different = true;
|
||||
}
|
||||
}
|
||||
if ($newAttribute[$f] != $originalAttribute[$f]) {
|
||||
$different = true;
|
||||
if ($different) {
|
||||
$newAttribute['id'] = $originalAttribute['id'];
|
||||
$newAttribute['event_id'] = $object['Object']['event_id'];
|
||||
$newAttribute['object_id'] = $object['Object']['id'];
|
||||
$newAttribute['timestamp'] = $date->getTimestamp();
|
||||
$result = $this->Event->Attribute->save(array('Attribute' => $newAttribute), array(
|
||||
'category',
|
||||
'value',
|
||||
'to_ids',
|
||||
'distribution',
|
||||
'sharing_group_id',
|
||||
'comment',
|
||||
'timestamp',
|
||||
'object_id',
|
||||
'event_id',
|
||||
'disable_correlation'
|
||||
));
|
||||
}
|
||||
unset($object['Attribute'][$origKey]);
|
||||
continue 2;
|
||||
}
|
||||
if ($different) {
|
||||
$newAttribute['id'] = $originalAttribute['id'];
|
||||
$newAttribute['event_id'] = $object['Object']['event_id'];
|
||||
$newAttribute['object_id'] = $object['Object']['id'];
|
||||
$newAttribute['timestamp'] = $date->getTimestamp();
|
||||
$result = $this->Event->Attribute->save(array('Attribute' => $newAttribute), array(
|
||||
'category',
|
||||
'value',
|
||||
'to_ids',
|
||||
'distribution',
|
||||
'sharing_group_id',
|
||||
'comment',
|
||||
'timestamp',
|
||||
'object_id',
|
||||
'event_id',
|
||||
'disable_correlation'
|
||||
));
|
||||
}
|
||||
unset($object['Attribute'][$origKey]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->Event->Attribute->create();
|
||||
$newAttribute['event_id'] = $object['Object']['event_id'];
|
||||
$newAttribute['object_id'] = $object['Object']['id'];
|
||||
if (!isset($newAttribute['timestamp'])) {
|
||||
$newAttribute['distribution'] = Configure::read('MISP.default_attribute_distribution');
|
||||
if ($newAttribute['distribution'] == 'event') {
|
||||
$newAttribute['distribution'] = 5;
|
||||
$this->Event->Attribute->create();
|
||||
$newAttribute['event_id'] = $object['Object']['event_id'];
|
||||
$newAttribute['object_id'] = $object['Object']['id'];
|
||||
if (!isset($newAttribute['timestamp'])) {
|
||||
$newAttribute['distribution'] = Configure::read('MISP.default_attribute_distribution');
|
||||
if ($newAttribute['distribution'] == 'event') {
|
||||
$newAttribute['distribution'] = 5;
|
||||
}
|
||||
}
|
||||
$this->Event->Attribute->save($newAttribute);
|
||||
$attributeArrays['add'][] = $newAttribute;
|
||||
unset($objectToSave['Attribute'][$newKey]);
|
||||
}
|
||||
foreach ($object['Attribute'] as $origKey => $originalAttribute) {
|
||||
$originalAttribute['deleted'] = 1;
|
||||
$this->Event->Attribute->save($originalAttribute);
|
||||
}
|
||||
} else { // we only add the new attribute
|
||||
$newAttribute = $objectToSave['Attribute'][0];
|
||||
if ($newAttribute != 'last-seen' && $newAttribute != 'first-seen') { // fs/ls should be added in the object's meta
|
||||
$this->Event->Attribute->create();
|
||||
$newAttribute['event_id'] = $object['Object']['event_id'];
|
||||
$newAttribute['object_id'] = $object['Object']['id'];
|
||||
if (!isset($newAttribute['timestamp'])) {
|
||||
$newAttribute['distribution'] = Configure::read('MISP.default_attribute_distribution');
|
||||
if ($newAttribute['distribution'] == 'event') {
|
||||
$newAttribute['distribution'] = 5;
|
||||
}
|
||||
}
|
||||
$this->Attribute->saveAttributes(array($newAttribute));
|
||||
}
|
||||
$this->Event->Attribute->save($newAttribute);
|
||||
$attributeArrays['add'][] = $newAttribute;
|
||||
unset($objectToSave['Attribute'][$newKey]);
|
||||
}
|
||||
foreach ($object['Attribute'] as $origKey => $originalAttribute) {
|
||||
$originalAttribute['deleted'] = 1;
|
||||
$this->Event->Attribute->save($originalAttribute);
|
||||
}
|
||||
return $this->id;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="attributes <?php if (!isset($ajax) || !$ajax) echo 'form';?>">
|
||||
<?php
|
||||
$url_params = $action == 'add' ? 'add/' . $event_id : 'edit/' . $attribute['Attribute']['id'];
|
||||
echo $this->Form->create('Attribute', array('id', 'url' => '/attributes/' . $url_params));
|
||||
echo $this->Form->create('Attribute', array('id'=>'AttributeForm', 'url' => '/attributes/' . $url_params));
|
||||
?>
|
||||
<fieldset>
|
||||
<legend><?php echo $action == 'add' ? __('Add Attribute') : __('Edit Attribute'); ?></legend>
|
||||
|
@ -78,6 +78,7 @@
|
|||
echo $this->Form->input('batch_import', array(
|
||||
'type' => 'checkbox'
|
||||
));
|
||||
echo $this->element('form_seen_input');
|
||||
echo '<div class="input clear"></div>';
|
||||
echo $this->Form->input('disable_correlation', array(
|
||||
'type' => 'checkbox'
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
echo $this->Form->create('Attribute', array('id' => 'Attribute' . '_' . $object['id'] . '_first_seen_form', 'url' => '/attributes/editField/' . $object['id']));
|
||||
?>
|
||||
<?php
|
||||
echo $this->Form->input('first_seen', array(
|
||||
'label' => false,
|
||||
'type' => 'text',
|
||||
'value' => 0,
|
||||
'id' => 'Attribute' . '_' . $object['id'] . '_first_seen_field',
|
||||
'div' => false
|
||||
));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
echo $this->Form->create('Attribute', array('id' => 'Attribute' . '_' . $object['id'] . '_last_seen_form', 'url' => '/attributes/editField/' . $object['id']));
|
||||
?>
|
||||
<?php
|
||||
echo $this->Form->input('last_seen', array(
|
||||
'label' => false,
|
||||
'type' => 'text',
|
||||
'value' => 0,
|
||||
'id' => 'Attribute' . '_' . $object['id'] . '_last_seen_field',
|
||||
'div' => false
|
||||
));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
$tr_class = '';
|
||||
$linkClass = 'white';
|
||||
$currentType = 'denyForm';
|
||||
$currentType = 'Object';
|
||||
$tr_class = 'tableHighlightBorderTop borderBlue';
|
||||
if ($event['Event']['id'] != $object['event_id']) {
|
||||
if (!$isSiteAdmin && $event['extensionEvents'][$object['event_id']]['Orgc']['id'] != $me['org_id']) {
|
||||
|
@ -85,8 +85,11 @@
|
|||
}
|
||||
?>
|
||||
</td>
|
||||
<td class="shortish">
|
||||
<?php echo h($object['comment']); ?>
|
||||
<td class="showspaces bitwider" onmouseenter="quickEditHover(this, 'Object', '<?php echo $object['id']; ?>', 'comment', <?php echo $event['Event']['id'];?>);">
|
||||
<div id = "Object_<?php echo $object['id']; ?>_comment_placeholder" class = "inline-field-placeholder"></div>
|
||||
<div id = "Object_<?php echo $object['id']; ?>_comment_solid" class="inline-field-solid">
|
||||
<?php echo nl2br(h($object['comment'])); ?>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="4">
|
||||
</td>
|
||||
|
@ -147,5 +150,6 @@
|
|||
'child' => $attrKey == $lastElement ? 'last' : true
|
||||
));
|
||||
}
|
||||
echo '<tr><td class="fa fa-plus-circle objectAddField" title="' . __('Add an Object Attribute') .'" onclick="quickFetchValidObjectAttribute(' . h($object['id']) . ')"></td></tr>';
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
<?php echo $this->Html->script('moment-with-locales'); ?>
|
||||
|
||||
<div class="input-group">
|
||||
<?php
|
||||
echo $this->Form->input('first_seen', array(
|
||||
'type' => 'text',
|
||||
'div' => 'input hidden',
|
||||
'required' => false,
|
||||
));
|
||||
echo $this->Form->input('last_seen', array(
|
||||
'type' => 'text',
|
||||
'div' => 'input hidden',
|
||||
'required' => false,
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="input clear"></div>
|
||||
|
||||
|
||||
<script>
|
||||
var controller = "<?php echo(substr(ucfirst($this->params->controller), 0, -1)); ?>"; // get current controller name fo that we can access all form fields
|
||||
var time_vals = [
|
||||
['Hour', 23, 1000*1000*60*60],
|
||||
['Minute', 59, 1000*1000*60],
|
||||
['Second', 59, 1000*60],
|
||||
['ms', 999, 1000],
|
||||
['us', 999, 1],
|
||||
];
|
||||
|
||||
class MicroDatetime {
|
||||
constructor(value) {
|
||||
this.isoDatetimeMicroRegex = /(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})\.(\d{3})(\d*)(\D\S*)/g;
|
||||
this.isotimeMicroRegex = /(\d{2})\:(\d{2})\:(\d{2})\.(\d{3})(\d*)(\D\S*)/g;
|
||||
this.numberRegex = /^(\-|\+)?([0-9]+|Infinity)$/g;
|
||||
|
||||
if (value === undefined || value === "") {
|
||||
this.moment = undefined;
|
||||
this.micro = 0;
|
||||
} else {
|
||||
// check if timestamp UNIX timestamp (in sec)
|
||||
if (this.numberRegex.test(value)) {
|
||||
var timestamp = parseInt(value);
|
||||
var timestamp_str = String(timestamp);
|
||||
if (timestamp === '' || timestamp === undefined || timestamp === null || isNaN(timestamp)) {
|
||||
this.moment = moment(0);
|
||||
this.micro = 0;
|
||||
} else {
|
||||
var all_milli = parseInt(timestamp)*1000;
|
||||
all_milli = isNaN(all_milli) || all_milli === undefined ? 0 : all_milli;
|
||||
this.moment = moment(all_milli);
|
||||
this.micro = 0;
|
||||
}
|
||||
// check if only a time
|
||||
} else { // let moment parse the date
|
||||
try {
|
||||
value = String(value);
|
||||
this.moment = new moment(value);
|
||||
if (this.moment.isValid()) {
|
||||
var res = this.isoDatetimeMicroRegex.exec(value);
|
||||
var micro_str = res !== null ? res[8] : 0;
|
||||
this.micro = parseInt(micro_str);
|
||||
this.micro = isNaN(this.micro) ? 0 : this.micro;
|
||||
} else {
|
||||
this.moment = undefined;
|
||||
this.micro = 0;
|
||||
showMessage('fail', 'Failed to parse the date: <strong>' + value + '</strong>');
|
||||
}
|
||||
} catch (err) {
|
||||
if (this.isotimeMicroRegex.test(value)) {
|
||||
this.moment = moment(value, "HH:mm:ss.SSSSSSZ");
|
||||
if (this.moment.isValid()) {
|
||||
var res = this.isotimeMicroRegex.exec(value);
|
||||
var micro_str = res !== null ? res[8] : 0;
|
||||
this.micro = parseInt(micro_str);
|
||||
this.micro = isNaN(this.micro) ? 0 : this.micro;
|
||||
} else {
|
||||
this.moment = undefined;
|
||||
this.micro = 0;
|
||||
showMessage('fail', 'Failed to parse the date: ' + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_microISO() {
|
||||
if (this.moment === undefined || this.moment === null) {
|
||||
return "";
|
||||
}
|
||||
var tz = this.moment.format('Z');
|
||||
var str = this.moment.toISOString(true);
|
||||
str = str.replace(tz, pad_zero(this.micro, 3)+tz);
|
||||
return str;
|
||||
}
|
||||
|
||||
get_date() {
|
||||
if (this.moment === undefined || this.moment === null) {
|
||||
return "";
|
||||
}
|
||||
return this.moment.format('YYYY/MM/DD');
|
||||
}
|
||||
|
||||
get_time() {
|
||||
if (this.moment === undefined || this.moment === null) {
|
||||
return "";
|
||||
}
|
||||
return this.moment.format('HH:mm:ss.SSS')
|
||||
+ String(pad_zero(this.micro, 3))
|
||||
+ this.moment.format('Z');
|
||||
}
|
||||
|
||||
has_date() {
|
||||
return this.moment !== undefined;
|
||||
}
|
||||
|
||||
has_time() {
|
||||
if (this.moment === undefined) {
|
||||
return false;
|
||||
} else {
|
||||
return this.moment.get('hour') != 0
|
||||
|| this.moment.get('minute') != 0
|
||||
|| this.moment.get('second') != 0
|
||||
|| this.moment.get('millisecond') != 0
|
||||
|| this.micro != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pad_zero(val, pad) {
|
||||
var ret = '';
|
||||
for (var i=0; i<(pad-String(val).length); i++) {
|
||||
ret += '0';
|
||||
}
|
||||
ret += String(val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function get_slider_and_input(type, scale, factor, max) {
|
||||
var row = $('<tr></tr>');
|
||||
var td1 = $('<td></td>');
|
||||
var td2 = $('<td></td>');
|
||||
var td3 = $('<td></td>');
|
||||
var label = $('<span>'+scale+'</span>').css({fontWeight: 'bold'});
|
||||
var input = $('<input id="input-time-'+type+'-'+scale+'" type="number" min=0 max='+max+' step=1 value=0 factor='+factor+' scale='+scale+'></input>').css({width: '50px', margin: '5px'});
|
||||
var slider_width = '200px';
|
||||
var slider = $('<input id="slider-time-'+type+'-'+scale+'" type="range" min=0 max='+max+' step=1 value=0 factor='+factor+' scale='+scale+'></input>').css({width: slider_width, margin: '5px'});
|
||||
|
||||
row.append(td1.append(label))
|
||||
.append(td2.append(input))
|
||||
.append(td3.append(slider))
|
||||
return row;
|
||||
}
|
||||
|
||||
function reflect_change_on_sliders(seen, skip_input_update, overwrite) {
|
||||
if (seen == 'both' || seen == 'first') {
|
||||
var f_val = overwrite === undefined ? $('#'+controller+'FirstSeen').val() : overwrite;
|
||||
var f_microdatetime = new MicroDatetime(f_val);
|
||||
var hours = f_microdatetime.moment !== undefined ? f_microdatetime.moment.hours() : 0;
|
||||
var minutes = f_microdatetime.moment !== undefined ? f_microdatetime.moment.minutes() : 0;
|
||||
var seconds = f_microdatetime.moment !== undefined ? f_microdatetime.moment.seconds() : 0;
|
||||
var milli = f_microdatetime.moment !== undefined ? f_microdatetime.moment.milliseconds() : 0;
|
||||
var d = f_microdatetime.moment !== undefined ? f_microdatetime.get_date() : undefined;
|
||||
|
||||
// mirror slider and input field
|
||||
$('#input-time-first-'+time_vals[0]).val(hours);
|
||||
$('#slider-time-first-'+time_vals[0]).val(hours);
|
||||
|
||||
$('#input-time-first-'+time_vals[1]).val(minutes);
|
||||
$('#slider-time-first-'+time_vals[1]).val(minutes);
|
||||
|
||||
$('#input-time-first-'+time_vals[2]).val(seconds);
|
||||
$('#slider-time-first-'+time_vals[2]).val(seconds);
|
||||
|
||||
$('#input-time-first-'+time_vals[3]).val(milli);
|
||||
$('#slider-time-first-'+time_vals[3]).val(milli);
|
||||
|
||||
$('#input-time-first-'+time_vals[4]).val(f_microdatetime.micro);
|
||||
$('#slider-time-first-'+time_vals[4]).val(f_microdatetime.micro);
|
||||
|
||||
$('#date_fs').datepicker('setDate', d);
|
||||
$('#time_fs').val(f_microdatetime.get_time());
|
||||
}
|
||||
|
||||
if (seen == 'both' || seen == 'last') {
|
||||
var l_val = overwrite === undefined ? $('#'+controller+'LastSeen').val() : overwrite;
|
||||
var l_microdatetime = new MicroDatetime(l_val);
|
||||
var hours = l_microdatetime.moment !== undefined ? l_microdatetime.moment.hours() : 0;
|
||||
var minutes = l_microdatetime.moment !== undefined ? l_microdatetime.moment.minutes() : 0;
|
||||
var seconds = l_microdatetime.moment !== undefined ? l_microdatetime.moment.seconds() : 0;
|
||||
var milli = l_microdatetime.moment !== undefined ? l_microdatetime.moment.milliseconds() : 0;
|
||||
var d = l_microdatetime.moment !== undefined ? l_microdatetime.get_date() : undefined;
|
||||
|
||||
// mirror slider and input field
|
||||
$('#input-time-last-'+time_vals[0]).val(hours);
|
||||
$('#slider-time-last-'+time_vals[0]).val(hours);
|
||||
|
||||
$('#input-time-last-'+time_vals[1]).val(minutes);
|
||||
$('#slider-time-last-'+time_vals[1]).val(minutes);
|
||||
|
||||
$('#input-time-last-'+time_vals[2]).val(seconds);
|
||||
$('#slider-time-last-'+time_vals[2]).val(seconds);
|
||||
|
||||
$('#input-time-last-'+time_vals[3]).val(milli);
|
||||
$('#slider-time-last-'+time_vals[3]).val(milli);
|
||||
|
||||
$('#input-time-last-'+time_vals[4]).val(l_microdatetime.micro);
|
||||
$('#slider-time-last-'+time_vals[4]).val(l_microdatetime.micro);
|
||||
|
||||
$('#date_ls').datepicker('setDate', d);
|
||||
$('#time_ls').val(l_microdatetime.get_time());
|
||||
}
|
||||
|
||||
if (!skip_input_update) {
|
||||
reflect_change_on_input(seen);
|
||||
}
|
||||
}
|
||||
|
||||
// get data stored in sliders
|
||||
function get_time_from_slider(which) {
|
||||
var micro = 0;
|
||||
var mom;
|
||||
var dp = which == 'first' ? $('#date_fs') : $('#date_ls');
|
||||
var timej = which == 'first' ? $('#time_fs') : $('#time_ls');
|
||||
if (dp.val() != "") { // no time without a date
|
||||
mom = dp.datepicker('getDate');
|
||||
mom = mom === null ? moment(0) : moment(mom.toISOString());
|
||||
if (timej.val() != "") {
|
||||
$('#precision_tool_'+which).find('input[type="number"]').each(function() {
|
||||
switch($(this).attr('scale')) {
|
||||
case 'us':
|
||||
micro = parseInt($(this).val());
|
||||
break;
|
||||
case 'ms':
|
||||
mom.set('ms', parseInt($(this).val()));
|
||||
break;
|
||||
case 'Second':
|
||||
mom.set('s', parseInt($(this).val()));
|
||||
break;
|
||||
case 'Minute':
|
||||
mom.set('m', parseInt($(this).val()));
|
||||
break;
|
||||
case 'Hour':
|
||||
mom.set('h', parseInt($(this).val()));
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else { // no time, setting it UTC noon
|
||||
micro = 0;
|
||||
mom.set('h', 0);
|
||||
}
|
||||
}
|
||||
microdatetime = new MicroDatetime();
|
||||
microdatetime.moment = mom;
|
||||
microdatetime.micro = micro;
|
||||
return microdatetime;
|
||||
}
|
||||
|
||||
function reflect_change_on_input(seen, full) {
|
||||
if ($('#seen_precision_tool').prop('checked')) {
|
||||
if (seen == 'both' || seen == 'first') {
|
||||
var microdatetime = get_time_from_slider('first');
|
||||
if($('#date_fs').val() !== '') {
|
||||
$("#time_fs").val(microdatetime.get_time());
|
||||
}
|
||||
}
|
||||
|
||||
if (seen == 'both' || seen == 'last') {
|
||||
var microdatetime = get_time_from_slider('last');
|
||||
if($('#date_ls').val() !== '') {
|
||||
$("#time_ls").val(microdatetime.get_time());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reflect_change_on_form() {
|
||||
var microdatetime = get_time_from_slider('first');
|
||||
if (microdatetime.moment !== undefined) {
|
||||
$('#'+controller+'FirstSeen').val(microdatetime.get_microISO());
|
||||
} else {
|
||||
$('#'+controller+'FirstSeen').val("");
|
||||
}
|
||||
var microdatetime = get_time_from_slider('last');
|
||||
if (microdatetime.moment !== undefined) {
|
||||
$('#'+controller+'LastSeen').val(microdatetime.get_microISO());
|
||||
} else {
|
||||
$('#'+controller+'LastSeen').val("");
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var sliders_container = "<?php if ($this->params->controller === 'attributes') { echo 'fieldset'; } else { echo '#meta-div'; } ?>";
|
||||
var inputs_container = $('<div class="input-group input-daterange"></div>');
|
||||
// create separate date and time input
|
||||
var date_div_fs = $('<div class="input clear larger-input-field" style="margin-left: 10px;"></div>').append(
|
||||
$('<label><?php echo __('First seen date') . '<span class="fa fa-calendar label-icon"></span>'; ?><input id="date_fs" type="text" style="width: 240px;"></input></label>')
|
||||
);
|
||||
$(inputs_container).append(date_div_fs);
|
||||
var date_div_ls = $('<div class="input text larger-input-field"></div>').append(
|
||||
$('<label><?php echo __('Last seen date') . '<span class="fa fa-calendar label-icon"></span>'; ?><input id="date_ls" type="text" style="width: 240px;"></input></label>')
|
||||
);
|
||||
$(inputs_container).append(date_div_ls);
|
||||
$(sliders_container).append(inputs_container);
|
||||
|
||||
var time_div_fs = $('<div class="input clear larger-input-field" style="margin-left: 10px;"></div>').append(
|
||||
$('<label><?php echo __('First seen time') . '<span class="fa fa-clock-o label-icon"></span>'; ?><input id="time_fs" type="text" style="width: 240px; text-align: center;"></input></label>')
|
||||
);
|
||||
$(sliders_container).append(time_div_fs);
|
||||
var time_div_ls = $('<div class="input larger-input-field"></div>').append(
|
||||
$('<label><?php echo __('Last seen time') . '<span class="fa fa-clock-o label-icon"></span>'; ?><input id="time_ls" type="text" style="width: 240px; text-align: center;"></input></label>')
|
||||
);
|
||||
$(sliders_container).append(time_div_ls);
|
||||
|
||||
// create checkbox
|
||||
var div_checkbox_prec_tool = $('<div class="clear checkbox" style="margin-left: 10px;"></div>').append(
|
||||
$('<label style="display: inline-block"><input id="seen_precision_tool" type="checkbox" style="margin-top: 0px;"></input><?php echo(__('Enable precision tool'))?><span class="fa fa-bullseye label-icon"</span></label>')
|
||||
);
|
||||
$(sliders_container).append(div_checkbox_prec_tool);
|
||||
|
||||
// create sliders
|
||||
var div = $('<div id="precision_tool" class="precision-tool clear" style="display: none"></div>');
|
||||
var content = $('<table id="precision_tool_first" style="float: left;"></table>');
|
||||
for (var i=0; i<time_vals.length; i++) {
|
||||
var type = time_vals[i][0];
|
||||
var max = time_vals[i][1];
|
||||
var factor = time_vals[i][2];
|
||||
var row = get_slider_and_input('first', type, factor, max)
|
||||
content.append(row);
|
||||
}
|
||||
div.append(content);
|
||||
var content = $('<table id="precision_tool_last" style="float:left; margin-left: 15px;"></table>');
|
||||
for (var i=0; i<time_vals.length; i++) {
|
||||
var type = time_vals[i][0];
|
||||
var max = time_vals[i][1];
|
||||
var factor = time_vals[i][2];
|
||||
var row = get_slider_and_input('last', type, factor, max)
|
||||
content.append(row);
|
||||
}
|
||||
div.append(content);
|
||||
$(sliders_container).append(div);
|
||||
|
||||
time_vals.forEach(function(elem) {
|
||||
$('#input-time-first-'+elem[0]).on('input', function(e) {
|
||||
$('#slider-time-first-'+elem[0]).val($(this).val());
|
||||
reflect_change_on_input('first');
|
||||
});
|
||||
$('#slider-time-first-'+elem[0]).on('input', function(e) {
|
||||
$('#input-time-first-'+elem[0]).val($(this).val());
|
||||
reflect_change_on_input('first');
|
||||
});
|
||||
|
||||
$('#input-time-last-'+elem[0]).on('input', function(e) {
|
||||
$('#slider-time-last-'+elem[0]).val($(this).val());
|
||||
reflect_change_on_input('last');
|
||||
});
|
||||
$('#slider-time-last-'+elem[0]).on('input', function(e) {
|
||||
$('#input-time-last-'+elem[0]).val($(this).val());
|
||||
reflect_change_on_input('last');
|
||||
});
|
||||
});
|
||||
|
||||
$('#seen_precision_tool').change(function(e) {
|
||||
if(e.target.checked) {
|
||||
$('#precision_tool').show();
|
||||
} else {
|
||||
$('#precision_tool').hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('#time_fs').on("focus", function(event) {
|
||||
$('#seen_precision_tool').prop('checked', true);
|
||||
$('#precision_tool').show();
|
||||
});
|
||||
$('#time_ls').on("focus", function(event) {
|
||||
$('#seen_precision_tool').prop('checked', true);
|
||||
$('#precision_tool').show();
|
||||
});
|
||||
|
||||
$('#time_fs').on("input", function(event) {
|
||||
reflect_change_on_sliders('first', false, $(this).val());
|
||||
});
|
||||
$('#time_ls').on("input", function(event) {
|
||||
reflect_change_on_sliders('last', false, $(this).val());
|
||||
});
|
||||
|
||||
$('#time_fs').on("paste", function(event) {
|
||||
// prefetch clipboard text and apply change
|
||||
var datetimeString;
|
||||
if (event.originalEvent.clipboardData && event.originalEvent.clipboardData.types
|
||||
&& $.inArray('text/plain', event.originalEvent.clipboardData.types) !== -1) {
|
||||
datetimeString = event.originalEvent.clipboardData.getData('text/plain');
|
||||
}
|
||||
else if (window.clipboardData) {
|
||||
datetimeString = window.clipboardData.getData('Text');
|
||||
}
|
||||
$('#'+controller+'FirstSeen').val(datetimeString);
|
||||
reflect_change_on_sliders('first', false);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('#time_ls').on("paste", function(event) {
|
||||
// prefetch clipboard text and apply change
|
||||
var datetimeString;
|
||||
if (event.originalEvent.clipboardData && event.originalEvent.clipboardData.types
|
||||
&& $.inArray('text/plain', event.originalEvent.clipboardData.types) !== -1) {
|
||||
datetimeString = event.originalEvent.clipboardData.getData('text/plain');
|
||||
}
|
||||
else if (window.clipboardData) {
|
||||
datetimeString = window.clipboardData.getData('Text');
|
||||
}
|
||||
$('#'+controller+'LastSeen').val(datetimeString);
|
||||
reflect_change_on_sliders('last', false);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('#date_fs').closest('form').submit(function( event ) {
|
||||
reflect_change_on_form();
|
||||
});
|
||||
|
||||
var d1 = new MicroDatetime($('#'+controller+'FirstSeen').val());
|
||||
var d2 = new MicroDatetime($('#'+controller+'LastSeen').val());
|
||||
if (d1.has_date() || d2.has_date()) {
|
||||
$('#date_fs').val(d1.get_date());
|
||||
$('#date_ls').val(d2.get_date());
|
||||
}
|
||||
if (d1.has_time() || d2.has_time()) {
|
||||
$('#seen_precision_tool').prop('checked', true);
|
||||
$('#precision_tool').show();
|
||||
$('#time_fs').val(d1.get_time());
|
||||
$('#time_ls').val(d2.get_time());
|
||||
}
|
||||
|
||||
$('.input-daterange').datepicker({
|
||||
preventMultipleSet: true,
|
||||
format: 'yyyy/mm/dd',
|
||||
todayHighlight: true
|
||||
})
|
||||
reflect_change_on_sliders('both', true);
|
||||
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
$mayModify = (($isAclModify && $event['Event']['user_id'] == $me['id'] && $event['Orgc']['id'] == $me['org_id']) || ($isAclModifyOrg && $event['Orgc']['id'] == $me['org_id']));
|
||||
$mayPublish = ($isAclPublish && $event['Orgc']['id'] == $me['org_id']);
|
||||
?>
|
||||
|
||||
<div>
|
||||
<div id="timeline-header" class="eventgraph_header">
|
||||
<label id="timeline-scope" class="btn center-in-network-header network-control-btn">
|
||||
<span class="useCursorPointer fa fa-object-group" style="margin-right: 3px;"></span><?php echo __('Time scope')?>
|
||||
</label>
|
||||
<label id="timeline-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')?>
|
||||
<span id="timeline-display-badge" class="badge"></span>
|
||||
</label>
|
||||
|
||||
<input type="text" id="timeline-typeahead" class="center-in-network-header network-typeahead flushright" data-provide="typeahead" size="20" placeholder="Search for an item">
|
||||
</div>
|
||||
|
||||
|
||||
<div id="event_timeline" data-user-manipulation="<?php echo $mayModify || $isSiteAdmin ? 'true' : 'false'; ?>" data-extended="<?php echo $extended; ?>">
|
||||
<div class="loadingTimeline">
|
||||
<div class="spinner"></div>
|
||||
<div class="loadingText"><?php echo __('Loading');?></div>
|
||||
</div>
|
||||
</div>
|
||||
<span id="fullscreen-btn-timeline" class="fullscreen-btn-timeline 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>
|
||||
|
||||
<?php
|
||||
echo $this->Html->script('moment-with-locales');
|
||||
echo $this->Html->script('event-timeline');
|
||||
echo $this->Html->css('event-timeline');
|
||||
?>
|
|
@ -467,6 +467,9 @@
|
|||
<button class="btn btn-inverse toggle qet galaxy-toggle-button" id="eventgraph_toggle" data-toggle-type="eventgraph" onclick="enable_interactive_graph();">
|
||||
<span class="icon-plus icon-white" title="<?php echo __('Toggle Event graph');?>" role="button" tabindex="0" aria-label="<?php echo __('Toggle Event graph');?>" style="vertical-align:top;"></span><?php echo __('Event graph');?>
|
||||
</button>
|
||||
<button class="btn btn-inverse toggle qet galaxy-toggle-button" id="eventtimeline_toggle" data-toggle-type="eventtimeline" onclick="enable_timeline();">
|
||||
<span class="icon-plus icon-white" title="<?php echo __('Toggle Event timeline');?>" role="button" tabindex="0" aria-label="<?php echo __('Toggle Event timeline');?>" style="vertical-align:top;"></span><?php echo __('Event timeline');?>
|
||||
</button>
|
||||
<button class="btn btn-inverse toggle qet galaxy-toggle-button" id="correlationgraph_toggle" data-toggle-type="correlationgraph" onclick="enable_correlation_graph();">
|
||||
<span class="icon-plus icon-white" title="<?php echo __('Toggle Correlation graph');?>" role="button" tabindex="0" aria-label="<?php echo __('Toggle Correlation graph');?>" style="vertical-align:top;"></span><?php echo __('Correlation graph');?>
|
||||
</button>
|
||||
|
@ -492,6 +495,9 @@
|
|||
<div id="eventgraph_div" class="info_container_eventgraph_network" style="display: none;" data-fullscreen="false">
|
||||
<?php echo $this->element('view_event_graph'); ?>
|
||||
</div>
|
||||
<div id="eventtimeline_div" class="info_container_eventtimeline" style="display: none;" data-fullscreen="false">
|
||||
<?php echo $this->element('view_timeline'); ?>
|
||||
</div>
|
||||
<div id="correlationgraph_div" class="info_container_eventgraph_network" style="display: none;" data-fullscreen="false">
|
||||
</div>
|
||||
<div id="attackmatrix_div" class="info_container_eventgraph_network" style="display: none;" data-fullscreen="false" data-mitre-attack-galaxy-id="<?php echo h($mitreAttackGalaxyId)?>">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
echo $this->Form->create('Object', array('id', 'url' => $url, 'enctype' => 'multipart/form-data'));
|
||||
?>
|
||||
<h3><?php echo ucfirst($action) . ' ' . Inflector::humanize(h($template['ObjectTemplate']['name'])) . __(' Object'); ?></h3>
|
||||
<div class="row-fluid" style="margin-bottom:10px;">
|
||||
<div id="meta-div" class="row-fluid" style="margin-bottom:10px;">
|
||||
<dl class="span8">
|
||||
<dt><?php echo __('Object Template');?></dt>
|
||||
<dd>
|
||||
|
@ -77,6 +77,9 @@
|
|||
));
|
||||
?>
|
||||
</dd>
|
||||
<?php
|
||||
echo $this->element('form_seen_input');
|
||||
?>
|
||||
</dl>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
echo $this->Form->create('Object', array('class' => 'inline-form inline-field-form', 'id' => 'Object_' . $object['id'] . '_comment_form', 'url' => '/objects/editField/' . $object['id']));
|
||||
?>
|
||||
<div class='inline-input inline-input-container'>
|
||||
<div class="inline-input-accept inline-input-button inline-input-passive"><span class = "icon-ok" role="button" tabindex="0" aria-label="<?php echo __('Accept change'); ?>"></span></div>
|
||||
<div class="inline-input-decline inline-input-button inline-input-passive"><span class = "icon-remove" role="button" tabindex="0" aria-label="<?php echo __('Discard change'); ?>"></span></div>
|
||||
<?php
|
||||
echo $this->Form->input('comment', array(
|
||||
'type' => 'textarea',
|
||||
'label' => false,
|
||||
'value' => $object['comment'],
|
||||
'error' => array('escape' => false),
|
||||
'class' => 'inline-input',
|
||||
'id' => 'Object' . '_' . $object['id'] . '_comment_field',
|
||||
'div' => false
|
||||
));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->Form->end();
|
||||
?>
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
echo $this->Form->create('Object', array('class' => 'inline-form inline-field-form', 'id' => 'Object_' . $object['id'] . '_distribution_form', 'url' => '/objects/editField/' . $object['id']));
|
||||
?>
|
||||
<div class='inline-input inline-input-container'>
|
||||
<div class="inline-input-accept inline-input-button inline-input-passive"><span class = "icon-ok" role="button" tabindex="0" aria-label="<?php echo __('Accept change'); ?>"></span></div>
|
||||
<div class="inline-input-decline inline-input-button inline-input-passive"><span class = "icon-remove" role="button" tabindex="0" aria-label="<?php echo __('Discard change'); ?>"></span></div>
|
||||
<?php
|
||||
echo $this->Form->input('distribution', array(
|
||||
'options' => array($distributionLevels),
|
||||
'label' => false,
|
||||
'selected' => $object['distribution'],
|
||||
'error' => array('escape' => false),
|
||||
'class' => 'inline-input',
|
||||
'id' => 'Object_' . $object['id'] . '_distribution_field',
|
||||
'div' => false
|
||||
));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
echo $this->Form->create('Object', array('id' => 'Object' . '_' . $object['id'] . '_first_seen_form', 'url' => '/objects/editField/' . $object['id']));
|
||||
?>
|
||||
<?php
|
||||
echo $this->Form->input('first_seen', array(
|
||||
'label' => false,
|
||||
'type' => 'text',
|
||||
'value' => 0,
|
||||
'id' => 'Object' . '_' . $object['id'] . '_first_seen_field',
|
||||
'div' => false
|
||||
));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
echo $this->Form->create('Object', array('id' => 'Object' . '_' . $object['id'] . '_last_seen_form', 'url' => '/objects/editField/' . $object['id']));
|
||||
?>
|
||||
<?php
|
||||
echo $this->Form->input('last_seen', array(
|
||||
'label' => false,
|
||||
'type' => 'text',
|
||||
'value' => 0,
|
||||
'id' => 'Object' . '_' . $object['id'] . '_last_seen_field',
|
||||
'div' => false
|
||||
));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
echo nl2br(h($value)) . ' ';
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
$url = '/objects/quickAddAttributeForm/' . $object['id'];
|
||||
$element = $template['ObjectTemplateElement'][0];
|
||||
$k = 0;
|
||||
$action = 'add';
|
||||
echo $this->Form->create('Object', array(
|
||||
'id' => 'Object_' . $object['id'] . '_quick_add_attribute_form',
|
||||
'url' => $url,
|
||||
'class' => 'allDiv'
|
||||
));
|
||||
?>
|
||||
|
||||
<fieldset>
|
||||
<legend><?php echo __('Add Object attribute'); ?></legend>
|
||||
<div class="add_attribute_fields">
|
||||
<?php
|
||||
$formSettings = array(
|
||||
'type' => 'hidden',
|
||||
'value' => $element['object_relation'],
|
||||
'label' => false,
|
||||
'div' => false
|
||||
);
|
||||
echo $this->Form->input('Attribute.' . $k . '.object_relation', $formSettings);
|
||||
|
||||
$formSettings['value'] = $object['id'];
|
||||
echo $this->Form->input('Object.id', $formSettings);
|
||||
|
||||
$formSettings['value'] = $object['event_id'];
|
||||
echo $this->Form->input('Object.event_id', $formSettings);
|
||||
|
||||
$formSettings['value'] = $element['type'];
|
||||
echo '<div style="margin-bottom: 5px; font-size: 14px;">';
|
||||
echo $this->Form->input('Attribute.' . $k . '.type', $formSettings);
|
||||
echo '<span class="bold">' . Inflector::humanize(h($element['object_relation'])) . '</span>';
|
||||
echo ' :: ' . h($element['type']) . '';
|
||||
echo '<br>';
|
||||
echo '<span class="immutableAttributeDescription">' . h($element['description']) . '</span>';
|
||||
echo '</div>';
|
||||
?>
|
||||
|
||||
|
||||
<?php
|
||||
$formSettings = array(
|
||||
'options' => array_combine($element['categories'], $element['categories']),
|
||||
'default' => $element['default_category'],
|
||||
'div' => true
|
||||
);
|
||||
echo $this->Form->input('Attribute.' . $k . '.category', $formSettings);
|
||||
?>
|
||||
|
||||
<div class="clear">
|
||||
<?php
|
||||
echo '<label for="Attribute' . $k . '.value">' . __('Value') . '</label>';
|
||||
echo $this->element(
|
||||
'Objects/object_value_field',
|
||||
array(
|
||||
'element' => $element,
|
||||
'k' => $k,
|
||||
'action' => $action
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
echo $this->Form->input('Attribute.' . $k . '.to_ids', array(
|
||||
'type' => 'checkbox',
|
||||
'checked' => $element['to_ids'],
|
||||
));
|
||||
?>
|
||||
|
||||
<?php
|
||||
echo $this->Form->input('Attribute.' . $k . '.disable_correlation', array(
|
||||
'type' => 'checkbox',
|
||||
'checked' => $element['disable_correlation'],
|
||||
));
|
||||
?>
|
||||
|
||||
<div class='input'>
|
||||
<?php
|
||||
echo $this->Form->input('Attribute.' . $k . '.distribution', array(
|
||||
'class' => 'Attribute_distribution_select',
|
||||
'options' => $distributionData['levels'],
|
||||
'default' => !empty($element['distribution']) ? $element['distribution'] : $distributionData['initial'],
|
||||
'div' => false,
|
||||
'label' => __('Distribution ') . $this->element('formInfo', array('type' => 'distribution')),
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
<div class='input'>
|
||||
<div id="SGContainer" style="display:none;">
|
||||
<?php
|
||||
if (!empty($distributionData['sgs'])) {
|
||||
echo $this->Form->input('Attribute.' . $k . '.sharing_group_id', array(
|
||||
'options' => $distributionData['sgs'],
|
||||
'label' => __('Sharing Group')
|
||||
));
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
echo $this->Form->input('Attribute.' . $k . '.comment', array(
|
||||
'type' => 'textarea',
|
||||
'required' => false,
|
||||
'allowEmpty' => true,
|
||||
'div' => 'input clear',
|
||||
'class' => 'input-xxlarge'
|
||||
));
|
||||
?>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="overlay_spacing">
|
||||
<?php if ($ajax): ?>
|
||||
<span id="submitButton" class="btn btn-primary" style="margin-bottom:5px;float:left;" title="<?php echo __('Submit'); ?>" role="button" tabindex="0" aria-label="<?php echo __('Submit'); ?>" onClick="submitPopoverForm('<?php echo h($object['id']); ?>', 'quickAddAttributeForm', <?php echo h($object['event_id']); ?>)"><?php echo __('Submit'); ?></span>
|
||||
<?php else:
|
||||
echo $this->Form->button('Submit', array('class' => 'btn btn-primary'));
|
||||
endif;
|
||||
?>
|
||||
<span class="btn btn-inverse" style="float:right;" title="<?php echo __('Cancel'); ?>" role="button" tabindex="0" aria-label="<?php echo __('Cancel'); ?>" id="cancel_attribute_add"><?php echo __('Cancel'); ?></span>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
|
||||
|
||||
<script>
|
||||
<?php
|
||||
$formInfoTypes = array('distribution' => 'Distribution');
|
||||
echo 'var formInfoFields = ' . json_encode($formInfoTypes) . PHP_EOL;
|
||||
foreach ($formInfoTypes as $formInfoType => $humanisedName) {
|
||||
echo 'var ' . $formInfoType . 'FormInfoValues = {' . PHP_EOL;
|
||||
foreach ($info[$formInfoType] as $key => $formInfoData) {
|
||||
echo '"' . $key . '": "<span class=\"blue bold\">' . h($formInfoData['key']) . '</span>: ' . h($formInfoData['desc']) . '<br />",' . PHP_EOL;
|
||||
}
|
||||
echo '}' . PHP_EOL;
|
||||
}
|
||||
?>
|
||||
|
||||
$(document).ready(function() {
|
||||
initPopoverContent('Attribute0');
|
||||
$('#Attribute0Distribution').change(function() {
|
||||
initPopoverContent('Attribute0');
|
||||
if ($('#Attribute0Distribution').val() == 4) $('#SGContainer').show();
|
||||
else $('#SGContainer').hide();
|
||||
});
|
||||
|
||||
$('#cancel_attribute_add').click(function() {
|
||||
cancelPopoverForm();
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,22 @@
|
|||
<div class="popover_choice">
|
||||
<legend><?php echo __('Select Object Attribute To Add');?></legend>
|
||||
<div class="popover_choice_main" id ="popover_choice_main">
|
||||
<table style="width:100%;">
|
||||
<?php foreach ($template['ObjectTemplateElement'] as $objectAttribute): ?>
|
||||
<tr style="border-bottom:1px solid black;" class="templateChoiceButton">
|
||||
<td role="button" tabindex="0" aria-label="<?php echo h($objectAttribute['object_relation']); ?>" title="<?php echo h($objectAttribute['object_relation']); ?>" style="padding-left:10px;padding-right:10px; text-align:center;width:100%;" onClick="fetchAddObjectAttributeForm(<?php echo h($objectId) . ', \'' . h($objectAttribute['object_relation']) . '\''; ?>)"><?php echo '<strong>' . h($objectAttribute['object_relation']) . '</strong>' . ' :: ' . h($objectAttribute['type']) . '<br>' . h($objectAttribute['description']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
<div role="button" tabindex="0" aria-label="<?php echo __('Cancel');?>" title="<?php echo __('Cancel');?>" class="templateChoiceButton templateChoiceButtonLast" onClick="cancelPopoverForm();"><?php echo __('Cancel');?></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
resizePopoverBody();
|
||||
});
|
||||
|
||||
$(window).resize(function() {
|
||||
resizePopoverBody();
|
||||
});
|
||||
</script>
|
|
@ -49,6 +49,13 @@
|
|||
<td class="bold"><?php echo __('Comment');?></td>
|
||||
<td><?php echo h($data['Object']['comment']); ?></td>
|
||||
</tr>
|
||||
<td class="bold"><?php echo __('First seen');?></td>
|
||||
<td><?php echo h($data['Object']['first_seen']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bold"><?php echo __('Last seen');?></td>
|
||||
<td><?php echo h($data['Object']['last_seen']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<table id="attribute_table" class="table table-condensed table-striped">
|
||||
<thead>
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
.info_container_eventtimeline {
|
||||
margin-top:10px;
|
||||
border: 1px solid #0088cc;
|
||||
border-radius: 7px;
|
||||
box-shadow: 0px 0px 6px #B2B2B2;
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.vis-timeline {
|
||||
position: relative;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.fullscreen-btn-timeline {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
bottom: 33px;
|
||||
position: relative;
|
||||
right: 3px;
|
||||
opacity: 0.7;
|
||||
z-index: 1;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.loadingTimeline {
|
||||
display: none;
|
||||
position: absolute;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loadingTimeline > div.loadingText {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.timestamp-obj {
|
||||
box-shadow: #ff0000 0px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.timestamp-attr {
|
||||
box-shadow: #ff0000 0px 0px 0px 5px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.vis-item.attribute {
|
||||
background-color: orange;
|
||||
border-color: black;
|
||||
}
|
||||
.vis-item.attribute.vis-selected {
|
||||
box-shadow: 0 0 20px rgba(82, 168, 236, 1);`
|
||||
}
|
||||
|
||||
.vis-item.object {
|
||||
background-color: #3465a4;
|
||||
border-color: black;
|
||||
color: white;
|
||||
}
|
||||
.vis-item.object.vis-selected {
|
||||
box-shadow: 0 0 20px rgba(82, 168, 236, 1);`
|
||||
}
|
||||
|
||||
.vis-item.object_attribute {
|
||||
background-color: greenyellow;
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
.timeline-objectName {
|
||||
text-align:left;
|
||||
border-bottom: white solid 1px;
|
||||
}
|
||||
.timeline-objectAttrType {
|
||||
text-align:left;
|
||||
line-height: 14px;
|
||||
font-style: italic;
|
||||
}
|
||||
.timeline-objectAttrVal {
|
||||
text-align:left;
|
||||
line-height: 14px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.vis-delete {
|
||||
top: -8px !important;
|
||||
padding: 3px !important;
|
||||
height: 22px !important;
|
||||
width: 20px !important;
|
||||
}
|
||||
|
||||
.vis-expand-action {
|
||||
position: absolute;
|
||||
|
||||
padding: 3px 2px !important;
|
||||
height: 22px !important;
|
||||
width: 22px !important;
|
||||
|
||||
|
||||
top: 15px;
|
||||
right: -25px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
-webkit-transition: background 0.2s linear;
|
||||
-moz-transition: background 0.2s linear;
|
||||
-ms-transition: background 0.2s linear;
|
||||
-o-transition: background 0.2s linear;
|
||||
transition: background 0.2s linear;
|
||||
}
|
||||
.vis-item .vis-expand-action:after {
|
||||
color: green;
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
|
||||
-webkit-transition: color 0.2s linear;
|
||||
-moz-transition: color 0.2s linear;
|
||||
-ms-transition: color 0.2s linear;
|
||||
-o-transition: color 0.2s linear;
|
||||
transition: color 0.2s linear;
|
||||
}
|
||||
.expand-btn:after {
|
||||
content: "\21d4";
|
||||
}
|
||||
.collapse-btn:after {
|
||||
content: "\21b5";
|
||||
}
|
||||
.vis-item .vis-expand-action:hover {
|
||||
background: green;
|
||||
}
|
||||
.vis-item .vis-expand-action:hover:after {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.vis-onUpdateTime-tooltip {
|
||||
background: #7b7b7b !important;
|
||||
padding: 0px !important;
|
||||
border-radius: 5px !important;
|
||||
border: solid 1px #000 !important;
|
||||
}
|
||||
|
||||
#timeline-typeahead {
|
||||
width: 400px;
|
||||
}
|
|
@ -21,6 +21,10 @@ button .full-width {
|
|||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
form button.btn[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/*.close{
|
||||
float:none;
|
||||
}*/
|
||||
|
@ -289,6 +293,26 @@ td.highlight3 {
|
|||
|
||||
}
|
||||
|
||||
table-striped tbody > tr > td.objectAddField {
|
||||
padding: 2px 3px;
|
||||
line-height: normal;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -13px;
|
||||
left: -4px;
|
||||
color: white;
|
||||
background-color: #1369a0;
|
||||
border: 0px;
|
||||
border-radius: 15px;
|
||||
height: 12px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.table-striped tbody > tr > td.objectAddField:hover {
|
||||
transform: scale(1.7);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr.highlightBlueSides {
|
||||
border-left:2px solid #0088cc;
|
||||
border-right:2px solid #0088cc;
|
||||
|
@ -836,6 +860,20 @@ a.proposal_link_red:hover {
|
|||
border: 0;
|
||||
}
|
||||
|
||||
.immutableAttributeDescription:before {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
content: '';
|
||||
margin-right: 5px;
|
||||
margin-left: 2px;
|
||||
border-left: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
height: 10px;
|
||||
width: 5px;
|
||||
position: relative;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ajax_popover_form {
|
||||
display:none;
|
||||
width: 700px;
|
||||
|
@ -2219,6 +2257,23 @@ table tr:hover .down-expand-button {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.nanosec-div {
|
||||
font-weight:bold;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.larger-input-field {
|
||||
width: 270px !important;
|
||||
}
|
||||
|
||||
.precision-tool {
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.fa-as-icon {
|
||||
font-size: 20px;
|
||||
padding-left: 2px;
|
||||
|
|
|
@ -1461,6 +1461,7 @@
|
|||
return i.jquery ? i[0] : i;
|
||||
});
|
||||
delete options.inputs;
|
||||
this.preventMultipleSet = options.preventMultipleSet;
|
||||
|
||||
datepickerPlugin.call($(this.inputs), options)
|
||||
.on('changeDate', $.proxy(this.dateUpdated, this));
|
||||
|
@ -1507,10 +1508,12 @@
|
|||
if (i === -1)
|
||||
return;
|
||||
|
||||
$.each(this.pickers, function(i, p){
|
||||
if (!p.getUTCDate())
|
||||
p.setUTCDate(new_date);
|
||||
});
|
||||
if (!this.preventMultipleSet) {
|
||||
$.each(this.pickers, function(i, p){
|
||||
if (!p.getUTCDate())
|
||||
p.setUTCDate(new_date);
|
||||
});
|
||||
}
|
||||
|
||||
if (new_date < this.dates[j]){
|
||||
// Date being moved earlier/left
|
||||
|
|
|
@ -143,6 +143,7 @@ class ContextualMenu {
|
|||
__create_menu_div_bootstrap_popover() {
|
||||
var div = document.createElement('div');
|
||||
div.classList.add("contextual-menu");
|
||||
div.style.display = 'none';
|
||||
this.container.appendChild(div);
|
||||
var that = this;
|
||||
this.trigger_container.tabIndex = 0; // required for the popover focus feature
|
||||
|
@ -151,19 +152,20 @@ class ContextualMenu {
|
|||
container: 'body',
|
||||
html: true,
|
||||
placement: "bottom",
|
||||
content: function () {return $(that.menu); }, // return contextual menu html
|
||||
content: function () { var html=$(that.menu); html.css('display', 'inline-block'); return html;}, // return contextual menu html
|
||||
trigger: "manual",
|
||||
template: '<div class="popover" id="popover-contextual-menu-'+this.trigger_container.id+'" role="tooltip" style="'+additional_styling+'"><div class="arrow"></div></h3><div class="popover-content"></div></div>'
|
||||
})
|
||||
|
||||
// Overwrite the default popover behavior: hidding cause the popover to be detached from the DOM, making impossible to fetch input values in the form
|
||||
$(this.trigger_container).click (function(e) {
|
||||
if (that.has_been_shown_once) {
|
||||
$('#popover-contextual-menu-'+this.id).toggle();
|
||||
} else {
|
||||
that.has_been_shown_once = true;
|
||||
$(this).popover('show');
|
||||
}
|
||||
$(this).popover('toggle');
|
||||
// if (that.has_been_shown_once) {
|
||||
// $('#popover-contextual-menu-'+this.id).toggle();
|
||||
// } else {
|
||||
// that.has_been_shown_once = true;
|
||||
// $(this).popover('show');
|
||||
// }
|
||||
});
|
||||
return div;
|
||||
}
|
||||
|
@ -371,7 +373,7 @@ class ContextualMenu {
|
|||
this.menu.appendChild(label);
|
||||
this.menu.appendChild(div);
|
||||
if(options.event !== undefined) {
|
||||
button.addEventListener("click", function(evt) {
|
||||
button.addEventListener("click", function(evt) {
|
||||
var corresponding_select_id = evt.target.dataset.correspondingId;
|
||||
var selected_value = $('#'+corresponding_select_id).val();
|
||||
options.event(selected_value);
|
||||
|
|
|
@ -155,7 +155,7 @@ class EventGraph {
|
|||
$("#select_graph_scope").val(value);
|
||||
}
|
||||
|
||||
if (value == "Rotation key") {
|
||||
if (value == "Pivot key") {
|
||||
$("#network-scope-badge").text(value + ": " + eventGraph.scope_keyType);
|
||||
} else {
|
||||
$("#network-scope-badge").text(value);
|
||||
|
@ -176,30 +176,30 @@ class EventGraph {
|
|||
label: "Scope",
|
||||
tooltip: "The scope represented by the network",
|
||||
event: function(value) {
|
||||
if (value == "Rotation key" && $('#input_graph_scope_jsonkey').val() == "") { // no key selected for Rotation key scope
|
||||
if (value == "Pivot key" && $('#input_graph_scope_jsonkey').val() == "") { // no key selected for Pivot key scope
|
||||
return;
|
||||
} else {
|
||||
eventGraph.update_scope(value);
|
||||
dataHandler.fetch_data_and_update();
|
||||
}
|
||||
},
|
||||
options: ["Reference", "Tag", "Rotation key"],
|
||||
options: ["Reference", "Tag", "Pivot key"],
|
||||
default: "Reference"
|
||||
});
|
||||
menu_scope.add_select({
|
||||
id: "input_graph_scope_jsonkey",
|
||||
label: "Rotation key",
|
||||
label: "Pivot key",
|
||||
tooltip: "The key around which the network will be constructed",
|
||||
event: function(value) {
|
||||
if (value == "Rotation key" && $('#input_graph_scope_jsonkey').val() == "") { // no key selected for Rotation key scope
|
||||
if (value == "Pivot key" && $('#input_graph_scope_jsonkey').val() == "") { // no key selected for Pivot key scope
|
||||
return;
|
||||
} else {
|
||||
eventGraph.scope_keyType = value;
|
||||
eventGraph.update_scope("Rotation key");
|
||||
eventGraph.update_scope("Pivot key");
|
||||
dataHandler.fetch_data_and_update();
|
||||
}
|
||||
},
|
||||
options: dataHandler.available_rotation_key ? dataHandler.available_rotation_key : [],
|
||||
options: dataHandler.available_pivot_key ? dataHandler.available_pivot_key : [],
|
||||
default: ""
|
||||
});
|
||||
return menu_scope;
|
||||
|
@ -298,7 +298,8 @@ class EventGraph {
|
|||
for(var nodeId of objectIds) {
|
||||
eventGraph.expand_node(nodeId);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: "Expanding all nodes may takes some time"
|
||||
});
|
||||
menu_display.add_button({
|
||||
label: "Collapse all nodes",
|
||||
|
@ -310,7 +311,8 @@ class EventGraph {
|
|||
for(var nodeId of objectIds) {
|
||||
eventGraph.collapse_node(nodeId);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: "Collapsing all nodes may takes some time"
|
||||
});
|
||||
menu_display.add_slider({
|
||||
id: 'slider_display_max_char_num',
|
||||
|
@ -1427,8 +1429,8 @@ class DataHandler {
|
|||
return [[index, value]];
|
||||
});
|
||||
dataHandler.update_filtering_selectors(available_object_references, available_tags);
|
||||
dataHandler.available_rotation_key = data.available_rotation_key;
|
||||
eventGraph.menu_scope.add_options("input_graph_scope_jsonkey", dataHandler.available_rotation_key);
|
||||
dataHandler.available_pivot_key = data.available_pivot_key;
|
||||
eventGraph.menu_scope.add_options("input_graph_scope_jsonkey", dataHandler.available_pivot_key);
|
||||
if (data.items.length < nodes_ask_threshold) {
|
||||
eventGraph.update_graph(data);
|
||||
} else if (data.items.length > nodes_ask_threshold && confirm("The network contains a lot of nodes, displaying it may slow down your browser. Continue?")) {
|
||||
|
|
|
@ -0,0 +1,586 @@
|
|||
var max_displayed_char_timeline = 64;
|
||||
var eventTimeline;
|
||||
var items_timeline;
|
||||
var items_backup;
|
||||
var use_local_timezone = true;
|
||||
var mapping_text_to_id = new Map();
|
||||
var user_manipulation = $('#event_timeline').data('user-manipulation');
|
||||
var extended_text = $('#event_timeline').data('extended') == 1 ? "extended:1/" : "";
|
||||
var container_timeline = document.getElementById('event_timeline');
|
||||
var hardThreshold = 50;
|
||||
var softThreshold = 30;
|
||||
var timeline_disabled = false;
|
||||
var default_editable = {
|
||||
add: false, // add new items by double tapping
|
||||
updateTime: true, // drag items horizontally
|
||||
remove: true
|
||||
};
|
||||
var relationship_type_mapping = {
|
||||
'followed-by': 'after',
|
||||
'preceding-by': 'before',
|
||||
}
|
||||
var options = {
|
||||
template: function (item, element, data) {
|
||||
switch(item.group) {
|
||||
case "attribute":
|
||||
return build_attr_template(item);
|
||||
|
||||
case "object":
|
||||
return build_object_template(item);
|
||||
|
||||
case "object_attribute":
|
||||
console.log('Error');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
moment: function(date) {
|
||||
if (use_local_timezone) {
|
||||
return vis.moment(date);
|
||||
} else {
|
||||
return vis.moment(date).utc();
|
||||
}
|
||||
},
|
||||
verticalScroll: true,
|
||||
zoomKey: 'altKey',
|
||||
maxHeight: 400,
|
||||
minHeight: 400,
|
||||
multiselect: true,
|
||||
editable: user_manipulation ? default_editable : false,
|
||||
tooltipOnItemUpdateTime: true,
|
||||
onRemove: function(item, callback) { // clear timestamps
|
||||
update_seen(item, 'first', null, false, undefined);
|
||||
update_seen(item, 'last', null, false, function() { reflect_change(true); });
|
||||
eventTimeline.setSelection([]);
|
||||
$('.timelineSelectionTooltip').remove()
|
||||
},
|
||||
onMove: function(item, callback) {
|
||||
var newStart = moment(item.start.toISOString());
|
||||
var newEnd = (item.end !== undefined && item.end !== null) ? moment(item.end.toISOString()) : null;
|
||||
var c1 = item.first_seen !== null ? !item.first_seen.isSame(newStart) : true;
|
||||
var c2 = item.last_seen !== null ? !item.last_seen.isSame(newEnd) && item.seen_enabled : true;
|
||||
if (c1) {
|
||||
if (item.first_seen === null) {
|
||||
if (!c2) {
|
||||
update_seen(item, 'first', newStart, true, undefined);
|
||||
} else {
|
||||
update_seen(item, 'first', newStart, false, function() { reflect_change(true); });
|
||||
}
|
||||
} else {
|
||||
update_seen(item, 'first', newStart, !c2, undefined);
|
||||
}
|
||||
}
|
||||
if (c2) {
|
||||
update_seen(item, 'last', newEnd, true, undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
var timeline_typeaheadDataSearch;
|
||||
var timeline_typeaheadOption = {
|
||||
source: function (query, process) {
|
||||
if (timeline_typeaheadDataSearch === undefined) { // caching
|
||||
timeline_typeaheadDataSearch = Array.from(mapping_text_to_id.keys());
|
||||
}
|
||||
process(timeline_typeaheadDataSearch);
|
||||
},
|
||||
updater: function(value) {
|
||||
var id = mapping_text_to_id.get(value);
|
||||
eventTimeline.focus(id);
|
||||
$("#timeline-typeahead").blur();
|
||||
},
|
||||
autoSelect: true
|
||||
}
|
||||
|
||||
function isDefined(element) {
|
||||
return element !== undefined && element !== null;
|
||||
}
|
||||
|
||||
function generate_timeline_tooltip(itemID, target) {
|
||||
var item = items_timeline.get(itemID);
|
||||
if (
|
||||
item.first_seen === undefined
|
||||
|| item.first_seen === null
|
||||
|| item.first_seen_overwrite
|
||||
) { // do not generate if first_seen not set
|
||||
return;
|
||||
}
|
||||
var closest = $(target.closest(".vis-selected.vis-editable"));
|
||||
var btn_type = item.last_seen !== null ? 'collapse-btn' : 'expand-btn';
|
||||
var fct_type = item.last_seen !== null ? 'collapseItem' : 'expandItem';
|
||||
var btn = $('<div class="timelineSelectionTooltip vis-expand-action '+btn_type+'" data-itemid="'+item.id+'"></div>')
|
||||
if (item.last_seen !== null) {
|
||||
btn.click(collapseItem);
|
||||
} else {
|
||||
btn.click(expandItem);
|
||||
}
|
||||
closest.append(btn);
|
||||
}
|
||||
|
||||
/* UTIL */
|
||||
function collapseItem() {
|
||||
var itemID = $(this).data('itemid');
|
||||
var item = items_timeline.get(itemID);
|
||||
update_seen(item, 'last', null, true, undefined);
|
||||
}
|
||||
function expandItem() {
|
||||
var itemID = $(this).data('itemid');
|
||||
var item = items_timeline.get(itemID);
|
||||
var newEnd = get_next_step(item.first_seen);
|
||||
update_seen(item, 'last', newEnd, true, undefined);
|
||||
}
|
||||
|
||||
function get_next_step(mom) {
|
||||
var scale = eventTimeline.timeAxis.step.scale;
|
||||
var momAhead = mom.clone();
|
||||
momAhead.add(1, scale);
|
||||
return momAhead;
|
||||
}
|
||||
|
||||
function build_attr_template(attr) {
|
||||
var span = $('<span data-itemID="'+attr.id+'">');
|
||||
if (!attr.seen_enabled) {
|
||||
span.addClass('timestamp-attr');
|
||||
}
|
||||
span.text(attr.content);
|
||||
span.data('seen_enabled', attr.seen_enabled);
|
||||
var html = span[0].outerHTML;
|
||||
return html;
|
||||
}
|
||||
|
||||
function build_object_template(obj) {
|
||||
var table = $('<table>');
|
||||
table.data('seen_enabled', obj.seen_enabled);
|
||||
if (!obj.seen_enabled) {
|
||||
table.addClass('timestamp-obj');
|
||||
}
|
||||
var bolt_html = obj.overwrite_enabled ? " <i class=\"fa fa-bolt\" style=\"color: yellow; font-size: large;\" title=\"Object is (or can be) overwritten by its attributes\">" : "";
|
||||
table.append($('<tr class="timeline-objectName"><th>'+obj.content+bolt_html+'</th><th></th></tr>'));
|
||||
for (var attr of obj.Attribute) {
|
||||
var overwritten = obj.overwrite_enabled && (attr.contentType == "first-seen" || attr.contentType == "last-seen") ? " <i class=\"fa fa-bolt\" style=\"color: yellow;\" title=\"Overwrite object "+attr.contentType+"\"></i>" : "";
|
||||
table.append(
|
||||
$('<tr>').append(
|
||||
$('<td class="timeline-objectAttrType">' + attr.contentType + '</td>'
|
||||
+'<td class="timeline-objectAttrVal">' + attr.content+overwritten + '</td>'
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
var html = table[0].outerHTML;
|
||||
return html;
|
||||
}
|
||||
|
||||
function reflect_change(onIndex, itemType, itemId) {
|
||||
if (onIndex) {
|
||||
updateIndex(scope_id, 'event'); // MISP function
|
||||
} else { // reflect change on item only
|
||||
quick_fetch_seens(itemType, itemId, function(firstSeen, lastSeen) {
|
||||
var updatedItem = items_timeline.get(itemId);
|
||||
updatedItem.first_seen = firstSeen;
|
||||
updatedItem.last_seen = lastSeen;
|
||||
updatedItem.first_seen_overwrite = false;
|
||||
updatedItem.last_seen_overwrite = false;
|
||||
var e = $.extend({}, default_editable);
|
||||
e.remove = true;
|
||||
updatedItem.editable = e;
|
||||
set_spanned_time(updatedItem);
|
||||
items_timeline.remove(updatedItem.id);
|
||||
items_timeline.add(updatedItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function quick_fetch_seens(itemType, itemId, callback) {
|
||||
var url = "/" + itemType + "/" + "fetchViewValue" + "/" + itemId + "/";
|
||||
var dfs = $.ajax({
|
||||
dataType: "html",
|
||||
cache: false,
|
||||
success: function(data, textStatus) {
|
||||
return data;
|
||||
},
|
||||
url: url+"first_seen"
|
||||
});
|
||||
var dls = $.ajax({
|
||||
dataType: "html",
|
||||
cache: false,
|
||||
success: function(data, textStatus) {
|
||||
return data;
|
||||
},
|
||||
url: url+"last_seen"
|
||||
});
|
||||
|
||||
$.when( dfs, dls).done(function(a1, a2) {
|
||||
firstSeen = a1[0].replace(' ', '');
|
||||
firstSeen = firstSeen == '' ? null : firstSeen;
|
||||
lastSeen = a2[0].replace(' ', '');
|
||||
lastSeen = lastSeen == '' ? null : lastSeen;
|
||||
callback(firstSeen, lastSeen);
|
||||
});
|
||||
}
|
||||
|
||||
function update_seen(item, seenType, value, reflect, callback) {
|
||||
var itemType = item.group + 's';
|
||||
var momentISO = value !== null ? value.toISOString() : null;
|
||||
fetch_form_and_submit(itemType, item, seenType, momentISO, reflect, callback);
|
||||
}
|
||||
|
||||
function fetch_form_and_submit(itemType, item, seenType, value, reflect, callback) {
|
||||
var url = "/" + itemType + "/fetchEditForm/" + item.id + "/" + seenType+"_seen";
|
||||
$.ajax({
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loadingTimeline").show();
|
||||
},
|
||||
dataType:"html",
|
||||
cache: false,
|
||||
success: function (data, textStatus) {
|
||||
var form = $(data);
|
||||
$(container_timeline).append(form);
|
||||
form.css({display: 'none'});
|
||||
var field = form.find('input[name*="' + seenType + '_seen"]');
|
||||
field.val(value);
|
||||
// submit the form
|
||||
$.ajax({
|
||||
data: form.serialize(),
|
||||
cache: false,
|
||||
success:function (data, textStatus) {
|
||||
if (reflect) {
|
||||
reflect_change(false, itemType, item.id);
|
||||
}
|
||||
form.remove()
|
||||
},
|
||||
error:function() {
|
||||
console.log('fail', 'Request failed for an unknown reason.');
|
||||
},
|
||||
complete: function () {
|
||||
$(".loadingTimeline").hide();
|
||||
if (callback !== undefined) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
type:"post",
|
||||
url: form.attr('action')
|
||||
});
|
||||
},
|
||||
error: function() {
|
||||
console.log('Feature not supported.');
|
||||
},
|
||||
url: url,
|
||||
});
|
||||
}
|
||||
|
||||
function timestampToMoment(timestamp) {
|
||||
var factor = 1000;
|
||||
var d = moment(timestamp*factor);
|
||||
return d;
|
||||
}
|
||||
|
||||
function set_spanned_time(item) {
|
||||
var timestamp = item.timestamp;
|
||||
var fs = item.first_seen == null ? null : moment(item.first_seen);
|
||||
var ls = item.last_seen == null ? null : moment(item.last_seen);
|
||||
item.first_seen = fs;
|
||||
item.last_seen = ls;
|
||||
|
||||
item.seen_enabled = false;
|
||||
item.overwrite_enabled = false;
|
||||
if (fs===null && ls===null) {
|
||||
item.start = timestampToMoment(timestamp);
|
||||
item.type = 'box';
|
||||
|
||||
} else if (fs===null && ls!==null) {
|
||||
item.start = timestampToMoment(timestamp);
|
||||
item.type = 'box';
|
||||
|
||||
} else if (ls===null && fs!==null) {
|
||||
item.start = fs;
|
||||
item.seen_enabled = true;
|
||||
delete item.end;
|
||||
item.type = 'box';
|
||||
|
||||
} else { // fs and ls are defined
|
||||
item.start = fs;
|
||||
item.end = ls;
|
||||
item.seen_enabled = true;
|
||||
if (fs == ls) {
|
||||
item.type = 'box';
|
||||
} else {
|
||||
item.type = 'range';
|
||||
}
|
||||
}
|
||||
|
||||
if (item.first_seen_overwrite === true || item.last_seen_overwrite === true) {
|
||||
var e = $.extend({}, default_editable);
|
||||
e.remove = false;
|
||||
item.editable = e;
|
||||
item.overwrite_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
function map_scope(val) {
|
||||
switch(val) {
|
||||
case 'First seen/Last seen':
|
||||
return 'seen';
|
||||
case 'Object relationship':
|
||||
return 'relationship';
|
||||
default:
|
||||
return 'seen';
|
||||
}
|
||||
}
|
||||
|
||||
function timelinePopupCallback(state) {
|
||||
if (eventTimeline === undefined) {
|
||||
return;
|
||||
}
|
||||
reload_timeline();
|
||||
}
|
||||
|
||||
function adjust_text_length(elem) {
|
||||
var maxChar = $('#slider_timeline_display_max_char_num').val();
|
||||
elem.content = elem.content.substring(0, maxChar) + (elem.content.length < maxChar ? "" : "[...]");
|
||||
}
|
||||
|
||||
function update_badge() {
|
||||
if (use_local_timezone) {
|
||||
$("#timeline-display-badge").text("Timezone: " + ": " + moment().format('Z'));
|
||||
} else {
|
||||
$("#timeline-display-badge").text("Timezone: " + ": " + moment().utc().format('Z (z)'));
|
||||
}
|
||||
}
|
||||
|
||||
function reload_timeline() {
|
||||
update_badge();
|
||||
var payload = {scope: map_scope($('#select_timeline_scope').val())};
|
||||
$.ajax({
|
||||
url: "/events/"+"getEventTimeline"+"/"+scope_id+"/"+extended_text+"event.json",
|
||||
dataType: 'json',
|
||||
type: 'post',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify( payload ),
|
||||
processData: false,
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loadingTimeline").show();
|
||||
},
|
||||
success: function( data, textStatus, jQxhr ){
|
||||
items_timeline.clear();
|
||||
for (var item of data.items) {
|
||||
item.className = item.group;
|
||||
set_spanned_time(item);
|
||||
if (item.group == 'object') {
|
||||
for (var attr of item.Attribute) {
|
||||
mapping_text_to_id.set(attr.contentType+': '+attr.content+' ('+item.id+')', item.id);
|
||||
adjust_text_length(attr);
|
||||
}
|
||||
} else {
|
||||
mapping_text_to_id.set(item.content+' ('+item.id+')', item.id);
|
||||
adjust_text_length(item);
|
||||
}
|
||||
}
|
||||
items_timeline.add(data.items);
|
||||
handle_not_seen_enabled($('#checkbox_timeline_display_hide_not_seen_enabled').val())
|
||||
},
|
||||
error: function( jqXhr, textStatus, errorThrown ){
|
||||
console.log( errorThrown );
|
||||
},
|
||||
complete: function() {
|
||||
$(".loadingTimeline").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function enable_timeline() {
|
||||
if (eventTimeline !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_popover();
|
||||
|
||||
$('#timeline-typeahead').typeahead(timeline_typeaheadOption);
|
||||
|
||||
var payload = {scope: map_scope($('#select_timeline_scope').val())};
|
||||
$.ajax({
|
||||
url: "/events/"+"getEventTimeline"+"/"+scope_id+"/"+extended_text+"event.json",
|
||||
dataType: 'json',
|
||||
type: 'post',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify( payload ),
|
||||
processData: false,
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loadingTimeline").show();
|
||||
},
|
||||
success: function( data, textStatus, jQxhr ){
|
||||
if (data.items.length > hardThreshold) {
|
||||
$('#eventtimeline_div').html('<div class="alert alert-danger" style="margin: 10px;">Timeline: To much data to show</div>');
|
||||
timeline_disabled = true;
|
||||
return;
|
||||
} else if (data.items.length > softThreshold) {
|
||||
var res = confirm('You are about to draw a lot ('+data.items.length+') of items in the timeline. Do you wish to continue?');
|
||||
if (!res) {
|
||||
$('#eventtimeline_div').html('<div class="alert alert-danger" style="margin: 10px;">Timeline: To much data to show</div>');
|
||||
timeline_disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (var item of data.items) {
|
||||
item.className = item.group;
|
||||
set_spanned_time(item);
|
||||
if (item.group == 'object') {
|
||||
for (var attr of item.Attribute) {
|
||||
mapping_text_to_id.set(attr.contentType+': '+attr.content+' ('+item.id+')', item.id);
|
||||
adjust_text_length(attr);
|
||||
}
|
||||
} else {
|
||||
mapping_text_to_id.set(item.content+' ('+item.id+')', item.id);
|
||||
adjust_text_length(item);
|
||||
}
|
||||
}
|
||||
items_timeline = new vis.DataSet(data.items);
|
||||
eventTimeline = new vis.Timeline(container_timeline, items_timeline, options);
|
||||
update_badge();
|
||||
|
||||
eventTimeline.on('select', handle_selection);
|
||||
|
||||
eventTimeline.on('doubleClick', handle_doubleClick);
|
||||
|
||||
items_timeline.on('update', function(eventname, data) {
|
||||
handle_selection({
|
||||
event: { target: $('span[data-itemID="'+data.items[0]+'"]')},
|
||||
items: data.items
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function( jqXhr, textStatus, errorThrown ){
|
||||
console.log( errorThrown );
|
||||
},
|
||||
complete: function() {
|
||||
$(".loadingTimeline").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handle_selection(data) {
|
||||
var event = data.event;
|
||||
var target = event.target;
|
||||
var items = data.items;
|
||||
|
||||
if (items.length == 0) {
|
||||
$('.timelineSelectionTooltip').remove()
|
||||
} else {
|
||||
for (var itemID of items) {
|
||||
generate_timeline_tooltip(itemID, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function edit_item(id, callback) {
|
||||
var group = items_timeline.get(id).group;
|
||||
if (group == 'attribute') {
|
||||
simplePopup('/attributes/edit/'+id);
|
||||
} else if (group == 'object') {
|
||||
window.location = '/objects/edit/'+id;
|
||||
}
|
||||
}
|
||||
|
||||
function handle_doubleClick(data) {
|
||||
// should be replaced by keyboard shortcut: SHIFT+E ?
|
||||
//edit_item(data.item);
|
||||
}
|
||||
|
||||
function handle_not_seen_enabled(hide) {
|
||||
if (hide) {
|
||||
var hidden = items_timeline.get({
|
||||
filter: function(item) {
|
||||
return !item.seen_enabled;
|
||||
}
|
||||
});
|
||||
var hidden_ids = [];
|
||||
items_timeline.forEach(function(item) {
|
||||
hidden_ids.push(item.id);
|
||||
});
|
||||
items_timeline.remove(hidden)
|
||||
items_backup = hidden;
|
||||
} else {
|
||||
items_timeline.add(items_backup);
|
||||
}
|
||||
}
|
||||
|
||||
$('#fullscreen-btn-timeline').click(function() {
|
||||
var timeline_div = $('#eventtimeline_div');
|
||||
var fullscreen_enabled = !timeline_div.data('fullscreen');
|
||||
timeline_div.data('fullscreen', fullscreen_enabled);
|
||||
var height_val = fullscreen_enabled == true ? "calc(100vh - 42px - 42px - 10px)" : "400px";
|
||||
|
||||
timeline_div.css("max-height", height_val);
|
||||
setTimeout(function() { // timeline takes time to be drawn
|
||||
timeline_div[0].scrollIntoView({
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, 1);
|
||||
eventTimeline.setOptions({maxHeight: height_val});
|
||||
});
|
||||
|
||||
// init_scope_menu
|
||||
var menu_scope_timeline, menu_display_timeline;
|
||||
function init_popover() {
|
||||
if (timeline_disabled) return;
|
||||
menu_scope_timeline = new ContextualMenu({
|
||||
trigger_container: document.getElementById("timeline-scope"),
|
||||
bootstrap_popover: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventtimeline_div")
|
||||
});
|
||||
menu_scope_timeline.add_select({
|
||||
id: "select_timeline_scope",
|
||||
label: "Scope",
|
||||
tooltip: "The time scope represented by the timeline",
|
||||
event: function(value) {
|
||||
if (value == "First seen/Last seen") {
|
||||
reload_timeline();
|
||||
}
|
||||
},
|
||||
options: ["First seen/Last seen"],
|
||||
default: "First seen/Last seen"
|
||||
});
|
||||
|
||||
menu_display_timeline = new ContextualMenu({
|
||||
trigger_container: document.getElementById("timeline-display"),
|
||||
bootstrap_popover: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventtimeline_div")
|
||||
});
|
||||
menu_display_timeline.add_slider({
|
||||
id: 'slider_timeline_display_max_char_num',
|
||||
label: "Charater to show",
|
||||
title: "Maximum number of charater to display in the label",
|
||||
min: 8,
|
||||
max: 2048,
|
||||
value: max_displayed_char_timeline,
|
||||
step: 8,
|
||||
applyButton: true,
|
||||
event: function(value) {
|
||||
$("#slider_timeline__display_max_char_num").parent().find("span").text(value);
|
||||
},
|
||||
eventApply: function(value) {
|
||||
reload_timeline();
|
||||
}
|
||||
});
|
||||
menu_display_timeline.add_checkbox({
|
||||
id: 'checkbox_timeline_display_hide_not_seen_enabled',
|
||||
label: "Hide first seen not set",
|
||||
title: "Hide items that does not have first seen sets",
|
||||
event: function(value) {
|
||||
handle_not_seen_enabled(value)
|
||||
}
|
||||
});
|
||||
menu_display_timeline.add_checkbox({
|
||||
id: 'checkbox_timeline_display_gmt',
|
||||
label: "Display with current timezone",
|
||||
title: "Set the dates relative to the browser timezone. Otherwise, keep dates in GMT",
|
||||
event: function(value) {
|
||||
use_local_timezone = value;
|
||||
reload_timeline()
|
||||
},
|
||||
checked: true
|
||||
});
|
||||
}
|
|
@ -409,6 +409,11 @@ function updateIndex(id, context, newPage) {
|
|||
} else {
|
||||
console.log("genericPopupCallback function not defined");
|
||||
}
|
||||
if (typeof timelinePopupCallback !== "undefined") {
|
||||
timelinePopupCallback("success");
|
||||
} else {
|
||||
console.log("timelinepopupcallback function not defined");
|
||||
}
|
||||
},
|
||||
url: url,
|
||||
});
|
||||
|
@ -437,15 +442,42 @@ function updateAttributeFieldOnSuccess(name, type, id, field, event) {
|
|||
});
|
||||
}
|
||||
|
||||
function updateObjectFieldOnSuccess(name, type, id, field, event) {
|
||||
$.ajax({
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
if (field != 'timestamp') {
|
||||
$(".loading").show();
|
||||
}
|
||||
},
|
||||
dataType:"html",
|
||||
cache: false,
|
||||
success:function (data, textStatus) {
|
||||
if (field != 'timestamp') {
|
||||
$(".loading").hide();
|
||||
$(name + '_solid').html(data);
|
||||
$(name + '_placeholder').empty();
|
||||
$(name + '_solid').show();
|
||||
} else {
|
||||
$('#' + type + '_' + id + '_' + 'timestamp_solid').html(data);
|
||||
}
|
||||
},
|
||||
url:"/objects/fetchViewValue/" + id + "/" + field,
|
||||
});
|
||||
}
|
||||
|
||||
function activateField(type, id, field, event) {
|
||||
resetForms();
|
||||
if (type == 'denyForm') return;
|
||||
var objectType = 'attributes';
|
||||
var containerName = 'Attribute';
|
||||
if (type == 'ShadowAttribute') {
|
||||
objectType = 'shadow_attributes';
|
||||
} else if (type == 'Object') {
|
||||
objectType = 'objects';
|
||||
containerName = 'Object';
|
||||
}
|
||||
var name = '#' + type + '_' + id + '_' + field;
|
||||
var container_name = '#Attribute_' + id + '_' + field;
|
||||
var container_name = '#' + containerName + id + '_' + field;
|
||||
$.ajax({
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loading").show();
|
||||
|
@ -583,6 +615,8 @@ function submitForm(type, id, field, context) {
|
|||
var name = '#' + type + '_' + id + '_' + field;
|
||||
if (type == 'ShadowAttribute') {
|
||||
object_type = 'shadow_attributes';
|
||||
} else if (type == 'Object') {
|
||||
object_type = 'objects';
|
||||
}
|
||||
$.ajax({
|
||||
data: $(name + '_field').closest("form").serialize(),
|
||||
|
@ -723,7 +757,8 @@ function handleAjaxEditResponse(data, name, type, id, field, event) {
|
|||
responseArray = data;
|
||||
if (type == 'Attribute') {
|
||||
if (responseArray.saved) {
|
||||
showMessage('success', responseArray.success);
|
||||
var msg = responseArray.success !== undefined ? responseArray.success : responseArray.message;
|
||||
showMessage('success', msg);
|
||||
updateAttributeFieldOnSuccess(name, type, id, field, event);
|
||||
updateAttributeFieldOnSuccess(name, type, id, 'timestamp', event);
|
||||
eventUnpublish();
|
||||
|
@ -734,12 +769,75 @@ function handleAjaxEditResponse(data, name, type, id, field, event) {
|
|||
}
|
||||
if (type == 'ShadowAttribute') {
|
||||
updateIndex(event, 'event');
|
||||
} else if (type == 'Object') {
|
||||
if (responseArray.saved) {
|
||||
var msg = responseArray.success !== undefined ? responseArray.success : responseArray.message;
|
||||
showMessage('success', msg);
|
||||
updateObjectFieldOnSuccess(name, type, id, field, event);
|
||||
updateObjectFieldOnSuccess(name, type, id, 'timestamp', event);
|
||||
eventUnpublish();
|
||||
} else {
|
||||
showMessage('fail', 'Validation failed: ' + responseArray.errors.value);
|
||||
updateObjectFieldOnSuccess(name, type, id, field, event);
|
||||
}
|
||||
}
|
||||
if (responseArray.hasOwnProperty('check_publish')) {
|
||||
checkAndSetPublishedInfo();
|
||||
}
|
||||
}
|
||||
|
||||
function quickFetchValidObjectAttribute(objectId) {
|
||||
var itemType = "objects";
|
||||
var formUrl= "quickFetchTemplateWithValidObjectAttributes";
|
||||
var compiledUrlForm = "/" + itemType + "/" + formUrl + "/" + objectId;
|
||||
$.ajax({
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loading").show();
|
||||
},
|
||||
success:function (data, textStatus) {
|
||||
if (data.fail !== undefined && data.fail) {
|
||||
showMessage('fail', data.errors);
|
||||
} else {
|
||||
$('#popover_form').html(data);
|
||||
openPopup('#popover_form');
|
||||
}
|
||||
},
|
||||
error:function() {
|
||||
showMessage('fail', 'Could not fetch allowed attribute type.');
|
||||
},
|
||||
complete:function() {
|
||||
$(".loading").hide();
|
||||
},
|
||||
type: "get",
|
||||
url: compiledUrlForm
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function fetchAddObjectAttributeForm(objectId, fieldName) {
|
||||
var itemType = "objects";
|
||||
var formUrl= "quickAddAttributeForm";
|
||||
var compiledUrlForm = "/" + itemType + "/" + formUrl + "/" + objectId + "/" + fieldName;
|
||||
$.ajax({
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loading").show();
|
||||
},
|
||||
success:function (data, textStatus) {
|
||||
$('#popover_form').html(data);
|
||||
openPopup('#popover_form');
|
||||
},
|
||||
error:function() {
|
||||
showMessage('fail', 'Could not fetch allowed attribute type.');
|
||||
},
|
||||
complete:function() {
|
||||
$(".loading").hide();
|
||||
},
|
||||
type: "get",
|
||||
url: compiledUrlForm
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleGenericAjaxResponse(data, skip_reload) {
|
||||
if (typeof skip_reload === "undefined") {
|
||||
skip_reload = false;
|
||||
|
@ -1144,6 +1242,9 @@ function submitPopoverForm(context_id, referer, update_context_id) {
|
|||
case 'addObjectReference':
|
||||
url = "/objectReferences/add/" + context_id;
|
||||
break;
|
||||
case 'quickAddAttributeForm':
|
||||
url = "/objects/quickAddAttributeForm/" + context_id;
|
||||
break;
|
||||
}
|
||||
if (url !== null) {
|
||||
$.ajax({
|
||||
|
|
Loading…
Reference in New Issue