mirror of https://github.com/MISP/MISP
Merge branch 'develop' into 2.4
commit
c6710443e0
2
PyMISP
2
PyMISP
|
@ -1 +1 @@
|
|||
Subproject commit 492cfba2d2ad015d3fcda6e16c221fdefd93eca2
|
||||
Subproject commit 4715f91d2ab948fb44640426be6a40099f94c910
|
|
@ -1 +1 @@
|
|||
{"major":2, "minor":4, "hotfix":185}
|
||||
{"major":2, "minor":4, "hotfix":186}
|
||||
|
|
|
@ -85,6 +85,7 @@ class ServerShell extends AppShell
|
|||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
Configure::write('CurrentUserId', $userId);
|
||||
|
||||
if (!empty($this->args[1])) {
|
||||
$technique = $this->args[1];
|
||||
|
@ -129,6 +130,7 @@ class ServerShell extends AppShell
|
|||
$user = $this->getUser($userId);
|
||||
$serverId = $this->args[1];
|
||||
$server = $this->getServer($serverId);
|
||||
Configure::write('CurrentUserId', $userId);
|
||||
if (!empty($this->args[2])) {
|
||||
$technique = $this->args[2];
|
||||
} else {
|
||||
|
@ -146,7 +148,7 @@ class ServerShell extends AppShell
|
|||
try {
|
||||
$result = $this->Server->pull($user, $technique, $server, $jobId, $force);
|
||||
if (is_array($result)) {
|
||||
$message = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]);
|
||||
$message = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled, %s analyst data pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4], $result[5]);
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
} else {
|
||||
$message = __('ERROR: %s', $result);
|
||||
|
@ -169,6 +171,7 @@ class ServerShell extends AppShell
|
|||
$user = $this->getUser($userId);
|
||||
$serverId = $this->args[1];
|
||||
$server = $this->getServer($serverId);
|
||||
Configure::write('CurrentUserId', $userId);
|
||||
$technique = empty($this->args[2]) ? 'full' : $this->args[2];
|
||||
if (!empty($this->args[3])) {
|
||||
$jobId = $this->args[3];
|
||||
|
@ -203,6 +206,7 @@ class ServerShell extends AppShell
|
|||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
Configure::write('CurrentUserId', $userId);
|
||||
$technique = isset($this->args[1]) ? $this->args[1] : 'full';
|
||||
|
||||
$servers = $this->Server->find('list', array(
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class AnalystDataBlocklistsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler', 'BlockList');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 entries <- no we won't, this is the max a user van view/page.
|
||||
'order' => array(
|
||||
'AnalystDataBlocklist.created' => 'DESC'
|
||||
),
|
||||
);
|
||||
|
||||
public function index()
|
||||
{
|
||||
$passedArgsArray = array();
|
||||
$passedArgs = $this->passedArgs;
|
||||
$params = array();
|
||||
$validParams = array('analyst_data_uuid', 'comment', 'analyst_data_info', 'analyst_data_orgc');
|
||||
foreach ($validParams as $validParam) {
|
||||
if (!empty($this->params['named'][$validParam])) {
|
||||
$params[$validParam] = $this->params['named'][$validParam];
|
||||
}
|
||||
}
|
||||
if (!empty($this->params['named']['searchall'])) {
|
||||
$params['AND']['OR'] = array(
|
||||
'analyst_data_uuid' => $this->params['named']['searchall'],
|
||||
'comment' => $this->params['named']['searchall'],
|
||||
'analyst_data_info' => $this->params['named']['searchall'],
|
||||
'analyst_data_orgc' => $this->params['named']['searchall']
|
||||
);
|
||||
}
|
||||
$this->set('passedArgs', json_encode($passedArgs));
|
||||
$this->set('passedArgsArray', $passedArgsArray);
|
||||
return $this->BlockList->index($this->_isRest(), $params);
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$this->set('action', 'add');
|
||||
return $this->BlockList->add($this->_isRest());
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$this->set('action', 'edit');
|
||||
return $this->BlockList->edit($this->_isRest(), $id);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
if (Validation::uuid($id)) {
|
||||
$entry = $this->AnalystDataBlocklist->find('first', array(
|
||||
'conditions' => array('analyst_data_uuid' => $id)
|
||||
));
|
||||
if (empty($entry)) {
|
||||
throw new NotFoundException(__('Invalid blocklist entry'));
|
||||
}
|
||||
$id = $entry['AnalystDataBlocklist']['id'];
|
||||
}
|
||||
return $this->BlockList->delete($this->_isRest(), $id);
|
||||
}
|
||||
|
||||
public function massDelete()
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!isset($this->request->data['AnalystDataBlocklist'])) {
|
||||
$this->request->data = array('AnalystDataBlocklist' => $this->request->data);
|
||||
}
|
||||
$ids = $this->request->data['AnalystDataBlocklist']['ids'];
|
||||
$analyst_data_ids = json_decode($ids, true);
|
||||
if (empty($analyst_data_ids)) {
|
||||
throw new NotFoundException(__('Invalid Analyst Data IDs.'));
|
||||
}
|
||||
$result = $this->AnalystDataBlocklist->deleteAll(array('AnalystDataBlocklist.id' => $analyst_data_ids));
|
||||
if ($result) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('AnalystDataBlocklist', 'Deleted', $ids, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success('Blocklist entry removed');
|
||||
$this->redirect(array('controller' => 'AnalystDataBlocklist', 'action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
$error = __('Failed to delete Analyst Data from AnalystDataBlocklist. Error: ') . PHP_EOL . h($result);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('AnalystDataBlocklist', 'Deleted', false, $error, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error($error);
|
||||
$this->redirect(array('controller' => 'AnalystDataBlocklists', 'action' => 'index'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ids = json_decode($this->request->query('ids'), true);
|
||||
if (empty($ids)) {
|
||||
throw new NotFoundException(__('Invalid analyst data IDs.'));
|
||||
|
||||
}
|
||||
$this->set('analyst_data_ids', $ids);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class AnalystDataController extends AppController
|
||||
{
|
||||
|
||||
public $components = ['Session', 'RequestHandler'];
|
||||
|
||||
public $paginate = [
|
||||
'limit' => 60,
|
||||
'order' => []
|
||||
];
|
||||
|
||||
public $uses = [
|
||||
'Opinion',
|
||||
'Note',
|
||||
'Relationship'
|
||||
];
|
||||
|
||||
private $__valid_types = [
|
||||
'Opinion',
|
||||
'Note',
|
||||
'Relationship'
|
||||
];
|
||||
|
||||
// public $modelSelection = 'Note';
|
||||
|
||||
private function _setViewElements()
|
||||
{
|
||||
$dropdownData = [];
|
||||
$this->loadModel('Event');
|
||||
$dropdownData['distributionLevels'] = $this->Event->distributionLevels;
|
||||
$this->set('initialDistribution', Configure::read('MISP.default_event_distribution'));
|
||||
$dropdownData['sgs'] = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
|
||||
$dropdownData['valid_targets'] = array_combine($this->AnalystData->valid_targets, $this->AnalystData->valid_targets);
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('modelSelection', $this->modelSelection);
|
||||
$this->set('distributionLevels', $this->Event->distributionLevels);
|
||||
App::uses('LanguageRFC5646Tool', 'Tools');
|
||||
$this->set('languageRFC5646', ['' => __('- No language -'), LanguageRFC5646Tool::getLanguages()]);
|
||||
}
|
||||
|
||||
public function add($type = 'Note', $object_uuid = null, $object_type = null)
|
||||
{
|
||||
$this->__typeSelector($type);
|
||||
if (!empty($object_uuid)) {
|
||||
$this->request->data[$this->modelSelection]['object_uuid'] = $object_uuid;
|
||||
}
|
||||
if (!empty($object_type)) {
|
||||
$this->request->data[$this->modelSelection]['object_type'] = $object_type;
|
||||
}
|
||||
|
||||
if (empty($this->request->data[$this->modelSelection]['object_type']) && !empty($this->request->data[$this->modelSelection]['object_uuid'])) {
|
||||
$this->request->data[$this->modelSelection]['object_type'] = $this->AnalystData->deduceType($object_uuid);
|
||||
}
|
||||
$params = [];
|
||||
$this->CRUD->add($params);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->_setViewElements();
|
||||
if ($type == 'Relationship') {
|
||||
$this->set('existingRelations', $this->AnalystData->getExistingRelationships());
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'add_' . strtolower($type)));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function edit($type = 'Note', $id)
|
||||
{
|
||||
if ($type === 'all' && Validation::uuid($id)) {
|
||||
$this->loadModel('AnalystData');
|
||||
$type = $this->AnalystData->deduceType($id);
|
||||
}
|
||||
$this->__typeSelector($type);
|
||||
if (!is_numeric($id) && Validation::uuid($id)) {
|
||||
$id = $this->AnalystData->getIDFromUUID($type, $id);
|
||||
}
|
||||
|
||||
$this->set('id', $id);
|
||||
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
|
||||
$params = [
|
||||
'fields' => $this->AnalystData->getEditableFields(),
|
||||
'conditions' => $conditions,
|
||||
'afterFind' => function(array $analystData): array {
|
||||
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
|
||||
if (!$canEdit) {
|
||||
throw new MethodNotAllowedException(__('You are not authorised to do that.'));
|
||||
}
|
||||
return $analystData;
|
||||
},
|
||||
'beforeSave' => function(array $analystData): array {
|
||||
$analystData[$this->modelSelection]['modified'] = date('Y-m-d H:i:s');
|
||||
return $analystData;
|
||||
}
|
||||
];
|
||||
$this->CRUD->edit($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->_setViewElements();
|
||||
if ($type == 'Relationship') {
|
||||
$this->set('existingRelations', $this->AnalystData->getExistingRelationships());
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'edit'));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($type = 'Note', $id, $hard=true)
|
||||
{
|
||||
if ($type === 'all' && Validation::uuid($id)) {
|
||||
$this->loadModel('AnalystData');
|
||||
$type = $this->AnalystData->deduceType($id);
|
||||
}
|
||||
$this->__typeSelector($type);
|
||||
if (!is_numeric($id) && Validation::uuid($id)) {
|
||||
$id = $this->AnalystData->getIDFromUUID($type, $id);
|
||||
}
|
||||
|
||||
$params = [
|
||||
'afterFind' => function(array $analystData) {
|
||||
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
|
||||
if (!$canEdit) {
|
||||
throw new MethodNotAllowedException(__('You are not authorised to do that.'));
|
||||
}
|
||||
return $analystData;
|
||||
},
|
||||
'afterDelete' => function($deletedAnalystData) use ($hard) {
|
||||
if (empty($hard)) {
|
||||
return;
|
||||
}
|
||||
$type = $this->AnalystData->deduceAnalystDataType($deletedAnalystData);
|
||||
$info = '- Unsupported analyst type -';
|
||||
if ($type === 'Note') {
|
||||
$info = $deletedAnalystData[$type]['note'];
|
||||
} else if ($type === 'Opinion') {
|
||||
$info = sprintf('%s/100 :: %s', $deletedAnalystData[$type]['opinion'], $deletedAnalystData[$type]['comment']);
|
||||
} else if ($type === 'Relationship') {
|
||||
$info = sprintf('-- %s --> %s :: %s', $deletedAnalystData[$type]['relationship_type'] ?? '[undefined]', $deletedAnalystData[$type]['related_object_type'], $deletedAnalystData[$type]['related_object_uuid']);
|
||||
}
|
||||
$this->AnalystDataBlocklist = ClassRegistry::init('AnalystDataBlocklist');
|
||||
$this->AnalystDataBlocklist->create();
|
||||
if (!empty($deletedAnalystData[$type]['orgc_uuid'])) {
|
||||
if (!empty($deletedAnalystData[$type]['Orgc'])) {
|
||||
$orgc = $deletedAnalystData[$type];
|
||||
} else {
|
||||
$orgc = $this->Orgc->find('first', array(
|
||||
'conditions' => ['Orgc.uuid' => $deletedAnalystData[$type]['orgc_uuid']],
|
||||
'recursive' => -1,
|
||||
'fields' => ['Orgc.name'],
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$orgc = ['Orgc' => ['name' => 'MISP']];
|
||||
}
|
||||
$this->AnalystDataBlocklist->save(['analyst_data_uuid' => $deletedAnalystData[$type]['uuid'], 'analyst_data_info' => $info, 'analyst_data_orgc' => $orgc['Orgc']['name']]);
|
||||
}
|
||||
];
|
||||
$this->CRUD->delete($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function view($type = 'Note', $id)
|
||||
{
|
||||
if ($type === 'all' && Validation::uuid($id)) {
|
||||
$this->loadModel('AnalystData');
|
||||
$type = $this->AnalystData->getAnalystDataTypeFromUUID($id);
|
||||
}
|
||||
$this->__typeSelector($type);
|
||||
if (!is_numeric($id) && Validation::uuid($id)) {
|
||||
$id = $this->AnalystData->getIDFromUUID($type, $id);
|
||||
}
|
||||
|
||||
if (!$this->IndexFilter->isRest()) {
|
||||
$this->AnalystData->fetchRecursive = true;
|
||||
}
|
||||
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
|
||||
$this->CRUD->view($id, [
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['Org', 'Orgc'],
|
||||
'afterFind' => function(array $analystData) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
unset($analystData[$this->modelSelection]['_canEdit']);
|
||||
}
|
||||
return $analystData;
|
||||
}
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->loadModel('Event');
|
||||
$this->_setViewElements();
|
||||
$this->set('distributionLevels', $this->Event->distributionLevels);
|
||||
$this->set('shortDist', $this->Event->shortDist);
|
||||
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'view'));
|
||||
$this->render('view');
|
||||
}
|
||||
|
||||
public function index($type = 'Note')
|
||||
{
|
||||
$this->__typeSelector($type);
|
||||
|
||||
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
|
||||
$params = [
|
||||
'filters' => ['uuid', 'target_object'],
|
||||
'quickFilters' => ['name'],
|
||||
'conditions' => $conditions,
|
||||
'afterFind' => function(array $data) {
|
||||
foreach ($data as $i => $analystData) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
unset($analystData[$this->modelSelection]['_canEdit']);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
];
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->_setViewElements();
|
||||
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'index'));
|
||||
}
|
||||
|
||||
public function getRelatedElement($type, $uuid)
|
||||
{
|
||||
$this->__typeSelector('Relationship');
|
||||
$data = $this->AnalystData->getRelatedElement($this->Auth->user(), $type, $uuid);
|
||||
return $this->RestResponse->viewData($data, 'json');
|
||||
}
|
||||
|
||||
public function getChildren($type = 'Note', $uuid, $depth=2)
|
||||
{
|
||||
$this->__typeSelector($type);
|
||||
$data = $this->AnalystData->getChildren($this->Auth->user(), $uuid, $depth);
|
||||
return $this->RestResponse->viewData($data, 'json');
|
||||
}
|
||||
|
||||
public function filterAnalystDataForPush()
|
||||
{
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException(__('This function is only accessible via POST requests.'));
|
||||
}
|
||||
|
||||
$this->loadModel('AnalystData');
|
||||
|
||||
$allIncomingAnalystData = $this->request->data;
|
||||
$allData = $this->AnalystData->filterAnalystDataForPush($allIncomingAnalystData);
|
||||
|
||||
return $this->RestResponse->viewData($allData, $this->response->type());
|
||||
}
|
||||
|
||||
public function indexMinimal()
|
||||
{
|
||||
$this->loadModel('AnalystData');
|
||||
$filters = [];
|
||||
if ($this->request->is('post')) {
|
||||
$filters = $this->request->data;
|
||||
}
|
||||
$options = [];
|
||||
if (!empty($filters['orgc_name'])) {
|
||||
$orgcNames = $filters['orgc_name'];
|
||||
if (!is_array($orgcNames)) {
|
||||
$orgcName = [$orgcNames];
|
||||
}
|
||||
$filterName = 'orgc_uuid';
|
||||
foreach ($orgcNames as $orgcName) {
|
||||
if ($orgcName[0] === '!') {
|
||||
$orgc = $this->AnalystData->Orgc->fetchOrg(substr($orgcName, 1));
|
||||
if ($orgc === false) {
|
||||
continue;
|
||||
}
|
||||
$options[]['AND'][] = ["{$filterName} !=" => $orgc['uuid']];
|
||||
} else {
|
||||
$orgc = $this->AnalystData->Orgc->fetchOrg($orgcName);
|
||||
if ($orgc === false) {
|
||||
continue;
|
||||
}
|
||||
$options['OR'][] = [$filterName => $orgc['uuid']];
|
||||
}
|
||||
}
|
||||
}
|
||||
$allData = $this->AnalystData->indexMinimal($this->Auth->user(), $options);
|
||||
|
||||
return $this->RestResponse->viewData($allData, $this->response->type());
|
||||
}
|
||||
|
||||
public function pushAnalystData()
|
||||
{
|
||||
if (!$this->Auth->user()['Role']['perm_sync'] || !$this->Auth->user()['Role']['perm_analyst_data']) {
|
||||
throw new MethodNotAllowedException(__('You do not have the permission to do that.'));
|
||||
}
|
||||
if (!$this->_isRest()) {
|
||||
throw new MethodNotAllowedException(__('This action is only accessible via a REST request.'));
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$this->loadModel('AnalystData');
|
||||
$analystData = $this->request->data;
|
||||
$saveResult = $this->AnalystData->captureAnalystData($this->Auth->user(), $analystData);
|
||||
$messageInfo = __('%s imported, %s ignored, %s failed. %s', $saveResult['imported'], $saveResult['ignored'], $saveResult['failed'], !empty($saveResult['errors']) ? implode(', ', $saveResult['errors']) : '');
|
||||
if ($saveResult['success']) {
|
||||
$message = __('Analyst Data imported. ') . $messageInfo;
|
||||
return $this->RestResponse->saveSuccessResponse('AnalystData', 'pushAnalystData', false, $this->response->type(), $message);
|
||||
} else {
|
||||
$message = __('Could not import analyst data. ') . $messageInfo;
|
||||
return $this->RestResponse->saveFailResponse('AnalystData', 'pushAnalystData', false, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function __typeSelector($type) {
|
||||
foreach ($this->__valid_types as $vt) {
|
||||
if ($type === $vt) {
|
||||
$this->modelSelection = $vt;
|
||||
$this->loadModel($vt);
|
||||
$this->AnalystData = $this->{$vt};
|
||||
$this->modelClass = $vt;
|
||||
$this->{$vt}->current_user = $this->Auth->user();
|
||||
return $vt;
|
||||
}
|
||||
}
|
||||
throw new MethodNotAllowedException(__('Invalid type.'));
|
||||
}
|
||||
}
|
|
@ -33,8 +33,8 @@ class AppController extends Controller
|
|||
|
||||
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
|
||||
|
||||
private $__queryVersion = '158';
|
||||
public $pyMispVersion = '2.4.185';
|
||||
private $__queryVersion = '159';
|
||||
public $pyMispVersion = '2.4.186';
|
||||
public $phpmin = '7.2';
|
||||
public $phprec = '7.4';
|
||||
public $phptoonew = '8.0';
|
||||
|
@ -306,6 +306,7 @@ class AppController extends Controller
|
|||
$this->set('isAclTagger', $role['perm_tagger']);
|
||||
$this->set('isAclGalaxyEditor', !empty($role['perm_galaxy_editor']));
|
||||
$this->set('isAclSighting', $role['perm_sighting'] ?? false);
|
||||
$this->set('isAclAnalystDataCreator', $role['perm_analyst_data'] ?? false);
|
||||
$this->set('aclComponent', $this->ACL);
|
||||
$this->userRole = $role;
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
use PHPUnit\Framework\MockObject\InvalidMethodNameException;
|
||||
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class CollectionElementsController extends AppController
|
||||
{
|
||||
|
||||
public $components = ['Session', 'RequestHandler'];
|
||||
|
||||
public $paginate = [
|
||||
'limit' => 60,
|
||||
'order' => []
|
||||
];
|
||||
|
||||
public $uses = [
|
||||
];
|
||||
|
||||
public function add($collection_id)
|
||||
{
|
||||
$this->CollectionElement->Collection->current_user = $this->Auth->user();
|
||||
if (!$this->CollectionElement->Collection->mayModify($this->Auth->user('id'), intval($collection_id))) {
|
||||
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
|
||||
}
|
||||
$this->CRUD->add([
|
||||
'beforeSave' => function (array $collectionElement) use ($collection_id) {
|
||||
$collectionElement['CollectionElement']['collection_id'] = intval($collection_id);
|
||||
return $collectionElement;
|
||||
}
|
||||
]);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$dropdownData = [
|
||||
'types' => array_combine($this->CollectionElement->valid_types, $this->CollectionElement->valid_types)
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'add_element'));
|
||||
}
|
||||
|
||||
public function delete($element_id)
|
||||
{
|
||||
$collectionElement = $this->CollectionElement->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'CollectionElement.id' => $element_id
|
||||
]
|
||||
]);
|
||||
$collection_id = $collectionElement['CollectionElement']['collection_id'];
|
||||
if (!$this->CollectionElement->Collection->mayModify($this->Auth->user('id'), $collection_id)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
|
||||
}
|
||||
$this->CRUD->delete($element_id);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function index($collection_id)
|
||||
{
|
||||
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'index'));
|
||||
if (!$this->CollectionElement->Collection->mayView($this->Auth->user('id'), intval($collection_id))) {
|
||||
throw new NotFoundException(__('Invalid collection or no access.'));
|
||||
}
|
||||
$params = [
|
||||
'filters' => ['uuid', 'type', 'name'],
|
||||
'quickFilters' => ['name'],
|
||||
'conditions' => ['collection_id' => $collection_id]
|
||||
];
|
||||
$this->loadModel('Event');
|
||||
$this->set('distributionLevels', $this->Event->distributionLevels);
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function addElementToCollection($element_type, $element_uuid)
|
||||
{
|
||||
if ($this->request->is('get')) {
|
||||
$validCollections = $this->CollectionElement->Collection->find('list', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['Collection.id', 'Collection.name'],
|
||||
'conditions' => ['Collection.orgc_id' => $this->Auth->user('org_id')]
|
||||
]);
|
||||
if (empty($validCollections)) {
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->redirect(['controller' => 'collections', 'action' => 'add']);
|
||||
}
|
||||
throw new NotFoundException(__('You don\'t have any collections yet. Make sure you create one first before you can start adding elements.'));
|
||||
}
|
||||
$dropdownData = [
|
||||
'collections' => $validCollections
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
} else if ($this->request->is('post')) {
|
||||
if (!isset($this->request->data['CollectionElement'])) {
|
||||
$this->request->data = ['CollectionElement' => $this->request->data];
|
||||
}
|
||||
if (!isset($this->request->data['CollectionElement']['collection_id'])) {
|
||||
throw new NotFoundException(__('No collection_id specified.'));
|
||||
}
|
||||
$collection_id = intval($this->request->data['CollectionElement']['collection_id']);
|
||||
if (!$this->CollectionElement->Collection->mayModify($this->Auth->user('id'), $collection_id)) {
|
||||
throw new NotFoundException(__('Invalid collection or not authorized.'));
|
||||
}
|
||||
$description = empty($this->request->data['CollectionElement']['description']) ? '' : $this->request->data['CollectionElement']['description'];
|
||||
$dataToSave = [
|
||||
'CollectionElement' => [
|
||||
'element_uuid' => $element_uuid,
|
||||
'element_type' => $element_type,
|
||||
'description' => $description,
|
||||
'collection_id' => $collection_id
|
||||
]
|
||||
];
|
||||
$this->CollectionElement->create();
|
||||
$error = '';
|
||||
try {
|
||||
$result = $this->CollectionElement->save($dataToSave);
|
||||
} catch (PDOException $e) {
|
||||
if ($e->errorInfo[0] == 23000) {
|
||||
$error = __(' Element already in Collection.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$message = __('Element added to the Collection.');
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('CollectionElements', 'addElementToCollection', false, $this->response->type(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(Router::url($this->referer(), true));
|
||||
}
|
||||
} else {
|
||||
$message = __('Element could not be added to the Collection.%s', $error);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('CollectionElements', 'addElementToCollection', false, $message, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect(Router::url($this->referer(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class CollectionsController extends AppController
|
||||
{
|
||||
|
||||
public $components = ['Session', 'RequestHandler'];
|
||||
|
||||
public $paginate = [
|
||||
'limit' => 60,
|
||||
'order' => []
|
||||
];
|
||||
|
||||
public $uses = [
|
||||
];
|
||||
|
||||
private $valid_types = [
|
||||
'campaign',
|
||||
'intrusion_set',
|
||||
'named_threat',
|
||||
'other',
|
||||
'research'
|
||||
];
|
||||
|
||||
public function add()
|
||||
{
|
||||
$this->Collection->current_user = $this->Auth->user();
|
||||
$params = [];
|
||||
if ($this->request->is('post')) {
|
||||
$data = $this->request->data;
|
||||
$params = [
|
||||
'afterSave' => function (array $collection) use ($data) {
|
||||
$this->Collection->CollectionElement->captureElements($collection);
|
||||
return $collection;
|
||||
}
|
||||
];
|
||||
}
|
||||
$this->CRUD->add($params);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'add'));
|
||||
$this->loadModel('Event');
|
||||
$dropdownData = [
|
||||
'types' => array_combine($this->valid_types, $this->valid_types),
|
||||
'distributionLevels' => $this->Event->distributionLevels,
|
||||
'sgs' => $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1)
|
||||
];
|
||||
$this->set('initialDistribution', Configure::read('MISP.default_event_distribution'));
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$this->Collection->current_user = $this->Auth->user();
|
||||
if (!$this->Collection->mayModify($this->Auth->user('id'), $id)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
|
||||
}
|
||||
$params = [];
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$oldCollection = $this->Collection->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Collection.id' => intval($id)]
|
||||
]);
|
||||
if (empty($oldCollection)) {
|
||||
throw new NotFoundException(__('Invalid collection.'));
|
||||
}
|
||||
if (empty($this->request->data['Collection'])) {
|
||||
$this->request->data = ['Collection' => $this->request->data];
|
||||
}
|
||||
$data = $this->request->data;
|
||||
if (
|
||||
isset($data['Collection']['modified']) &&
|
||||
$data['Collection']['modified'] <= $oldCollection['Collection']['modified']
|
||||
) {
|
||||
throw new ForbiddenException(__('Collection received older or same as local version.'));
|
||||
}
|
||||
$params = [
|
||||
'afterSave' => function (array &$collection) use ($data) {
|
||||
$collection = $this->Collection->CollectionElement->captureElements($collection);
|
||||
return $collection;
|
||||
}
|
||||
];
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->CRUD->edit($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'edit'));
|
||||
$this->loadModel('Event');
|
||||
$dropdownData = [
|
||||
'types' => $this->valid_types,
|
||||
'distributionLevels' => $this->Event->distributionLevels,
|
||||
'sgs' => $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1)
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
if (!$this->Collection->mayModify($this->Auth->user('id'), $id)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
|
||||
}
|
||||
$this->CRUD->delete($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$this->set('mayModify', $this->Collection->mayModify($this->Auth->user('id'), $id));
|
||||
if (!$this->Collection->mayView($this->Auth->user('id'), $id)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'view'));
|
||||
$params = [
|
||||
'contain' => [
|
||||
'Orgc',
|
||||
'Org',
|
||||
'User',
|
||||
'CollectionElement'
|
||||
],
|
||||
'afterFind' => function (array $collection){
|
||||
return $this->Collection->rearrangeCollection($collection);
|
||||
}
|
||||
];
|
||||
$this->CRUD->view($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->loadModel('Event');
|
||||
$this->set('distributionLevels', $this->Event->distributionLevels);
|
||||
$this->render('view');
|
||||
}
|
||||
|
||||
public function index($filter = null)
|
||||
{
|
||||
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'index'));
|
||||
$params = [
|
||||
'filters' => ['Collection.uuid', 'Collection.type', 'Collection.name'],
|
||||
'quickFilters' => ['Collection.name'],
|
||||
'contain' => ['Orgc'],
|
||||
'afterFind' => function($collections) {
|
||||
foreach ($collections as $k => $collection) {
|
||||
$collections[$k]['Collection']['element_count'] = $this->Collection->CollectionElement->find('count', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['CollectionElement.collection_id' => $collection['Collection']['id']]
|
||||
]);
|
||||
}
|
||||
return $collections;
|
||||
}
|
||||
];
|
||||
if ($filter === 'my_collections') {
|
||||
$params['conditions']['Collection.user_id'] = $this->Auth->user('id');
|
||||
}
|
||||
if ($filter === 'org_collections') {
|
||||
$params['conditions']['Collection.orgc_id'] = $this->Auth->user('org_id');
|
||||
}
|
||||
$this->loadModel('Event');
|
||||
$this->set('distributionLevels', $this->Event->distributionLevels);
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,25 @@ class ACLComponent extends Component
|
|||
'queryACL' => array(),
|
||||
'restSearch' => array('*'),
|
||||
),
|
||||
'analystData' => [
|
||||
'add' => ['AND' => ['perm_add', 'perm_analyst_data']],
|
||||
'delete' => ['AND' => ['perm_add', 'perm_analyst_data']],
|
||||
'edit' => ['AND' => ['perm_add', 'perm_analyst_data']],
|
||||
'filterAnalystDataForPush' => ['perm_sync'],
|
||||
'getChildren' => ['*'],
|
||||
'getRelatedElement' => ['*'],
|
||||
'index' => ['*'],
|
||||
'indexMinimal' => ['*'],
|
||||
'pushAnalystData' => ['perm_sync'],
|
||||
'view' => ['*'],
|
||||
],
|
||||
'analystDataBlocklists' => array(
|
||||
'add' => array(),
|
||||
'delete' => array(),
|
||||
'edit' => array(),
|
||||
'index' => array(),
|
||||
'massDelete' => array(),
|
||||
),
|
||||
'api' => [
|
||||
'rest' => ['perm_auth'],
|
||||
'viewDeprecatedFunctionUse' => [],
|
||||
|
@ -89,6 +108,19 @@ class ACLComponent extends Component
|
|||
'pull_sgs' => [],
|
||||
'view' => []
|
||||
],
|
||||
'collections' => [
|
||||
'add' => ['perm_modify'],
|
||||
'delete' => ['perm_modify'],
|
||||
'edit' => ['perm_modify'],
|
||||
'index' => ['*'],
|
||||
'view' => ['*']
|
||||
],
|
||||
'collectionElements' => [
|
||||
'add' => ['perm_modify'],
|
||||
'addElementToCollection' => ['perm_modify'],
|
||||
'delete' => ['perm_modify'],
|
||||
'index' => ['*']
|
||||
],
|
||||
'correlationExclusions' => [
|
||||
'add' => [],
|
||||
'edit' => [],
|
||||
|
@ -1079,6 +1111,27 @@ class ACLComponent extends Component
|
|||
return $cluster['GalaxyCluster']['orgc_id'] == $user['org_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user can modify given analyst data
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $analystData
|
||||
* @return bool
|
||||
*/
|
||||
public function canEditAnalystData(array $user, array $analystData, $modelType): bool
|
||||
{
|
||||
if (!isset($analystData[$modelType])) {
|
||||
throw new InvalidArgumentException('Passed object does not contain a(n) ' . $modelType);
|
||||
}
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($analystData[$modelType]['orgc_uuid'] == $user['Organisation']['uuid']) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user can publish given galaxy cluster
|
||||
*
|
||||
|
|
|
@ -25,6 +25,7 @@ class CRUDComponent extends Component
|
|||
}
|
||||
$options['filters'][] = 'quickFilter';
|
||||
}
|
||||
$this->Controller->{$this->Controller->modelClass}->includeAnalystData = true;
|
||||
$params = $this->Controller->IndexFilter->harvestParameters(empty($options['filters']) ? [] : $options['filters']);
|
||||
$query = [];
|
||||
$query = $this->setFilters($params, $query);
|
||||
|
@ -39,6 +40,7 @@ class CRUDComponent extends Component
|
|||
if (!empty($this->Controller->paginate['fields'])) {
|
||||
$query['fields'] = $this->Controller->paginate['fields'];
|
||||
}
|
||||
$query['includeAnalystData'] = true;
|
||||
$data = $this->Controller->{$this->Controller->modelClass}->find('all', $query);
|
||||
if (isset($options['afterFind'])) {
|
||||
if (is_callable($options['afterFind'])) {
|
||||
|
@ -49,6 +51,7 @@ class CRUDComponent extends Component
|
|||
}
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
|
||||
} else {
|
||||
$query['includeAnalystData'] = true;
|
||||
$this->Controller->paginate = $query;
|
||||
$data = $this->Controller->paginate();
|
||||
if (isset($options['afterFind'])) {
|
||||
|
@ -93,7 +96,7 @@ class CRUDComponent extends Component
|
|||
$savedData = $model->save($data);
|
||||
if ($savedData) {
|
||||
if (isset($params['afterSave'])) {
|
||||
$params['afterSave']($data);
|
||||
$params['afterSave']($savedData);
|
||||
}
|
||||
$data = $model->find('first', [
|
||||
'recursive' => -1,
|
||||
|
@ -200,7 +203,7 @@ class CRUDComponent extends Component
|
|||
if (isset($params['beforeSave'])) {
|
||||
$data = $params['beforeSave']($data);
|
||||
}
|
||||
if ($model->save($data)) {
|
||||
if ($data = $model->save($data)) {
|
||||
if (isset($params['afterSave'])) {
|
||||
$params['afterSave']($data);
|
||||
}
|
||||
|
@ -231,6 +234,8 @@ class CRUDComponent extends Component
|
|||
if (empty($id)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
$this->Controller->{$modelName}->includeAnalystData = true;
|
||||
$this->Controller->{$modelName}->includeAnalystDataRecursive = true;
|
||||
$query = [
|
||||
'recursive' => -1,
|
||||
'conditions' => [$modelName . '.id' => $id],
|
||||
|
@ -297,6 +302,9 @@ class CRUDComponent extends Component
|
|||
$result = $this->Controller->{$modelName}->delete($id);
|
||||
}
|
||||
if ($result) {
|
||||
if (isset($params['afterDelete']) && is_callable($params['afterDelete'])) {
|
||||
$params['afterDelete']($data);
|
||||
}
|
||||
$message = __('%s deleted.', $modelName);
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveSuccessResponse($modelName, 'delete', $id, 'json', $message);
|
||||
|
@ -336,7 +344,9 @@ class CRUDComponent extends Component
|
|||
if ($filter === 'quickFilter') {
|
||||
continue;
|
||||
}
|
||||
if (strlen(trim($filterValue, '%')) === strlen($filterValue)) {
|
||||
if (is_array($filterValue)) {
|
||||
$query['conditions']['AND'][] = [$filter => $filterValue];
|
||||
} else if (strlen(trim($filterValue, '%')) === strlen($filterValue)) {
|
||||
$query['conditions']['AND'][] = [$filter => $filterValue];
|
||||
} else {
|
||||
$query['conditions']['AND'][] = [$filter . ' LIKE' => $filterValue];
|
||||
|
|
|
@ -213,6 +213,7 @@ class RestResponseComponent extends Component
|
|||
'perm_tag_editor',
|
||||
'default_role',
|
||||
'perm_sighting',
|
||||
'perm_analyst_data',
|
||||
'permission'
|
||||
)
|
||||
),
|
||||
|
@ -234,6 +235,7 @@ class RestResponseComponent extends Component
|
|||
'perm_tag_editor',
|
||||
'default_role',
|
||||
'perm_sighting',
|
||||
'perm_analyst_data',
|
||||
'permission'
|
||||
)
|
||||
)
|
||||
|
@ -1557,6 +1559,11 @@ class RestResponseComponent extends Component
|
|||
'type' => 'integer',
|
||||
'values' => array(1 => 'True', 0 => 'False')
|
||||
),
|
||||
'perm_analyst_data' => array(
|
||||
'input' => 'radio',
|
||||
'type' => 'integer',
|
||||
'values' => array(1 => 'True', 0 => 'False')
|
||||
),
|
||||
'permission' => array(
|
||||
'input' => 'select',
|
||||
'type' => 'string',
|
||||
|
|
|
@ -124,6 +124,7 @@ class RestSearchComponent extends Component
|
|||
'extended',
|
||||
'extensionList',
|
||||
'excludeGalaxy',
|
||||
'includeAnalystData',
|
||||
'includeRelatedTags',
|
||||
'includeDecayScore',
|
||||
'includeScoresOnEvent',
|
||||
|
|
|
@ -61,6 +61,8 @@ class EventReportsController extends AppController
|
|||
|
||||
public function view($reportId, $ajax=false)
|
||||
{
|
||||
$this->EventReport->includeAnalystData = true;
|
||||
$this->EventReport->includeAnalystDataRecursive = true;
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($report, $this->response->type());
|
||||
|
@ -175,6 +177,7 @@ class EventReportsController extends AppController
|
|||
$filters = $this->IndexFilter->harvestParameters(['event_id', 'value', 'context', 'index_for_event', 'extended_event']);
|
||||
$filters['embedded_view'] = $this->request->is('ajax');
|
||||
$compiledConditions = $this->__generateIndexConditions($filters);
|
||||
$this->EventReport->includeAnalystData = true;
|
||||
if ($this->_isRest()) {
|
||||
$reports = $this->EventReport->find('all', [
|
||||
'recursive' => -1,
|
||||
|
@ -515,6 +518,7 @@ class EventReportsController extends AppController
|
|||
{
|
||||
$distributionLevels = $this->EventReport->Event->Attribute->distributionLevels;
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
$this->set('shortDist', $this->EventReport->Event->Attribute->shortDist);
|
||||
$this->set('initialDistribution', $this->EventReport->Event->Attribute->defaultDistribution());
|
||||
}
|
||||
|
||||
|
|
|
@ -755,7 +755,8 @@ class EventsController extends AppController
|
|||
if ($nothing) {
|
||||
$this->paginate['conditions']['AND'][] = ['Event.id' => -1]; // do not fetch any event
|
||||
}
|
||||
|
||||
$this->Event->includeAnalystData = true;
|
||||
$this->paginate['includeAnalystData'] = true;
|
||||
$events = $this->paginate();
|
||||
|
||||
if (count($events) === 1 && isset($this->passedArgs['searchall'])) {
|
||||
|
@ -811,6 +812,7 @@ class EventsController extends AppController
|
|||
$rules = [
|
||||
'contain' => ['EventTag'],
|
||||
'fields' => array_keys($fieldNames),
|
||||
'includeAnalystData' => isset($passedArgs['includeAnalystData']) ? $passedArgs['includeAnalystData'] : true,
|
||||
];
|
||||
}
|
||||
if (isset($passedArgs['sort']) && isset($fieldNames[$passedArgs['sort']])) {
|
||||
|
@ -1227,6 +1229,9 @@ class EventsController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
$this->Event->Attribute->includeAnalystData = true;
|
||||
$this->Event->Attribute->includeAnalystDataRecursive = true;
|
||||
|
||||
if (isset($filters['focus'])) {
|
||||
$this->set('focus', $filters['focus']);
|
||||
}
|
||||
|
@ -1691,7 +1696,7 @@ class EventsController extends AppController
|
|||
}
|
||||
|
||||
$namedParams = $this->request->params['named'];
|
||||
|
||||
$conditions['includeAnalystData'] = true;
|
||||
if ($this->_isRest()) {
|
||||
$conditions['includeAttachments'] = isset($namedParams['includeAttachments']) ? $namedParams['includeAttachments'] : true;
|
||||
} else {
|
||||
|
@ -1786,7 +1791,6 @@ class EventsController extends AppController
|
|||
} else {
|
||||
$user = $this->Auth->user();
|
||||
}
|
||||
|
||||
$results = $this->Event->fetchEvent($user, $conditions);
|
||||
if (empty($results)) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
|
@ -2404,14 +2408,14 @@ class EventsController extends AppController
|
|||
}
|
||||
if (isset($this->params['named']['distribution'])) {
|
||||
$distribution = intval($this->params['named']['distribution']);
|
||||
if (array_key_exists($distribution, $distributionLevels)) {
|
||||
$initialDistribution = $distribution;
|
||||
} else {
|
||||
if (!array_key_exists($distribution, $distributionLevels)) {
|
||||
throw new MethodNotAllowedException(__('Wrong distribution level'));
|
||||
}
|
||||
} else {
|
||||
$distribution = $initialDistribution;
|
||||
}
|
||||
$sharingGroupId = null;
|
||||
if ($initialDistribution == 4) {
|
||||
if ($distribution == 4) {
|
||||
if (!isset($this->params['named']['sharing_group_id'])) {
|
||||
throw new MethodNotAllowedException(__('The sharing group id is needed when the distribution is set to 4 ("Sharing group").'));
|
||||
}
|
||||
|
@ -2420,8 +2424,25 @@ class EventsController extends AppController
|
|||
throw new MethodNotAllowedException(__('Please select a valid sharing group id.'));
|
||||
}
|
||||
}
|
||||
$clusterDistribution = $initialDistribution;
|
||||
$clusterSharingGroupId = null;
|
||||
if (isset($this->params['named']['galaxies_as_tags'])) {
|
||||
$galaxies_as_tags = $this->params['named']['galaxies_as_tags'];
|
||||
if (isset($this->params['name']['cluster_distribution'])) {
|
||||
$clusterDistribution = intval($this->params['named']['cluster_distribution']);
|
||||
if (!array_key_exists($clusterDistribution, $distributionLevels)) {
|
||||
throw new MethodNotAllowedException(__('Wrong cluster distribution level'));
|
||||
}
|
||||
if ($clusterDistribution == 4) {
|
||||
if (!isset($this->params['named']['cluster_sharing_group_id'])) {
|
||||
throw new MethodNotAllowedException(__('The cluster sharing group id is needed when the cluster distribution is set to 4 ("Sharing group").'));
|
||||
}
|
||||
$clusterSharingGroupId = intval($this->params['named']['cluster_sharing_group_id']);
|
||||
if (!array_key_exists($clusterSharingGroupId, $sgs)) {
|
||||
throw new MethodNotAllowedException(__('Please select a valid cluster sharing group id.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($this->params['named']['debugging'])) {
|
||||
$debug = $this->params['named']['debugging'];
|
||||
|
@ -2433,9 +2454,11 @@ class EventsController extends AppController
|
|||
$stix_version,
|
||||
'uploaded_stix_file.' . ($stix_version == '1' ? 'xml' : 'json'),
|
||||
$publish,
|
||||
$initialDistribution,
|
||||
$distribution,
|
||||
$sharingGroupId,
|
||||
$galaxies_as_tags,
|
||||
$clusterDistribution,
|
||||
$clusterSharingGroupId,
|
||||
$debug
|
||||
);
|
||||
if (is_numeric($result)) {
|
||||
|
@ -2467,6 +2490,8 @@ class EventsController extends AppController
|
|||
$this->data['Event']['distribution'],
|
||||
$this->data['Event']['sharing_group_id'] ?? null,
|
||||
$this->data['Event']['galaxies_handling'],
|
||||
$this->data['Event']['cluster_distribution'],
|
||||
$this->data['Event']['cluster_sharing_group_id'] ?? null,
|
||||
$debug
|
||||
);
|
||||
if (is_numeric($result)) {
|
||||
|
@ -2692,7 +2717,7 @@ class EventsController extends AppController
|
|||
$this->request->data = $this->request->data['Event'];
|
||||
}
|
||||
$eventToSave = $event;
|
||||
$capturedObjects = ['Attribute', 'Object', 'Tag', 'Galaxy', 'EventReport'];
|
||||
$capturedObjects = ['Attribute', 'Object', 'Tag', 'Galaxy', 'EventReport', 'Note', 'Opinion', 'Relationship',];
|
||||
foreach ($capturedObjects as $objectType) {
|
||||
if (!empty($this->request->data[$objectType])) {
|
||||
if (!empty($regenerateUUIDs)) {
|
||||
|
@ -4373,12 +4398,12 @@ class EventsController extends AppController
|
|||
$id = $event['Event']['id'];
|
||||
$exports = array(
|
||||
'json' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/json/eventid:' . $id . '.json',
|
||||
'url' => $this->baseurl . '/events/restSearch/json/includeAnalystData:1/eventid:' . $id . '.json',
|
||||
'text' => __('MISP JSON (metadata + all attributes)'),
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => __('Encode Attachments'),
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/eventid:' . $id . '.json',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/includeAnalystData:1/eventid:' . $id . '.json',
|
||||
'checkbox_default' => true,
|
||||
),
|
||||
'xml' => array(
|
||||
|
|
|
@ -173,6 +173,8 @@ class GalaxyClustersController extends AppController
|
|||
*/
|
||||
public function view($id)
|
||||
{
|
||||
$this->GalaxyCluster->includeAnalystData = true;
|
||||
$this->GalaxyCluster->includeAnalystDataRecursive = true;
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors=true, $full=true);
|
||||
$tag = $this->GalaxyCluster->Tag->find('first', array(
|
||||
'conditions' => array(
|
||||
|
@ -208,6 +210,7 @@ class GalaxyClustersController extends AppController
|
|||
$this->loadModel('Attribute');
|
||||
$distributionLevels = $this->Attribute->distributionLevels;
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
$this->set('shortDist', $this->Attribute->shortDist);
|
||||
if (!$cluster['GalaxyCluster']['default'] && !$cluster['GalaxyCluster']['published'] && $cluster['GalaxyCluster']['orgc_id'] == $this->Auth->user()['org_id']) {
|
||||
$this->Flash->warning(__('This cluster is not published. Users will not be able to use it'));
|
||||
}
|
||||
|
|
|
@ -481,8 +481,8 @@ class OrganisationsController extends AppController
|
|||
$extension = pathinfo($logo['name'], PATHINFO_EXTENSION);
|
||||
$filename = $orgId . '.' . ($extension === 'svg' ? 'svg' : 'png');
|
||||
|
||||
if ($logo['size'] > 250*1024) {
|
||||
$this->Flash->error(__('This organisation logo is too large, maximum file size allowed is 250kB.'));
|
||||
if ($logo['size'] > 250 * 1024) {
|
||||
$this->Flash->error(__('This organisation logo is too large, maximum file size allowed is 250 kB.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -492,10 +492,12 @@ class OrganisationsController extends AppController
|
|||
}
|
||||
|
||||
$imgMime = mime_content_type($logo['tmp_name']);
|
||||
if ($extension === 'png' && !exif_imagetype($logo['tmp_name'])) {
|
||||
if ($extension === 'png' && (function_exists('exif_imagetype') && !exif_imagetype($logo['tmp_name']))) {
|
||||
$this->Flash->error(__('This is not a valid PNG image.'));
|
||||
return false;
|
||||
} else if ($extension === 'svg' && !($imgMime === 'image/svg+xml' || $imgMime === 'image/svg')) {
|
||||
}
|
||||
|
||||
if ($extension === 'svg' && !($imgMime === 'image/svg+xml' || $imgMime === 'image/svg')) {
|
||||
$this->Flash->error(__('This is not a valid SVG image.'));
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -529,7 +529,7 @@ class ServersController extends AppController
|
|||
|
||||
if (!$fail) {
|
||||
// say what fields are to be updated
|
||||
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
|
||||
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'push_analyst_data', 'pull_analyst_data', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
|
||||
$this->request->data['Server']['id'] = $id;
|
||||
if (isset($this->request->data['Server']['authkey']) && "" != $this->request->data['Server']['authkey']) {
|
||||
$fieldList[] = 'authkey';
|
||||
|
@ -776,7 +776,7 @@ class ServersController extends AppController
|
|||
if (!Configure::read('MISP.background_jobs')) {
|
||||
$result = $this->Server->pull($this->Auth->user(), $technique, $s);
|
||||
if (is_array($result)) {
|
||||
$success = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]);
|
||||
$success = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled, %s analyst data pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4], $result[5]);
|
||||
} else {
|
||||
$error = $result;
|
||||
}
|
||||
|
@ -784,6 +784,7 @@ class ServersController extends AppController
|
|||
$this->set('fails', $result[1]);
|
||||
$this->set('pulledProposals', $result[2]);
|
||||
$this->set('pulledSightings', $result[3]);
|
||||
$this->set('pulledAnalystData', $result[5]);
|
||||
} else {
|
||||
$this->loadModel('Job');
|
||||
$jobId = $this->Job->createJob(
|
||||
|
@ -1889,6 +1890,7 @@ class ServersController extends AppController
|
|||
'perm_sync' => (bool) $user['Role']['perm_sync'],
|
||||
'perm_sighting' => (bool) $user['Role']['perm_sighting'],
|
||||
'perm_galaxy_editor' => (bool) $user['Role']['perm_galaxy_editor'],
|
||||
'perm_analyst_data' => (bool) $user['Role']['perm_analyst_data'],
|
||||
'uuid' => $user['Role']['perm_sync'] ? Configure::read('MISP.uuid') : '-',
|
||||
'request_encoding' => $this->CompressedRequestHandler->supportedEncodings(),
|
||||
'filter_sightings' => true, // check if Sightings::filterSightingUuidsForPush method is supported
|
||||
|
|
|
@ -37,6 +37,10 @@ class MispAdminSyncTestWidget
|
|||
$colour = 'orange';
|
||||
$message .= ' ' . __('No sighting access.');
|
||||
}
|
||||
if (empty($result['info']['perm_analyst_data'])) {
|
||||
$colour = 'orange';
|
||||
$message .= ' ' . __('No analyst data sync access.');
|
||||
}
|
||||
} else {
|
||||
$colour = 'red';
|
||||
$message = $syncTestErrorCodes[$result['status']];
|
||||
|
|
|
@ -21,7 +21,7 @@ class JSONConverterTool
|
|||
|
||||
public static function convertObject($object, $isSiteAdmin = false, $raw = false)
|
||||
{
|
||||
$toRearrange = array('SharingGroup', 'Attribute', 'ShadowAttribute', 'Event', 'CryptographicKey');
|
||||
$toRearrange = array('SharingGroup', 'Attribute', 'ShadowAttribute', 'Event', 'CryptographicKey', 'Note', 'Opinion', 'Relationship');
|
||||
foreach ($toRearrange as $element) {
|
||||
if (isset($object[$element])) {
|
||||
$object['Object'][$element] = $object[$element];
|
||||
|
@ -40,7 +40,7 @@ class JSONConverterTool
|
|||
|
||||
public static function convert($event, $isSiteAdmin=false, $raw = false)
|
||||
{
|
||||
$toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object', 'EventReport', 'CryptographicKey');
|
||||
$toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object', 'EventReport', 'CryptographicKey', 'Note', 'Opinion', 'Relationship');
|
||||
foreach ($toRearrange as $object) {
|
||||
if (isset($event[$object])) {
|
||||
$event['Event'][$object] = $event[$object];
|
||||
|
@ -69,6 +69,16 @@ class JSONConverterTool
|
|||
}
|
||||
}
|
||||
|
||||
if (isset($event['Event']['Note'])) {
|
||||
$event['Event']['Note'] = self::__cleanAnalystData($event['Event']['Note']);
|
||||
}
|
||||
if (isset($event['Event']['Opinion'])) {
|
||||
$event['Event']['Opinion'] = self::__cleanAnalystData($event['Event']['Opinion']);
|
||||
}
|
||||
if (isset($event['Event']['Relationship'])) {
|
||||
$event['Event']['Relationship'] = self::__cleanAnalystData($event['Event']['Relationship']);
|
||||
}
|
||||
|
||||
// cleanup the array from things we do not want to expose
|
||||
$tempSightings = array();
|
||||
if (!empty($event['Sighting'])) {
|
||||
|
@ -209,6 +219,17 @@ class JSONConverterTool
|
|||
return $objects;
|
||||
}
|
||||
|
||||
private function __cleanAnalystData($data)
|
||||
{
|
||||
foreach ($data as $k => $entry) {
|
||||
if (empty($entry['SharingGroup'])) {
|
||||
unset($data[$k]['SharingGroup']);
|
||||
}
|
||||
}
|
||||
$data = array_values($data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function arrayPrinter($array, $root = true)
|
||||
{
|
||||
if (is_array($array)) {
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
class LanguageRFC5646Tool
|
||||
{
|
||||
const RFC5646_LANGUAGE = [
|
||||
'af' => 'Afrikaans',
|
||||
'af-ZA' => 'Afrikaans (South Africa)',
|
||||
'ar' => 'Arabic',
|
||||
'ar-AE' => 'Arabic (U.A.E.)',
|
||||
'ar-BH' => 'Arabic (Bahrain)',
|
||||
'ar-DZ' => 'Arabic (Algeria)',
|
||||
'ar-EG' => 'Arabic (Egypt)',
|
||||
'ar-IQ' => 'Arabic (Iraq)',
|
||||
'ar-JO' => 'Arabic (Jordan)',
|
||||
'ar-KW' => 'Arabic (Kuwait)',
|
||||
'ar-LB' => 'Arabic (Lebanon)',
|
||||
'ar-LY' => 'Arabic (Libya)',
|
||||
'ar-MA' => 'Arabic (Morocco)',
|
||||
'ar-OM' => 'Arabic (Oman)',
|
||||
'ar-QA' => 'Arabic (Qatar)',
|
||||
'ar-SA' => 'Arabic (Saudi Arabia)',
|
||||
'ar-SY' => 'Arabic (Syria)',
|
||||
'ar-TN' => 'Arabic (Tunisia)',
|
||||
'ar-YE' => 'Arabic (Yemen)',
|
||||
'az' => 'Azeri (Latin)',
|
||||
'az-AZ' => 'Azeri (Latin) (Azerbaijan)',
|
||||
'az-Cyrl-AZ' => 'Azeri (Cyrillic) (Azerbaijan)',
|
||||
'be' => 'Belarusian',
|
||||
'be-BY' => 'Belarusian (Belarus)',
|
||||
'bg' => 'Bulgarian',
|
||||
'bg-BG' => 'Bulgarian (Bulgaria)',
|
||||
'bs-BA' => 'Bosnian (Bosnia and Herzegovina)',
|
||||
'ca' => 'Catalan',
|
||||
'ca-ES' => 'Catalan (Spain)',
|
||||
'cs' => 'Czech',
|
||||
'cs-CZ' => 'Czech (Czech Republic)',
|
||||
'cy' => 'Welsh',
|
||||
'cy-GB' => 'Welsh (United Kingdom)',
|
||||
'da' => 'Danish',
|
||||
'da-DK' => 'Danish (Denmark)',
|
||||
'de' => 'German',
|
||||
'de-AT' => 'German (Austria)',
|
||||
'de-CH' => 'German (Switzerland)',
|
||||
'de-DE' => 'German (Germany)',
|
||||
'de-LI' => 'German (Liechtenstein)',
|
||||
'de-LU' => 'German (Luxembourg)',
|
||||
'dv' => 'Divehi',
|
||||
'dv-MV' => 'Divehi (Maldives)',
|
||||
'el' => 'Greek',
|
||||
'el-GR' => 'Greek (Greece)',
|
||||
'en' => 'English',
|
||||
'en-AU' => 'English (Australia)',
|
||||
'en-BZ' => 'English (Belize)',
|
||||
'en-CA' => 'English (Canada)',
|
||||
'en-CB' => 'English (Caribbean)',
|
||||
'en-GB' => 'English (United Kingdom)',
|
||||
'en-IE' => 'English (Ireland)',
|
||||
'en-JM' => 'English (Jamaica)',
|
||||
'en-NZ' => 'English (New Zealand)',
|
||||
'en-PH' => 'English (Republic of the Philippines)',
|
||||
'en-TT' => 'English (Trinidad and Tobago)',
|
||||
'en-US' => 'English (United States)',
|
||||
'en-ZA' => 'English (South Africa)',
|
||||
'en-ZW' => 'English (Zimbabwe)',
|
||||
'eo' => 'Esperanto',
|
||||
'es' => 'Spanish',
|
||||
'es-AR' => 'Spanish (Argentina)',
|
||||
'es-BO' => 'Spanish (Bolivia)',
|
||||
'es-CL' => 'Spanish (Chile)',
|
||||
'es-CO' => 'Spanish (Colombia)',
|
||||
'es-CR' => 'Spanish (Costa Rica)',
|
||||
'es-DO' => 'Spanish (Dominican Republic)',
|
||||
'es-EC' => 'Spanish (Ecuador)',
|
||||
'es-ES' => 'Spanish (Spain)',
|
||||
'es-GT' => 'Spanish (Guatemala)',
|
||||
'es-HN' => 'Spanish (Honduras)',
|
||||
'es-MX' => 'Spanish (Mexico)',
|
||||
'es-NI' => 'Spanish (Nicaragua)',
|
||||
'es-PA' => 'Spanish (Panama)',
|
||||
'es-PE' => 'Spanish (Peru)',
|
||||
'es-PR' => 'Spanish (Puerto Rico)',
|
||||
'es-PY' => 'Spanish (Paraguay)',
|
||||
'es-SV' => 'Spanish (El Salvador)',
|
||||
'es-UY' => 'Spanish (Uruguay)',
|
||||
'es-VE' => 'Spanish (Venezuela)',
|
||||
'et' => 'Estonian',
|
||||
'et-EE' => 'Estonian (Estonia)',
|
||||
'eu' => 'Basque',
|
||||
'eu-ES' => 'Basque (Spain)',
|
||||
'fa' => 'Farsi',
|
||||
'fa-IR' => 'Farsi (Iran)',
|
||||
'fi' => 'Finnish',
|
||||
'fi-FI' => 'Finnish (Finland)',
|
||||
'fo' => 'Faroese',
|
||||
'fo-FO' => 'Faroese (Faroe Islands)',
|
||||
'fr' => 'French',
|
||||
'fr-BE' => 'French (Belgium)',
|
||||
'fr-CA' => 'French (Canada)',
|
||||
'fr-CH' => 'French (Switzerland)',
|
||||
'fr-FR' => 'French (France)',
|
||||
'fr-LU' => 'French (Luxembourg)',
|
||||
'fr-MC' => 'French (Principality of Monaco)',
|
||||
'gl' => 'Galician',
|
||||
'gl-ES' => 'Galician (Spain)',
|
||||
'gu' => 'Gujarati',
|
||||
'gu-IN' => 'Gujarati (India)',
|
||||
'he' => 'Hebrew',
|
||||
'he-IL' => 'Hebrew (Israel)',
|
||||
'hi' => 'Hindi',
|
||||
'hi-IN' => 'Hindi (India)',
|
||||
'hr' => 'Croatian',
|
||||
'hr-BA' => 'Croatian (Bosnia and Herzegovina)',
|
||||
'hr-HR' => 'Croatian (Croatia)',
|
||||
'hu' => 'Hungarian',
|
||||
'hu-HU' => 'Hungarian (Hungary)',
|
||||
'hy' => 'Armenian',
|
||||
'hy-AM' => 'Armenian (Armenia)',
|
||||
'id' => 'Indonesian',
|
||||
'id-ID' => 'Indonesian (Indonesia)',
|
||||
'is' => 'Icelandic',
|
||||
'is-IS' => 'Icelandic (Iceland)',
|
||||
'it' => 'Italian',
|
||||
'it-CH' => 'Italian (Switzerland)',
|
||||
'it-IT' => 'Italian (Italy)',
|
||||
'ja' => 'Japanese',
|
||||
'ja-JP' => 'Japanese (Japan)',
|
||||
'ka' => 'Georgian',
|
||||
'ka-GE' => 'Georgian (Georgia)',
|
||||
'kk' => 'Kazakh',
|
||||
'kk-KZ' => 'Kazakh (Kazakhstan)',
|
||||
'kn' => 'Kannada',
|
||||
'kn-IN' => 'Kannada (India)',
|
||||
'ko' => 'Korean',
|
||||
'ko-KR' => 'Korean (Korea)',
|
||||
'kok' => 'Konkani',
|
||||
'kok-IN' => 'Konkani (India)',
|
||||
'ky' => 'Kyrgyz',
|
||||
'ky-KG' => 'Kyrgyz (Kyrgyzstan)',
|
||||
'lt' => 'Lithuanian',
|
||||
'lt-LT' => 'Lithuanian (Lithuania)',
|
||||
'lv' => 'Latvian',
|
||||
'lv-LV' => 'Latvian (Latvia)',
|
||||
'mi' => 'Maori',
|
||||
'mi-NZ' => 'Maori (New Zealand)',
|
||||
'mk' => 'FYRO Macedonian',
|
||||
'mk-MK' => 'FYRO Macedonian (Former Yugoslav Republic of Macedonia)',
|
||||
'mn' => 'Mongolian',
|
||||
'mn-MN' => 'Mongolian (Mongolia)',
|
||||
'mr' => 'Marathi',
|
||||
'mr-IN' => 'Marathi (India)',
|
||||
'ms' => 'Malay',
|
||||
'ms-BN' => 'Malay (Brunei Darussalam)',
|
||||
'ms-MY' => 'Malay (Malaysia)',
|
||||
'mt' => 'Maltese',
|
||||
'mt-MT' => 'Maltese (Malta)',
|
||||
'nb' => 'Norwegian (Bokm?l)',
|
||||
'nb-NO' => 'Norwegian (Bokm?l) (Norway)',
|
||||
'nl' => 'Dutch',
|
||||
'nl-BE' => 'Dutch (Belgium)',
|
||||
'nl-NL' => 'Dutch (Netherlands)',
|
||||
'nn-NO' => 'Norwegian (Nynorsk) (Norway)',
|
||||
'ns' => 'Northern Sotho',
|
||||
'ns-ZA' => 'Northern Sotho (South Africa)',
|
||||
'pa' => 'Punjabi',
|
||||
'pa-IN' => 'Punjabi (India)',
|
||||
'pl' => 'Polish',
|
||||
'pl-PL' => 'Polish (Poland)',
|
||||
'ps' => 'Pashto',
|
||||
'ps-AR' => 'Pashto (Afghanistan)',
|
||||
'pt' => 'Portuguese',
|
||||
'pt-BR' => 'Portuguese (Brazil)',
|
||||
'pt-PT' => 'Portuguese (Portugal)',
|
||||
'qu' => 'Quechua',
|
||||
'qu-BO' => 'Quechua (Bolivia)',
|
||||
'qu-EC' => 'Quechua (Ecuador)',
|
||||
'qu-PE' => 'Quechua (Peru)',
|
||||
'ro' => 'Romanian',
|
||||
'ro-RO' => 'Romanian (Romania)',
|
||||
'ru' => 'Russian',
|
||||
'ru-RU' => 'Russian (Russia)',
|
||||
'sa' => 'Sanskrit',
|
||||
'sa-IN' => 'Sanskrit (India)',
|
||||
'se' => 'Sami',
|
||||
'se-FI' => 'Sami (Finland)',
|
||||
'se-NO' => 'Sami (Norway)',
|
||||
'se-SE' => 'Sami (Sweden)',
|
||||
'sk' => 'Slovak',
|
||||
'sk-SK' => 'Slovak (Slovakia)',
|
||||
'sl' => 'Slovenian',
|
||||
'sl-SI' => 'Slovenian (Slovenia)',
|
||||
'sq' => 'Albanian',
|
||||
'sq-AL' => 'Albanian (Albania)',
|
||||
'sr-BA' => 'Serbian (Latin) (Bosnia and Herzegovina)',
|
||||
'sr-Cyrl-BA' => 'Serbian (Cyrillic) (Bosnia and Herzegovina)',
|
||||
'sr-SP' => 'Serbian (Latin) (Serbia and Montenegro)',
|
||||
'sr-Cyrl-SP' => 'Serbian (Cyrillic) (Serbia and Montenegro)',
|
||||
'sv' => 'Swedish',
|
||||
'sv-FI' => 'Swedish (Finland)',
|
||||
'sv-SE' => 'Swedish (Sweden)',
|
||||
'sw' => 'Swahili',
|
||||
'sw-KE' => 'Swahili (Kenya)',
|
||||
'syr' => 'Syriac',
|
||||
'syr-SY' => 'Syriac (Syria)',
|
||||
'ta' => 'Tamil',
|
||||
'ta-IN' => 'Tamil (India)',
|
||||
'te' => 'Telugu',
|
||||
'te-IN' => 'Telugu (India)',
|
||||
'th' => 'Thai',
|
||||
'th-TH' => 'Thai (Thailand)',
|
||||
'tl' => 'Tagalog',
|
||||
'tl-PH' => 'Tagalog (Philippines)',
|
||||
'tn' => 'Tswana',
|
||||
'tn-ZA' => 'Tswana (South Africa)',
|
||||
'tr' => 'Turkish',
|
||||
'tr-TR' => 'Turkish (Turkey)',
|
||||
'tt' => 'Tatar',
|
||||
'tt-RU' => 'Tatar (Russia)',
|
||||
'ts' => 'Tsonga',
|
||||
'uk' => 'Ukrainian',
|
||||
'uk-UA' => 'Ukrainian (Ukraine)',
|
||||
'ur' => 'Urdu',
|
||||
'ur-PK' => 'Urdu (Islamic Republic of Pakistan)',
|
||||
'uz' => 'Uzbek (Latin)',
|
||||
'uz-UZ' => 'Uzbek (Latin) (Uzbekistan)',
|
||||
'uz-Cyrl-UZ' => 'Uzbek (Cyrillic) (Uzbekistan)',
|
||||
'vi' => 'Vietnamese',
|
||||
'vi-VN' => 'Vietnamese (Viet Nam)',
|
||||
'xh' => 'Xhosa',
|
||||
'xh-ZA' => 'Xhosa (South Africa)',
|
||||
'zh' => 'Chinese',
|
||||
'zh-CN' => 'Chinese (S)',
|
||||
'zh-HK' => 'Chinese (Hong Kong)',
|
||||
'zh-MO' => 'Chinese (Macau)',
|
||||
'zh-SG' => 'Chinese (Singapore)',
|
||||
'zh-TW' => 'Chinese (T)',
|
||||
'zu' => 'Zulu',
|
||||
'zu-ZA' => 'Zulu (South Africa)'
|
||||
];
|
||||
|
||||
public static function getLanguages()
|
||||
{
|
||||
return self::RFC5646_LANGUAGE;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ class ServerSyncTool
|
|||
FEATURE_EDIT_OF_GALAXY_CLUSTER = 'edit_of_galaxy_cluster',
|
||||
PERM_SYNC = 'perm_sync',
|
||||
PERM_GALAXY_EDITOR = 'perm_galaxy_editor',
|
||||
PERM_ANALYST_DATA = 'perm_analyst_data',
|
||||
FEATURE_SIGHTING_REST_SEARCH = 'sighting_rest';
|
||||
|
||||
/** @var array */
|
||||
|
@ -215,6 +216,71 @@ class ServerSyncTool
|
|||
return $this->post('/galaxies/pushCluster', [$cluster], $logMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $rules
|
||||
* @return HttpSocketResponseExtended
|
||||
* @throws HttpSocketHttpException
|
||||
* @throws HttpSocketJsonException
|
||||
*/
|
||||
public function filterAnalystDataForPush(array $candidates)
|
||||
{
|
||||
if (!$this->isSupported(self::PERM_ANALYST_DATA)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->post('/analyst_data/filterAnalystDataForPush', $candidates);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $rules
|
||||
* @return HttpSocketResponseExtended
|
||||
* @throws HttpSocketHttpException
|
||||
* @throws HttpSocketJsonException
|
||||
*/
|
||||
public function fetchIndexMinimal(array $rules)
|
||||
{
|
||||
if (!$this->isSupported(self::PERM_ANALYST_DATA)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->post('/analyst_data/indexMinimal', $rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpSocketJsonException
|
||||
* @throws HttpSocketHttpException
|
||||
*/
|
||||
public function fetchAnalystData($type, array $uuids)
|
||||
{
|
||||
if (!$this->isSupported(self::PERM_ANALYST_DATA)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$params = [
|
||||
'uuid' => $uuids,
|
||||
];
|
||||
|
||||
$url = '/analyst_data/index/' . $type;
|
||||
$url .= $this->createParams($params);
|
||||
$url .= '.json';
|
||||
return $this->get($url);
|
||||
|
||||
// $response = $this->post('/analyst_data/restSearch' , $params);
|
||||
// return $response->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $analystData
|
||||
* @return HttpSocketResponseExtended
|
||||
* @throws HttpSocketHttpException
|
||||
* @throws HttpSocketJsonException
|
||||
*/
|
||||
public function pushAnalystData($type, array $analystData)
|
||||
{
|
||||
$logMessage = "Pushing Analyst Data #{$analystData[$type]['uuid']} to Server #{$this->serverId()}";
|
||||
return $this->post('/analyst_data/pushAnalystData', $analystData, $logMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return HttpSocketResponseExtended
|
||||
|
@ -414,6 +480,8 @@ class ServerSyncTool
|
|||
return isset($info['perm_sync']) && $info['perm_sync'];
|
||||
case self::PERM_GALAXY_EDITOR:
|
||||
return isset($info['perm_galaxy_editor']) && $info['perm_galaxy_editor'];
|
||||
case self::PERM_ANALYST_DATA:
|
||||
return isset($info['perm_analyst_data']) && $info['perm_analyst_data'];
|
||||
case self::FEATURE_SIGHTING_REST_SEARCH:
|
||||
$version = explode('.', $info['version']);
|
||||
return $version[0] == 2 && $version[1] == 4 && $version[2] > 164;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
class AnalystDataBlocklist extends AppModel
|
||||
{
|
||||
public $useTable = 'analyst_data_blocklists';
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
'change' => 'full'
|
||||
),
|
||||
'Containable',
|
||||
);
|
||||
|
||||
public $blocklistFields = array('analyst_data_uuid', 'comment', 'analyst_data_info', 'analyst_data_orgc');
|
||||
public $blocklistTarget = 'analyst_data';
|
||||
|
||||
public $validate = array(
|
||||
'analyst_data_uuid' => array(
|
||||
'unique' => array(
|
||||
'rule' => 'isUnique',
|
||||
'message' => 'Analyst Data already blocklisted.'
|
||||
),
|
||||
'uuid' => array(
|
||||
'rule' => array('uuid'),
|
||||
'message' => 'Please provide a valid UUID'
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
parent::beforeValidate();
|
||||
if (empty($this->data['AnalystDataBlocklist']['id'])) {
|
||||
$this->data['AnalystDataBlocklist']['date_created'] = date('Y-m-d H:i:s');
|
||||
}
|
||||
if (empty($this->data['AnalystDataBlocklist']['comment'])) {
|
||||
$this->data['AnalystDataBlocklist']['comment'] = '';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $analystDataUUID
|
||||
* @return bool
|
||||
*/
|
||||
public function checkIfBlocked($analystDataUUID)
|
||||
{
|
||||
return $this->hasAny([
|
||||
'analyst_data_uuid' => $analystDataUUID,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -48,6 +48,9 @@ class AppModel extends Model
|
|||
/** @var Workflow|null */
|
||||
private $Workflow;
|
||||
|
||||
public $includeAnalystData;
|
||||
public $includeAnalystDataRecursive;
|
||||
|
||||
// deprecated, use $db_changes
|
||||
// major -> minor -> hotfix -> requires_logout
|
||||
const OLD_DB_CHANGES = array(
|
||||
|
@ -87,7 +90,8 @@ class AppModel extends Model
|
|||
99 => false, 100 => false, 101 => false, 102 => false, 103 => false, 104 => false,
|
||||
105 => false, 106 => false, 107 => false, 108 => false, 109 => false, 110 => false,
|
||||
111 => false, 112 => false, 113 => true, 114 => false, 115 => false, 116 => false,
|
||||
117 => false, 118 => false, 119 => false, 120 => false
|
||||
117 => false, 118 => false, 119 => false, 120 => false, 121 => false, 122 => false,
|
||||
123 => false,
|
||||
);
|
||||
|
||||
const ADVANCED_UPDATES_DESCRIPTION = array(
|
||||
|
@ -2013,6 +2017,153 @@ class AppModel extends Model
|
|||
case 119:
|
||||
$sqlArray[] = "ALTER TABLE `access_logs` MODIFY `action` varchar(191) NOT NULL";
|
||||
break;
|
||||
case 121:
|
||||
$sqlArray[] = "CREATE TABLE `notes` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`authors` text,
|
||||
`org_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`orgc_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`created` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`modified` datetime ON UPDATE CURRENT_TIMESTAMP,
|
||||
`distribution` tinyint(4) NOT NULL,
|
||||
`sharing_group_id` int(10) unsigned,
|
||||
`locked` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`note` mediumtext,
|
||||
`language` varchar(16) DEFAULT 'en',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `object_uuid` (`object_uuid`),
|
||||
KEY `object_type` (`object_type`),
|
||||
KEY `org_uuid` (`org_uuid`),
|
||||
KEY `orgc_uuid` (`orgc_uuid`),
|
||||
KEY `distribution` (`distribution`),
|
||||
KEY `sharing_group_id` (`sharing_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
|
||||
$sqlArray[] = "CREATE TABLE `opinions` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`authors` text,
|
||||
`org_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`orgc_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`created` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`modified` datetime ON UPDATE CURRENT_TIMESTAMP,
|
||||
`distribution` tinyint(4) NOT NULL,
|
||||
`sharing_group_id` int(10) unsigned,
|
||||
`locked` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`opinion` int(10) unsigned,
|
||||
`comment` text,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `object_uuid` (`object_uuid`),
|
||||
KEY `object_type` (`object_type`),
|
||||
KEY `org_uuid` (`org_uuid`),
|
||||
KEY `orgc_uuid` (`orgc_uuid`),
|
||||
KEY `distribution` (`distribution`),
|
||||
KEY `sharing_group_id` (`sharing_group_id`),
|
||||
KEY `opinion` (`opinion`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
|
||||
$sqlArray[] = "CREATE TABLE `relationships` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(40) CHARACTER SET ascii NOT NULL,
|
||||
`object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`authors` text,
|
||||
`org_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`orgc_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`created` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`modified` datetime ON UPDATE CURRENT_TIMESTAMP,
|
||||
`distribution` tinyint(4) NOT NULL,
|
||||
`sharing_group_id` int(10) unsigned,
|
||||
`locked` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci,
|
||||
`related_object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`related_object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `object_uuid` (`object_uuid`),
|
||||
KEY `object_type` (`object_type`),
|
||||
KEY `org_uuid` (`org_uuid`),
|
||||
KEY `orgc_uuid` (`orgc_uuid`),
|
||||
KEY `distribution` (`distribution`),
|
||||
KEY `sharing_group_id` (`sharing_group_id`),
|
||||
KEY `relationship_type` (`relationship_type`),
|
||||
KEY `related_object_uuid` (`related_object_uuid`),
|
||||
KEY `related_object_type` (`related_object_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
|
||||
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `analyst_data_blocklists` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`analyst_data_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
|
||||
`created` datetime NOT NULL,
|
||||
`analyst_data_info` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
|
||||
`comment` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci,
|
||||
`analyst_data_orgc` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `analyst_data_uuid` (`analyst_data_uuid`),
|
||||
KEY `analyst_data_orgc` (`analyst_data_orgc`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
|
||||
|
||||
$sqlArray[] = "ALTER TABLE `roles` ADD `perm_analyst_data` tinyint(1) NOT NULL DEFAULT 0;";
|
||||
$sqlArray[] = "UPDATE `roles` SET `perm_analyst_data`=1 WHERE `perm_add` = 1;";
|
||||
|
||||
$sqlArray[] = "ALTER TABLE `servers` ADD `push_analyst_data` tinyint(1) NOT NULL DEFAULT 0 AFTER `push_galaxy_clusters`;";
|
||||
$sqlArray[] = "ALTER TABLE `servers` ADD `pull_analyst_data` tinyint(1) NOT NULL DEFAULT 0 AFTER `push_analyst_data`;";
|
||||
break;
|
||||
case 122:
|
||||
$sqlArray[] = "CREATE TABLE `collections` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`org_id` int(10) unsigned NOT NULL,
|
||||
`orgc_id` int(10) unsigned NOT NULL,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`created` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`modified` datetime ON UPDATE CURRENT_TIMESTAMP,
|
||||
`distribution` tinyint(4) NOT NULL,
|
||||
`sharing_group_id` int(10) unsigned,
|
||||
`name` varchar(191) NOT NULL,
|
||||
`type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`description` mediumtext,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `name` (`name`),
|
||||
KEY `type` (`type`),
|
||||
KEY `org_id` (`org_id`),
|
||||
KEY `orgc_id` (`orgc_id`),
|
||||
KEY `user_id` (`user_id`),
|
||||
KEY `distribution` (`distribution`),
|
||||
KEY `sharing_group_id` (`sharing_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
|
||||
$sqlArray[] = "CREATE TABLE `collection_elements` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`element_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`element_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
|
||||
`collection_id` int(10) unsigned NOT NULL,
|
||||
`description` text,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `element_uuid` (`element_uuid`),
|
||||
KEY `element_type` (`element_type`),
|
||||
KEY `collection_id` (`collection_id`),
|
||||
UNIQUE KEY `unique_element` (`element_uuid`, `collection_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
break;
|
||||
case 123:
|
||||
$sqlArray[] = 'ALTER TABLE `notes` MODIFY `created` datetime NOT NULL';
|
||||
$sqlArray[] = 'ALTER TABLE `opinions` MODIFY `created` datetime NOT NULL;';
|
||||
$sqlArray[] = 'ALTER TABLE `relationships` MODIFY `created` datetime NOT NULL;';
|
||||
$sqlArray[] = 'ALTER TABLE `notes` MODIFY `modified` datetime NOT NULL;';
|
||||
$sqlArray[] = 'ALTER TABLE `opinions` MODIFY `modified` datetime NOT NULL;';
|
||||
$sqlArray[] = 'ALTER TABLE `relationships` MODIFY `modified` datetime NOT NULL;';
|
||||
break;
|
||||
case 'fixNonEmptySharingGroupID':
|
||||
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
|
@ -4051,8 +4202,17 @@ class AppModel extends Model
|
|||
if (!empty($query['order']) && $this->validOrderClause($query['order']) === false) {
|
||||
throw new InvalidArgumentException('Invalid order clause');
|
||||
}
|
||||
|
||||
return parent::find($type, $query);
|
||||
$results = parent::find($type, $query);
|
||||
if (!empty($query['includeAnalystData']) && $this->Behaviors->enabled('AnalystDataParent')) {
|
||||
if ($type === 'first') {
|
||||
$results[$this->alias] = array_merge($results[$this->alias], $this->attachAnalystData($results[$this->alias]));
|
||||
} else if ($type === 'all') {
|
||||
foreach ($results as $k => $result) {
|
||||
$results[$k][$this->alias] = array_merge($results[$k][$this->alias], $this->attachAnalystData($results[$k][$this->alias]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function validOrderClause($order)
|
||||
|
@ -4087,7 +4247,6 @@ class AppModel extends Model
|
|||
$newImageDir = APP . 'files/img';
|
||||
$oldOrgDir = new Folder($oldImageDir . '/orgs');
|
||||
$oldCustomDir = new Folder($oldImageDir . '/custom');
|
||||
$result = false;
|
||||
$result = $oldOrgDir->copy([
|
||||
'from' => $oldImageDir . '/orgs',
|
||||
'to' => $newImageDir . '/orgs',
|
||||
|
|
|
@ -36,7 +36,8 @@ class Attribute extends AppModel
|
|||
'Trim',
|
||||
'Containable',
|
||||
'Regexp' => array('fields' => array('value')),
|
||||
'LightPaginator'
|
||||
'LightPaginator',
|
||||
'AnalystDataParent',
|
||||
);
|
||||
|
||||
public $displayField = 'value';
|
||||
|
@ -2658,6 +2659,7 @@ class Attribute extends AppModel
|
|||
if (!empty($attribute['Sighting'])) {
|
||||
$this->Sighting->captureSightings($attribute['Sighting'], $this->id, $eventId, $user);
|
||||
}
|
||||
$this->Event->captureAnalystData($user, $attribute);
|
||||
}
|
||||
if (!empty($this->validationErrors)) {
|
||||
$validationErrors = $this->validationErrors;
|
||||
|
@ -2798,6 +2800,7 @@ class Attribute extends AppModel
|
|||
if (!empty($attribute['Sighting'])) {
|
||||
$this->Sighting->captureSightings($attribute['Sighting'], $attributeId, $eventId, $user);
|
||||
}
|
||||
$this->Event->captureAnalystData($user, $attribute);
|
||||
if ($user['Role']['perm_tagger']) {
|
||||
/*
|
||||
We should unwrap the line below and remove the server option in the future once we have tag soft-delete
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Common functions for the 3 analyst objects
|
||||
*/
|
||||
class AnalystDataBehavior extends ModelBehavior
|
||||
{
|
||||
public $SharingGroup;
|
||||
|
||||
private $__current_type = null;
|
||||
|
||||
public function setup(Model $Model, $settings = array()) {
|
||||
// We want to know whether we're a Note, Opinion or Relationship
|
||||
$this->__current_type = $Model->alias;
|
||||
}
|
||||
|
||||
// Return the analystData of the current type for a given UUID (this only checks the ACL of the analystData, NOT of the parent.)
|
||||
public function fetchForUuid(Model $Model, $uuid, $user = null)
|
||||
{
|
||||
$conditions = [
|
||||
'object_uuid' => $uuid
|
||||
];
|
||||
$type = $Model->current_type;
|
||||
if (empty($user['Role']['perm_site_admin'])) {
|
||||
$validSharingGroups = $Model->SharingGroup->authorizedIds($user, true);
|
||||
$conditions['AND'][] = [
|
||||
'OR' => [
|
||||
$type . '.orgc_uuid' => $user['Organisation']['uuid'],
|
||||
$type . '.org_uuid' => $user['Organisation']['uuid'],
|
||||
$type . '.distribution IN' => [1, 2, 3],
|
||||
'AND' => [
|
||||
$type . '.distribution' => 4,
|
||||
$type . '.sharing_group_id IN' => $validSharingGroups
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
return $Model->find('all', [
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['Org', 'Orgc', 'SharingGroup'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkACL()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Common functions for the 3 analyst objects
|
||||
*/
|
||||
class AnalystDataParentBehavior extends ModelBehavior
|
||||
{
|
||||
private $__currentUser = null;
|
||||
|
||||
public $User;
|
||||
|
||||
|
||||
|
||||
public function attachAnalystData(Model $model, array $object, array $types = ['Note', 'Opinion', 'Relationship'])
|
||||
{
|
||||
// No uuid, nothing to attach
|
||||
if (empty($object['uuid'])) {
|
||||
return $object;
|
||||
}
|
||||
if (empty($this->__currentUser)) {
|
||||
$user_id = Configure::read('CurrentUserId');
|
||||
$this->User = ClassRegistry::init('User');
|
||||
if ($user_id) {
|
||||
$this->__currentUser = $this->User->getAuthUser($user_id);
|
||||
}
|
||||
}
|
||||
$data = [];
|
||||
foreach ($types as $type) {
|
||||
$this->{$type} = ClassRegistry::init($type);
|
||||
$this->{$type}->fetchRecursive = !empty($model->includeAnalystDataRecursive);
|
||||
$temp = $this->{$type}->fetchForUuid($object['uuid'], $this->__currentUser);
|
||||
if (!empty($temp)) {
|
||||
foreach ($temp as $k => $temp_element) {
|
||||
$data[$type][] = $temp_element[$type];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function afterFind(Model $model, $results, $primary = false)
|
||||
{
|
||||
if (!empty($model->includeAnalystData)) {
|
||||
foreach ($results as $k => $item) {
|
||||
if (isset($item[$model->alias])) {
|
||||
$results[$k] = array_merge($results[$k], $this->attachAnalystData($model, $item[$model->alias]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
class Collection extends AppModel
|
||||
{
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'Containable'
|
||||
);
|
||||
|
||||
public $belongsTo = [
|
||||
'Orgc' => array(
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'orgc_id',
|
||||
'fields' => [
|
||||
'Orgc.id',
|
||||
'Orgc.uuid',
|
||||
'Orgc.name'
|
||||
]
|
||||
),
|
||||
'Org' => array(
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'org_id',
|
||||
'fields' => [
|
||||
'Org.id',
|
||||
'Org.uuid',
|
||||
'Org.name'
|
||||
]
|
||||
),
|
||||
'User' => array(
|
||||
'className' => 'User',
|
||||
'foreignKey' => 'user_id',
|
||||
'fields' => [
|
||||
'User.id',
|
||||
'User.email'
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
public $hasMany = [
|
||||
'CollectionElement' => [
|
||||
'dependent' => true
|
||||
]
|
||||
];
|
||||
|
||||
public $valid_targets = [
|
||||
'Attribute',
|
||||
'Event',
|
||||
'GalaxyCluster',
|
||||
'Galaxy',
|
||||
'Object',
|
||||
'Note',
|
||||
'Opinion',
|
||||
'Relationship',
|
||||
'Organisation',
|
||||
'SharingGroup'
|
||||
];
|
||||
|
||||
public $current_user = null;
|
||||
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
if (empty($this->data['Collection'])) {
|
||||
$this->data = ['Collection' => $this->data];
|
||||
}
|
||||
if (empty($this->id) && empty($this->data['Collection']['uuid'])) {
|
||||
$this->data['Collection']['uuid'] = CakeText::uuid();
|
||||
}
|
||||
if (empty($this->id)) {
|
||||
$this->data['Collection']['user_id'] = $this->current_user['id'];
|
||||
if (empty($this->data['Collection']['orgc_id']) || empty($this->current_user['Role']['perm_sync'])) {
|
||||
$this->data['Collection']['orgc_id'] = $this->current_user['Organisation']['id'];
|
||||
}
|
||||
$this->data['Collection']['org_id'] = $this->current_user['Organisation']['id'];
|
||||
$this->data['Collection']['user_id'] = $this->current_user['id'];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function mayModify($user_id, $collection_id)
|
||||
{
|
||||
$user = $this->User->getAuthUser($user_id);
|
||||
$collection = $this->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Collection.id' => $collection_id]
|
||||
]);
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
if (empty($user['Role']['perm_modify'])) {
|
||||
return false;
|
||||
}
|
||||
if (!empty($user['Role']['perm_modify_org'])) {
|
||||
if ($user['org_id'] == $collection['Collection']['Orgc_id']) {
|
||||
return true;
|
||||
}
|
||||
if ($user['Role']['perm_sync'] && $user['org_id'] == $collection['Collection']['Org_id']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!empty($user['Role']['perm_modify']) && $user['id'] === $collection['Collection']['user_id']) {
|
||||
}
|
||||
}
|
||||
|
||||
public function mayView($user_id, $collection_id)
|
||||
{
|
||||
$user = $this->User->getAuthUser($user_id);
|
||||
$collection = $this->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Collection.id' => $collection_id]
|
||||
]);
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($collection['Collection']['org_id'] == $user('org_id')) {
|
||||
return true;
|
||||
}
|
||||
if (in_array($collection['Collection']['distribution'], [1,2,3])) {
|
||||
return true;
|
||||
}
|
||||
if ($collection['Collection']['distribution'] === 4) {
|
||||
$SharingGroup = ClassRegistry::init('SharingGroup');
|
||||
$sgs = $this->SharingGroup->fetchAllAuthorised($user, 'uuid');
|
||||
if (isset($sgs[$collection['Collection']['sharing_group_id']])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function rearrangeCollection(array $collection) {
|
||||
foreach ($collection as $key => $elements) {
|
||||
if ($key !== 'Collection') {
|
||||
$collection['Collection'][$key] = $elements;
|
||||
unset($collection[$key]);
|
||||
}
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
class CollectionElement extends AppModel
|
||||
{
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'Containable'
|
||||
);
|
||||
|
||||
public $belongsTo = array(
|
||||
'Collection' => array(
|
||||
'className' => 'Collection',
|
||||
'foreignKey' => 'collection_id'
|
||||
)
|
||||
);
|
||||
|
||||
// Make sure you also update the validation for element_type to include anything you add here.
|
||||
public $valid_types = [
|
||||
'Event',
|
||||
'GalaxyCluster'
|
||||
];
|
||||
|
||||
public $validate = [
|
||||
'collection_id' => [
|
||||
'numeric' => [
|
||||
'rule' => ['numeric']
|
||||
]
|
||||
],
|
||||
'uuid' => [
|
||||
'uuid' => [
|
||||
'rule' => 'uuid',
|
||||
'message' => 'Please provide a valid RFC 4122 UUID'
|
||||
]
|
||||
],
|
||||
'element_uuid' => [
|
||||
'element_uuid' => [
|
||||
'rule' => 'uuid',
|
||||
'message' => 'Please provide a valid RFC 4122 UUID'
|
||||
]
|
||||
],
|
||||
'element_type' => [
|
||||
'element_type' => [
|
||||
'rule' => ['inList', ['Event', 'GalaxyCluster']],
|
||||
'message' => 'Invalid object type.'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
// Massage to a common format
|
||||
if (empty($this->data['CollectionElement'])) {
|
||||
$this->data = ['CollectionElement' => $this->data];
|
||||
}
|
||||
|
||||
// if we're creating a new element, assign a uuid (unless provided)
|
||||
if (empty($this->id) && empty($this->data['CollectionElement']['uuid'])) {
|
||||
$this->data['CollectionElement']['uuid'] = CakeText::uuid();
|
||||
}
|
||||
if (
|
||||
empty($this->id) &&
|
||||
empty($this->data['CollectionElement']['element_type']) &&
|
||||
!empty($this->data['CollectionElement']['element_uuid'])
|
||||
) {
|
||||
$this->data['CollectionElement']['element_type'] = $this->deduceType($this->data['CollectionElement']['element_uuid']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function mayModify(int $user_id, int $collection_id)
|
||||
{
|
||||
$user = $this->User->getAuthUser($user_id);
|
||||
$collection = $this->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Collection.id' => $collection_id]
|
||||
]);
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
if (empty($user['Role']['perm_modify'])) {
|
||||
return false;
|
||||
}
|
||||
if (!empty($user['Role']['perm_modify_org'])) {
|
||||
if ($user['org_id'] == $collection['Collection']['Orgc_id']) {
|
||||
return true;
|
||||
}
|
||||
if ($user['Role']['perm_sync'] && $user['org_id'] == $collection['Collection']['Org_id']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!empty($user['Role']['perm_modify']) && $user['id'] === $collection['Collection']['user_id']) {
|
||||
}
|
||||
}
|
||||
|
||||
public function mayView(int $user_id, int $collection_id)
|
||||
{
|
||||
$user = $this->User->getAuthUser($user_id);
|
||||
$collection = $this->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Collection.id' => $collection_id]
|
||||
]);
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($collection['Collection']['org_id'] == $user('org_id')) {
|
||||
return true;
|
||||
}
|
||||
if (in_array($collection['Collection']['distribution'], [1,2,3])) {
|
||||
return true;
|
||||
}
|
||||
if ($collection['Collection']['distribution'] === 4) {
|
||||
$SharingGroup = ClassRegistry::init('SharingGroup');
|
||||
$sgs = $this->SharingGroup->fetchAllAuthorised($user, 'uuid');
|
||||
if (isset($sgs[$collection['Collection']['sharing_group_id']])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deduceType(string $uuid)
|
||||
{
|
||||
foreach ($this->valid_types as $valid_type) {
|
||||
$this->{$valid_type} = ClassRegistry::init($valid_type);
|
||||
$result = $this->$valid_type->find('first', [
|
||||
'conditions' => [$valid_type.'.uuid' => $uuid],
|
||||
'recursive' => -1
|
||||
]);
|
||||
if (!empty($result)) {
|
||||
return $valid_type;
|
||||
}
|
||||
}
|
||||
throw new NotFoundException(__('Invalid UUID'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass a Collection as received from another instance to this function to capture the elements
|
||||
* The received object is authoritative, so all elements that no longer exist in the upstream will be culled.
|
||||
*/
|
||||
public function captureElements($data) {
|
||||
$temp = $this->find('all', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['CollectionElement.collection_id' => $data['Collection']['id']]
|
||||
]);
|
||||
$oldElements = [];
|
||||
foreach ($temp as $oldElement) {
|
||||
$oldElements[$oldElement['CollectionElement']['uuid']] = $oldElement['CollectionElement'];
|
||||
}
|
||||
if (isset($data['Collection']['CollectionElement'])) {
|
||||
$elementsToSave = [];
|
||||
foreach ($data['Collection']['CollectionElement'] as $k => $element) {
|
||||
if (empty($element['uuid'])) {
|
||||
$element['uuid'] = CakeText::uuid();
|
||||
}
|
||||
if (isset($oldElements[$element['uuid']])) {
|
||||
if (isset($element['description'])) {
|
||||
$oldElements[$element['uuid']]['description'] = $element['description'];
|
||||
}
|
||||
$elementsToSave[$k] = $oldElements[$element['uuid']];
|
||||
unset($oldElements[$element['uuid']]);
|
||||
} else {
|
||||
$elementsToSave[$k] = [
|
||||
'CollectionElement' => [
|
||||
'uuid' => $element['uuid'],
|
||||
'element_uuid' => $element['element_uuid'],
|
||||
'element_type' => $element['element_type'],
|
||||
'description' => $element['description'],
|
||||
'collection_id' => $data['Collection']['id']
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
foreach ($elementsToSave as $k => $element) {
|
||||
if (empty($element['CollectionElement']['id'])) {
|
||||
$this->create();
|
||||
}
|
||||
try{
|
||||
$this->save($element);
|
||||
} catch (PDOException $e) {
|
||||
// duplicate value?
|
||||
}
|
||||
}
|
||||
foreach ($oldElements as $toDelete) {
|
||||
$this->delete($toDelete['id']);
|
||||
}
|
||||
$temp = $this->find('all', [
|
||||
'conditions' => ['CollectionElement.collection_id' => $data['Collection']['id']],
|
||||
'recursive' => -1
|
||||
]);
|
||||
$data['Collection']['CollectionElement'] = [];
|
||||
foreach ($temp as $element) {
|
||||
$data['Collection']['CollectionElement'][] = $element['CollectionElement'];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,9 @@ App::uses('ProcessTool', 'Tools');
|
|||
* @property Organisation $Org
|
||||
* @property Organisation $Orgc
|
||||
* @property CryptographicKey $CryptographicKey
|
||||
* @property Note $Note
|
||||
* @property Opinion $Opinion
|
||||
* @property Relationship $Relationship
|
||||
*/
|
||||
class Event extends AppModel
|
||||
{
|
||||
|
@ -40,7 +43,8 @@ class Event extends AppModel
|
|||
'change' => 'full'),
|
||||
'Trim',
|
||||
'Containable',
|
||||
'EventWarning'
|
||||
'EventWarning',
|
||||
'AnalystDataParent'
|
||||
);
|
||||
|
||||
public $displayField = 'id';
|
||||
|
@ -1760,6 +1764,11 @@ class Event extends AppModel
|
|||
if (!isset($options['fetchFullClusterRelationship'])) {
|
||||
$options['fetchFullClusterRelationship'] = false;
|
||||
}
|
||||
if (!isset($options['includeAnalystData'])) {
|
||||
$options['includeAnalystData'] = false;
|
||||
} else {
|
||||
$options['includeAnalystData'] = !empty($options['includeAnalystData']);
|
||||
}
|
||||
foreach ($this->possibleOptions as $opt) {
|
||||
if (!isset($options[$opt])) {
|
||||
$options[$opt] = false;
|
||||
|
@ -2038,6 +2047,8 @@ class Event extends AppModel
|
|||
if (!empty($options['page'])) {
|
||||
$params['page'] = $options['page'];
|
||||
}
|
||||
$this->includeAnalystData = $options['includeAnalystData'];
|
||||
$this->includeAnalystDataRecursive = $options['includeAnalystData'];
|
||||
if (!empty($options['order'])) {
|
||||
$params['order'] = $this->findOrder(
|
||||
$options['order'],
|
||||
|
@ -2198,6 +2209,13 @@ class Event extends AppModel
|
|||
if (!empty($options['includeGranularCorrelations'])) {
|
||||
$event['Attribute'] = $this->Attribute->Correlation->attachCorrelationExclusion($event['Attribute']);
|
||||
}
|
||||
if (!empty($options['includeAnalystData'])) {
|
||||
foreach ($event['Attribute'] as $k => $attribute) {
|
||||
$this->Attribute->includeAnalystDataRecursive = true;
|
||||
$analyst_data = $this->Attribute->attachAnalystData($attribute);
|
||||
$event['Attribute'][$k] = array_merge($event['Attribute'][$k], $analyst_data);
|
||||
}
|
||||
}
|
||||
|
||||
// move all object attributes to a temporary container
|
||||
$tempObjectAttributeContainer = array();
|
||||
|
@ -2248,6 +2266,11 @@ class Event extends AppModel
|
|||
if (isset($tempObjectAttributeContainer[$objectValue['id']])) {
|
||||
$objectValue['Attribute'] = $tempObjectAttributeContainer[$objectValue['id']];
|
||||
}
|
||||
if (!empty($options['includeAnalystData'])) {
|
||||
$this->Object->includeAnalystDataRecursive = true;
|
||||
$analyst_data = $this->Object->attachAnalystData($objectValue);
|
||||
$objectValue = array_merge($objectValue, $analyst_data);
|
||||
}
|
||||
}
|
||||
unset($tempObjectAttributeContainer);
|
||||
}
|
||||
|
@ -3633,6 +3656,7 @@ class Event extends AppModel
|
|||
$created_id = 0;
|
||||
$event['Event']['locked'] = 1;
|
||||
$event['Event']['published'] = $publish;
|
||||
$event = $this->updatedLockedFieldForAllAnalystData($event);
|
||||
$result = $this->_add($event, true, $user, '', null, false, null, $created_id, $validationIssues);
|
||||
$results[] = [
|
||||
'info' => $event['Event']['info'],
|
||||
|
@ -3644,6 +3668,59 @@ class Event extends AppModel
|
|||
return $results;
|
||||
}
|
||||
|
||||
private function updatedLockedFieldForAllAnalystData(array $event): array
|
||||
{
|
||||
$event = $this->updatedLockedFieldForAnalystData($event, 'Event');
|
||||
if (!empty($event['Event']['Attribute'])) {
|
||||
for ($i=0; $i < count($event['Event']['Attribute']); $i++) {
|
||||
$event['Event']['Attribute'][$i] = $this->updatedLockedFieldForAnalystData($event['Event']['Attribute'][$i]);
|
||||
}
|
||||
}
|
||||
if (!empty($event['Event']['Object'])) {
|
||||
for ($i=0; $i < count($event['Event']['Object']); $i++) {
|
||||
$event['Event']['Object'][$i] = $this->updatedLockedFieldForAnalystData($event['Event']['Object'][$i]);
|
||||
if (!empty($event['Event']['Object'][$i])) {
|
||||
for ($j=0; $j < count($event['Event']['Object'][$i]['Attribute']); $j++) {
|
||||
$event['Event']['Object'][$i]['Attribute'][$j] = $this->updatedLockedFieldForAnalystData($event['Event']['Object'][$i]['Attribute'][$j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($event['Event']['EventReport'])) {
|
||||
for ($i=0; $i < count($event['Event']['EventReport']); $i++) {
|
||||
$event['Event']['EventReport'][$i] = $this->updatedLockedFieldForAnalystData($event['Event']['EventReport'][$i]);
|
||||
}
|
||||
}
|
||||
return $event;
|
||||
}
|
||||
|
||||
private function updatedLockedFieldForAnalystData(array $data, $model=false): array
|
||||
{
|
||||
$this->AnalystData = ClassRegistry::init('AnalystData');
|
||||
if (!empty($model)) {
|
||||
$data = $data[$model];
|
||||
}
|
||||
foreach ($this->AnalystData::ANALYST_DATA_TYPES as $type) {
|
||||
if (!empty($data[$type])) {
|
||||
for ($i=0; $i < count($data[$type]); $i++) {
|
||||
$data[$type][$i]['locked'] = true;
|
||||
foreach ($this->AnalystData::ANALYST_DATA_TYPES as $childType) {
|
||||
if (!empty($data[$type][$i][$childType])) {
|
||||
for ($j=0; $j < count($data[$type][$i][$childType]); $j++) {
|
||||
$data[$type][$i][$childType][$j]['locked'] = true;
|
||||
$data[$type][$i][$childType][$j] = $this->updatedLockedFieldForAnalystData($data[$type][$i][$childType][$j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($model)) {
|
||||
$data = [$model => $data];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Low level function to add an Event based on an Event $data array.
|
||||
*
|
||||
|
@ -3881,6 +3958,8 @@ class Event extends AppModel
|
|||
if (isset($data['Sighting']) && !empty($data['Sighting'])) {
|
||||
$this->Sighting->captureSightings($data['Sighting'], null, $this->id, $user);
|
||||
}
|
||||
|
||||
$this->captureAnalystData($user, $data['Event']);
|
||||
if ($fromXml) {
|
||||
$created_id = $this->id;
|
||||
}
|
||||
|
@ -4182,6 +4261,8 @@ class Event extends AppModel
|
|||
if (isset($data['Sighting']) && !empty($data['Sighting'])) {
|
||||
$this->Sighting->captureSightings($data['Sighting'], null, $this->id, $user);
|
||||
}
|
||||
|
||||
$this->captureAnalystData($user, $data['Event']);
|
||||
// if published -> do the actual publishing
|
||||
if ($changed && (!empty($data['Event']['published']) && 1 == $data['Event']['published'])) {
|
||||
// The edited event is from a remote server ?
|
||||
|
@ -5914,15 +5995,17 @@ class Event extends AppModel
|
|||
* @param int $distribution
|
||||
* @param int|null $sharingGroupId
|
||||
* @param bool $galaxiesAsTags
|
||||
* @param int $clusterDistribution
|
||||
* @param int|null $clusterSharingGroupId
|
||||
* @param bool $debug
|
||||
* @return int|string|array
|
||||
* @throws JsonException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function upload_stix(array $user, $file, $stixVersion, $originalFile, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $debug = false)
|
||||
public function upload_stix(array $user, $file, $stixVersion, $originalFile, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $clusterDistribution, $clusterSharingGroupId, $debug = false)
|
||||
{
|
||||
$decoded = $this->convertStixToMisp($stixVersion, $file, $distribution, $sharingGroupId, $galaxiesAsTags, $debug);
|
||||
$decoded = $this->convertStixToMisp($stixVersion, $file, $distribution, $sharingGroupId, $galaxiesAsTags, $clusterDistribution, $clusterSharingGroupId, $user['Organisation']['uuid'], $debug);
|
||||
|
||||
if (!empty($decoded['success'])) {
|
||||
$data = JsonTool::decodeArray($decoded['converted']);
|
||||
|
@ -5986,11 +6069,14 @@ class Event extends AppModel
|
|||
* @param int $distribution
|
||||
* @param int|null $sharingGroupId
|
||||
* @param bool $galaxiesAsTags
|
||||
* @param int $clusterDistribution
|
||||
* @param int|null $clusterSharingGroupId
|
||||
* @param string $orgUuid
|
||||
* @param bool $debug
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function convertStixToMisp($stixVersion, $file, $distribution, $sharingGroupId, $galaxiesAsTags, $debug)
|
||||
private function convertStixToMisp($stixVersion, $file, $distribution, $sharingGroupId, $galaxiesAsTags, $clusterDistribution, $clusterSharingGroupId, $orgUuid, $debug)
|
||||
{
|
||||
$scriptDir = APP . 'files' . DS . 'scripts';
|
||||
if ($stixVersion === '2' || $stixVersion === '2.0' || $stixVersion === '2.1') {
|
||||
|
@ -6001,12 +6087,18 @@ class Event extends AppModel
|
|||
$scriptFile,
|
||||
'-i', $file,
|
||||
'--distribution', $distribution,
|
||||
'--org_uuid', $orgUuid
|
||||
];
|
||||
if ($distribution == 4) {
|
||||
array_push($shellCommand, '--sharing_group_id', $sharingGroupId);
|
||||
}
|
||||
if ($galaxiesAsTags) {
|
||||
$shellCommand[] = '--galaxies_as_tags';
|
||||
} else {
|
||||
array_push($shellCommand, '--cluster_distribution', $clusterDistribution);
|
||||
if ($clusterDistribution == 4) {
|
||||
array_push($shellCommand, '--cluster_sharing_group_id', $clusterSharingGroupId);
|
||||
}
|
||||
}
|
||||
if ($debug) {
|
||||
$shellCommand[] = '--debug';
|
||||
|
@ -7948,6 +8040,20 @@ class Event extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
public function captureAnalystData($user, $data)
|
||||
{
|
||||
$this->Note = ClassRegistry::init('Note');
|
||||
$this->Opinion = ClassRegistry::init('Opinion');
|
||||
$this->Relationship = ClassRegistry::init('Relationship');
|
||||
foreach ($this->Note::ANALYST_DATA_TYPES as $type) {
|
||||
if (!empty($data[$type])) {
|
||||
foreach ($data[$type] as $analystData) {
|
||||
$this->{$type}->captureAnalystData($user, $analystData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getTrendsForTags(array $user, array $eventFilters=[], int $baseDayRange, int $rollingWindows=3, $tagFilterPrefixes=null): array
|
||||
{
|
||||
$fullDayNumber = $baseDayRange + $baseDayRange * $rollingWindows;
|
||||
|
|
|
@ -17,6 +17,7 @@ class EventReport extends AppModel
|
|||
'change' => 'full'
|
||||
),
|
||||
'Regexp' => array('fields' => array('value')),
|
||||
'AnalystDataParent',
|
||||
);
|
||||
|
||||
public $validate = array(
|
||||
|
@ -118,6 +119,8 @@ class EventReport extends AppModel
|
|||
__('Event Report dropped due to validation for Event report %s failed: %s', $this->data['EventReport']['uuid'], $this->data['EventReport']['name']),
|
||||
__('Validation errors: %s.%sFull report: %s', json_encode($errors), PHP_EOL, json_encode($report['EventReport']))
|
||||
);
|
||||
} else {
|
||||
$this->Event->captureAnalystData($user, $report);
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
@ -188,6 +191,7 @@ class EventReport extends AppModel
|
|||
}
|
||||
$errors = $this->saveAndReturnErrors($report, ['fieldList' => self::CAPTURE_FIELDS], $errors);
|
||||
if (empty($errors)) {
|
||||
$this->Event->captureAnalystData($user, $report['EventReport']);
|
||||
$this->Event->unpublishEvent($eventId);
|
||||
}
|
||||
return $errors;
|
||||
|
|
|
@ -22,6 +22,7 @@ class GalaxyCluster extends AppModel
|
|||
'userKey' => 'user_id',
|
||||
'change' => 'full'),
|
||||
'Containable',
|
||||
'AnalystDataParent',
|
||||
);
|
||||
|
||||
private $__assetCache = array();
|
||||
|
@ -196,7 +197,7 @@ class GalaxyCluster extends AppModel
|
|||
*/
|
||||
public function arrangeData($cluster)
|
||||
{
|
||||
$models = array('Galaxy', 'SharingGroup', 'GalaxyElement', 'GalaxyClusterRelation', 'Org', 'Orgc', 'TargetingClusterRelation');
|
||||
$models = array('Galaxy', 'SharingGroup', 'GalaxyElement', 'GalaxyClusterRelation', 'Org', 'Orgc', 'TargetingClusterRelation', 'Note', 'Opinion', 'Relationship');
|
||||
foreach ($models as $model) {
|
||||
if (isset($cluster[$model])) {
|
||||
$cluster['GalaxyCluster'][$model] = $cluster[$model];
|
||||
|
|
|
@ -20,11 +20,12 @@ class MispObject extends AppModel
|
|||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
'change' => 'full'),
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
'change' => 'full'),
|
||||
'AnalystDataParent'
|
||||
);
|
||||
|
||||
public $belongsTo = array(
|
||||
|
@ -571,11 +572,18 @@ class MispObject extends AppModel
|
|||
if (isset($options['fields'])) {
|
||||
$params['fields'] = $options['fields'];
|
||||
}
|
||||
$contain = [];
|
||||
if (isset($options['contain'])) {
|
||||
$contain = $options['contain'];
|
||||
}
|
||||
if (empty($contain['Event'])) {
|
||||
$contain = ['Event' => ['distribution', 'id', 'user_id', 'orgc_id', 'org_id']];
|
||||
}
|
||||
$results = $this->find('all', array(
|
||||
'conditions' => $params['conditions'],
|
||||
'recursive' => -1,
|
||||
'fields' => $params['fields'],
|
||||
'contain' => array('Event' => array('distribution', 'id', 'user_id', 'orgc_id', 'org_id')),
|
||||
'contain' => $contain,
|
||||
'sort' => false
|
||||
));
|
||||
return $results;
|
||||
|
@ -1131,6 +1139,7 @@ class MispObject extends AppModel
|
|||
$this->Attribute->captureAttribute($attribute, $eventId, $user, $objectId, false, $parentEvent);
|
||||
}
|
||||
}
|
||||
$this->Event->captureAnalystData($user, $object['Object']);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1210,6 +1219,7 @@ class MispObject extends AppModel
|
|||
);
|
||||
return $this->validationErrors;
|
||||
}
|
||||
$this->Event->captureAnalystData($user, $object);
|
||||
if (!empty($object['Attribute'])) {
|
||||
$attributes = [];
|
||||
foreach ($object['Attribute'] as $attribute) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('AnalystData', 'Model');
|
||||
class Note extends AnalystData
|
||||
{
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'AnalystData',
|
||||
);
|
||||
|
||||
public $current_type = 'Note';
|
||||
public $current_type_id = 0;
|
||||
|
||||
public const EDITABLE_FIELDS = [
|
||||
'note',
|
||||
];
|
||||
|
||||
public $validate = [];
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
parent::beforeValidate();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('AnalystData', 'Model');
|
||||
class Opinion extends AnalystData
|
||||
{
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'AnalystData',
|
||||
);
|
||||
|
||||
public $current_type = 'Opinion';
|
||||
public $current_type_id = 1;
|
||||
|
||||
public const EDITABLE_FIELDS = [
|
||||
'opinion',
|
||||
'comment',
|
||||
];
|
||||
|
||||
public $validate = [];
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
parent::beforeValidate();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -196,7 +196,7 @@ class Organisation extends AppModel
|
|||
* @return int Organisation ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public function captureOrg($org, array $user, $force = false)
|
||||
public function captureOrg($org, array $user, $force = false, $returnUUID = false)
|
||||
{
|
||||
$fieldsToFetch = $force ?
|
||||
['id', 'uuid', 'type', 'date_created', 'date_modified', 'nationality', 'sector', 'contacts'] :
|
||||
|
@ -238,7 +238,7 @@ class Organisation extends AppModel
|
|||
}
|
||||
$this->create();
|
||||
$this->save($organisation);
|
||||
return $this->id;
|
||||
return $returnUUID ? $organisation['uuid'] : $this->id;
|
||||
} else {
|
||||
$changed = false;
|
||||
if (isset($org['uuid']) && empty($existingOrg[$this->alias]['uuid'])) {
|
||||
|
@ -262,7 +262,7 @@ class Organisation extends AppModel
|
|||
$this->save($existingOrg);
|
||||
}
|
||||
}
|
||||
return $existingOrg[$this->alias]['id'];
|
||||
return $returnUUID ? $existingOrg[$this->alias]['uuid']: $existingOrg[$this->alias]['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('AnalystData', 'Model');
|
||||
class Relationship extends AnalystData
|
||||
{
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'AnalystData',
|
||||
);
|
||||
|
||||
public $current_type = 'Relationship';
|
||||
public $current_type_id = 2;
|
||||
|
||||
protected $EDITABLE_FIELDS = [
|
||||
'relationship_type',
|
||||
];
|
||||
|
||||
public $validate = [];
|
||||
|
||||
/** @var object|null */
|
||||
protected $Event;
|
||||
/** @var object|null */
|
||||
protected $Attribute;
|
||||
/** @var object|null */
|
||||
protected $Object;
|
||||
/** @var object|null */
|
||||
protected $Note;
|
||||
/** @var object|null */
|
||||
protected $Opinion;
|
||||
/** @var object|null */
|
||||
protected $Relationship;
|
||||
/** @var object|null */
|
||||
protected $User;
|
||||
/** @var array|null */
|
||||
private $__currentUser;
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
$results = parent::afterFind($results, $primary);
|
||||
if (empty($this->__currentUser)) {
|
||||
$user_id = Configure::read('CurrentUserId');
|
||||
$this->User = ClassRegistry::init('User');
|
||||
if ($user_id) {
|
||||
$this->__currentUser = $this->User->getAuthUser($user_id);
|
||||
}
|
||||
}
|
||||
foreach ($results as $i => $v) {
|
||||
if (!empty($v[$this->alias]['related_object_type']) && !empty($v[$this->alias]['related_object_uuid'])) {
|
||||
$results[$i][$this->alias]['related_object'] = $this->getRelatedElement($this->__currentUser, $v[$this->alias]['related_object_type'], $v[$this->alias]['related_object_uuid']);
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getRelatedElement(array $user, $type, $uuid): array
|
||||
{
|
||||
$data = [];
|
||||
if ($type == 'Event') {
|
||||
$this->Event = ClassRegistry::init('Event');
|
||||
$params = [
|
||||
];
|
||||
$backup = $this->Event->includeAnalystData;
|
||||
$this->Event->includeAnalystData = false;
|
||||
$data = $this->Event->fetchSimpleEvent($user, $uuid, $params);
|
||||
$this->Event->includeAnalystData = $backup;
|
||||
} else if ($type == 'Attribute') {
|
||||
$this->Attribute = ClassRegistry::init('Attribute');
|
||||
$params = [
|
||||
'conditions' => [
|
||||
['Attribute.uuid' => $uuid],
|
||||
],
|
||||
'contain' => ['Event' => 'Orgc', 'Object',]
|
||||
];
|
||||
$backup = $this->Attribute->includeAnalystData;
|
||||
$this->Attribute->includeAnalystData = false;
|
||||
$data = $this->Attribute->fetchAttributeSimple($user, $params);
|
||||
$this->Attribute->includeAnalystData = $backup;
|
||||
$data = $this->rearrangeData($data, 'Attribute');
|
||||
} else if ($type == 'Object') {
|
||||
$this->Object = ClassRegistry::init('MispObject');
|
||||
$params = [
|
||||
'conditions' => [
|
||||
['Object.uuid' => $uuid],
|
||||
],
|
||||
'contain' => ['Event' => 'Orgc',]
|
||||
];
|
||||
$backup = $this->Object->includeAnalystData;
|
||||
$this->Object->includeAnalystData = false;
|
||||
$data = $this->Object->fetchObjectSimple($user, $params);
|
||||
$this->Object->includeAnalystData = $backup;
|
||||
if (!empty($data)) {
|
||||
$data = $data[0];
|
||||
}
|
||||
$data = $this->rearrangeData($data, 'Object');
|
||||
} else if ($type == 'Note') {
|
||||
$this->Note = ClassRegistry::init('Note');
|
||||
$params = [
|
||||
|
||||
];
|
||||
$backup = $this->Note->includeAnalystData;
|
||||
$this->Note->includeAnalystData = false;
|
||||
$data = $this->Note->fetchNote();
|
||||
$this->Note->includeAnalystData = $backup;
|
||||
} else if ($type == 'Opinion') {
|
||||
$this->Opinion = ClassRegistry::init('Opinion');
|
||||
$params = [
|
||||
|
||||
];
|
||||
$backup = $this->Opinion->includeAnalystData;
|
||||
$this->Opinion->includeAnalystData = false;
|
||||
$data = $this->Opinion->fetchOpinion();
|
||||
$this->Opinion->includeAnalystData = $backup;
|
||||
} else if ($type == 'Relationship') {
|
||||
$this->Relationship = ClassRegistry::init('Relationship');
|
||||
$params = [
|
||||
|
||||
];
|
||||
$backup = $this->Relationship->includeAnalystData;
|
||||
$this->Relationship->includeAnalystData = false;
|
||||
$data = $this->Relationship->fetchRelationship();
|
||||
$this->Relationship->includeAnalystData = $backup;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function rearrangeData(array $data, $objectType): array
|
||||
{
|
||||
$models = ['Event', 'Attribute', 'Object', 'Organisation', ];
|
||||
if (!empty($data) && !empty($data[$objectType])) {
|
||||
foreach ($models as $model) {
|
||||
if ($model == $objectType) {
|
||||
continue;
|
||||
}
|
||||
if (isset($data[$model])) {
|
||||
$data[$objectType][$model] = $data[$model];
|
||||
unset($data[$model]);
|
||||
}
|
||||
}
|
||||
$data[$objectType]['Organisation'] = $data[$objectType]['Event']['Orgc'];
|
||||
$data[$objectType]['orgc_uuid'] = $data[$objectType]['Event']['Orgc']['uuid'];
|
||||
unset($data[$objectType]['Event']['Orgc']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -331,6 +331,12 @@ class Role extends AppModel
|
|||
'readonlyenabled' => true,
|
||||
'title' => __('Allow the viewing of feed correlations. Enabling this can come at a performance cost.'),
|
||||
),
|
||||
'perm_analyst_data' => array(
|
||||
'id' => 'RolePermAnalystData',
|
||||
'text' => 'Analyst Data Creator',
|
||||
'readonlyenabled' => false,
|
||||
'title' => __('Create or modify Analyst Data such as Analyst Notes or Opinions.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -683,21 +683,24 @@ class Server extends AppModel
|
|||
$job->saveProgress($jobId, 'Pulling sightings.', 75);
|
||||
}
|
||||
$pulledSightings = $eventModel->Sighting->pullSightings($user, $serverSync);
|
||||
$this->AnalystData = ClassRegistry::init('AnalystData');
|
||||
$pulledAnalystData = $this->AnalystData->pull($user, $serverSync);
|
||||
}
|
||||
if ($jobId) {
|
||||
$job->saveStatus($jobId, true, 'Pull completed.');
|
||||
}
|
||||
|
||||
$change = sprintf(
|
||||
'%s events, %s proposals, %s sightings and %s galaxy clusters pulled or updated. %s events failed or didn\'t need an update.',
|
||||
'%s events, %s proposals, %s sightings, %s galaxy clusters and %s analyst data pulled or updated. %s events failed or didn\'t need an update.',
|
||||
count($successes),
|
||||
$pulledProposals,
|
||||
$pulledSightings,
|
||||
$pulledClusters,
|
||||
$pulledAnalystData,
|
||||
count($fails)
|
||||
);
|
||||
$this->loadLog()->createLogEntry($user, 'pull', 'Server', $server['Server']['id'], 'Pull from ' . $server['Server']['url'] . ' initiated by ' . $email, $change);
|
||||
return [$successes, $fails, $pulledProposals, $pulledSightings, $pulledClusters];
|
||||
return [$successes, $fails, $pulledProposals, $pulledSightings, $pulledClusters, $pulledAnalystData];
|
||||
}
|
||||
|
||||
public function filterRuleToParameter($filter_rules)
|
||||
|
@ -757,6 +760,41 @@ class Server extends AppModel
|
|||
return $clusterArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetchUUIDsFromServer Fetch remote analyst datas' UUIDs and timestamp
|
||||
*
|
||||
* @param ServerSyncTool $serverSync
|
||||
* @param array $conditions
|
||||
* @return array The list of analyst data
|
||||
* @throws JsonException|HttpSocketHttpException|HttpSocketJsonException
|
||||
*/
|
||||
public function fetchUUIDsFromServer(ServerSyncTool $serverSync, array $conditions = [])
|
||||
{
|
||||
$filterRules = $conditions;
|
||||
$dataArray = $serverSync->fetchIndexMinimal($filterRules)->json();
|
||||
if (isset($dataArray['response'])) {
|
||||
$dataArray = $dataArray['response'];
|
||||
}
|
||||
return $dataArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* filterAnalystDataForPush Send a candidate data to be pushed and returns the list of accepted entries
|
||||
*
|
||||
* @param ServerSyncTool $serverSync
|
||||
* @param array $conditions
|
||||
* @return array The list of analyst data
|
||||
* @throws JsonException|HttpSocketHttpException|HttpSocketJsonException
|
||||
*/
|
||||
public function filterAnalystDataForPush(ServerSyncTool $serverSync, array $candidates = [])
|
||||
{
|
||||
$dataArray = $serverSync->filterAnalystDataForPush($candidates)->json();
|
||||
if (isset($dataArray['response'])) {
|
||||
$dataArray = $dataArray['response'];
|
||||
}
|
||||
return $dataArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of cluster IDs that are present on the remote server and returns clusters that should be pulled
|
||||
*
|
||||
|
@ -1239,6 +1277,20 @@ class Server extends AppModel
|
|||
} else {
|
||||
$successes = array_merge($successes, $sightingSuccesses);
|
||||
}
|
||||
|
||||
if ($push['canPush'] || $push['canEditAnalystData']) {
|
||||
$this->AnalystData = ClassRegistry::init('AnalystData');
|
||||
$analystDataSuccesses = $this->AnalystData->push($user, $serverSync);
|
||||
} else {
|
||||
$analystDataSuccesses = array();
|
||||
}
|
||||
|
||||
if (!isset($successes)) {
|
||||
$successes = $analystDataSuccesses;
|
||||
} else {
|
||||
$successes = array_merge($successes, $analystDataSuccesses);
|
||||
}
|
||||
|
||||
if (!isset($fails)) {
|
||||
$fails = array();
|
||||
}
|
||||
|
@ -2543,33 +2595,33 @@ class Server extends AppModel
|
|||
|
||||
public function getFileRules()
|
||||
{
|
||||
return array(
|
||||
'orgs' => array(
|
||||
return [
|
||||
'orgs' => [
|
||||
'name' => __('Organisation logos'),
|
||||
'description' => __('The logo used by an organisation on the event index, event view, discussions, proposals, etc. Make sure that the filename is in the org.png format, where org is the case-sensitive organisation name.'),
|
||||
'expected' => array(),
|
||||
'expected' => [],
|
||||
'valid_format' => __('48x48 pixel .png files or .svg file'),
|
||||
'path' => APP . 'webroot' . DS . 'img' . DS . 'orgs',
|
||||
'path' => APP . 'files' . DS . 'img' . DS . 'orgs',
|
||||
'regex' => '.*\.(png|svg)$',
|
||||
'regex_error' => __('Filename must be in the following format: *.png or *.svg'),
|
||||
'files' => array(),
|
||||
),
|
||||
'img' => array(
|
||||
'files' => [],
|
||||
],
|
||||
'img' => [
|
||||
'name' => __('Additional image files'),
|
||||
'description' => __('Image files uploaded into this directory can be used for various purposes, such as for the login page logos'),
|
||||
'expected' => array(
|
||||
'expected' => [
|
||||
'MISP.footer_logo' => Configure::read('MISP.footer_logo'),
|
||||
'MISP.home_logo' => Configure::read('MISP.home_logo'),
|
||||
'MISP.welcome_logo' => Configure::read('MISP.welcome_logo'),
|
||||
'MISP.welcome_logo2' => Configure::read('MISP.welcome_logo2'),
|
||||
),
|
||||
],
|
||||
'valid_format' => __('PNG or SVG file'),
|
||||
'path' => APP . 'webroot' . DS . 'img' . DS . 'custom',
|
||||
'path' => APP . 'files' . DS . 'img' . DS . 'custom',
|
||||
'regex' => '.*\.(png|svg)$',
|
||||
'regex_error' => __('Filename must be in the following format: *.png or *.svg'),
|
||||
'files' => array(),
|
||||
),
|
||||
);
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function grabFiles()
|
||||
|
@ -2752,6 +2804,7 @@ class Server extends AppModel
|
|||
$canPush = isset($remoteVersion['perm_sync']) ? $remoteVersion['perm_sync'] : false;
|
||||
$canSight = isset($remoteVersion['perm_sighting']) ? $remoteVersion['perm_sighting'] : false;
|
||||
$canEditGalaxyCluster = isset($remoteVersion['perm_galaxy_editor']) ? $remoteVersion['perm_galaxy_editor'] : false;
|
||||
$canEditAnalystData = isset($remoteVersion['perm_analyst_data']) ? $remoteVersion['perm_analyst_data'] : false;
|
||||
$remoteVersionString = $remoteVersion['version'];
|
||||
$remoteVersion = explode('.', $remoteVersion['version']);
|
||||
if (!isset($remoteVersion[0])) {
|
||||
|
@ -2801,6 +2854,7 @@ class Server extends AppModel
|
|||
'response' => $response,
|
||||
'canPush' => $canPush,
|
||||
'canSight' => $canSight,
|
||||
'canEditAnalystData' => $canEditAnalystData,
|
||||
'canEditGalaxyCluster' => $canEditGalaxyCluster,
|
||||
'version' => $remoteVersion,
|
||||
'protectedMode' => $protectedMode,
|
||||
|
@ -2874,12 +2928,15 @@ class Server extends AppModel
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function redisInfo()
|
||||
{
|
||||
$output = array(
|
||||
$output = [
|
||||
'extensionVersion' => phpversion('redis'),
|
||||
'connection' => false,
|
||||
);
|
||||
];
|
||||
|
||||
try {
|
||||
$redis = RedisTool::init();
|
||||
|
|
|
@ -39,22 +39,21 @@ class Module_attribute_edition_operation extends WorkflowBaseActionModule
|
|||
protected function __saveAttributes(array $attributes, array $rData, array $params, array $user): array
|
||||
{
|
||||
$success = false;
|
||||
$attributes = [];
|
||||
$newAttributes = [];
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
$newAttribute = $this->_editAttribute($attribute, $rData, $params);
|
||||
$newAttributes[] = $newAttribute;
|
||||
unset($newAttribute['timestamp']);
|
||||
$newAttributes[] = $newAttribute;
|
||||
$result = $this->Attribute->editAttribute($newAttribute, $rData, $user, $newAttribute['object_id']);
|
||||
if (is_array($result)) {
|
||||
$attributes[] = $result;
|
||||
}
|
||||
}
|
||||
$this->Attribute->editAttributeBulk($attributes, $rData, $user);
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
$this->Attribute->editAttributeBulk($newAttributes, $rData, $user);
|
||||
foreach ($newAttributes as $k => $attribute) {
|
||||
$saveSuccess = empty($this->Attribute->validationErrors[$k]);
|
||||
if ($saveSuccess) {
|
||||
$rData = $this->_overrideAttribute($attribute, $newAttribute, $rData);
|
||||
$rData = $this->_overrideAttribute($attribute, $attribute, $rData);
|
||||
}
|
||||
$success = $success || !empty($saveSuccess);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ class Module_tag_replacement_generic extends Module_tag_operation
|
|||
public $blocking = false;
|
||||
public $id = 'tag_replacement_generic';
|
||||
public $name = 'Tag Replacement Generic';
|
||||
public $description = 'Toggle or remove the IDS flag on selected attributes.';
|
||||
public $description = 'Attach a tag, or substitue a tag by another';
|
||||
public $icon = 'tags';
|
||||
public $inputs = 1;
|
||||
public $outputs = 1;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# Configure Azure AD to use SIngle SignOn for MISP
|
||||
# Configure Azure AD to use Single Sign-On (SSO) for MISP
|
||||
|
||||
This plugin enables authentication with an Azure Active Directory server. Under the hood it uses oAuth2. There are still a number of rough edges but in general the plugin works.
|
||||
This plugin enables authentication with an Azure Active Directory (now called [Entra-ID](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id)) server. Under the hood it uses oAuth2. There are still a number of rough edges but in general the plugin works.
|
||||
|
||||
It supports verification if a user has the proper ‘MISP AD’ groups. Users should already exist in MISP. Future enhancement could include auto-create users
|
||||
|
||||
## Azure AD — App Registration Configuration
|
||||
|
||||
In Azure, add a new App Registration. Select Web and set the Redirect URI to your MISP server login page `https://misp.yourdomain.com/users/login`. The MISP instance does not need to be publicly accessible if it is reachable by your browser. The redirect URI that you specify here must be the same as used in the MISP configuration.
|
||||
In Azure, add a new App Registration. Select Web and set the Redirect URI to your MISP server login page `https://misp.yourdomain.com/users/login`. The MISP instance does not need to be publicly accessible if it is reachable by your browser. The redirect URI that you specify here must be the same as used in the MISP configuration (including `/users/login`). You can add as many redirect URIs as needed, meaning you can have multiple MISP servers use the same Azure App.
|
||||
|
||||
![AppReg Configuration](.images/Picture29.png)
|
||||
|
||||
On the Overview page of the new MISP App Registration capture the following inforamtion.
|
||||
On the Overview page of the new MISP App Registration capture the following information.
|
||||
|
||||
- [x] Application (client) ID
|
||||
- [x] Directory (tenant) ID
|
||||
|
@ -44,7 +44,7 @@ Create the following groups in Azure AD, these can be called anything you like f
|
|||
Make a name of your groups, we'll need these later.
|
||||
|
||||
- [x] Misp Users
|
||||
- [x] Misp ORG Admins
|
||||
- [x] Misp Org Admins
|
||||
- [x] Misp Site Admins
|
||||
|
||||
## Enable the AAD Plugin for MISP
|
||||
|
@ -122,7 +122,7 @@ Scroll down to near the bottom of the page and add in the following configuratio
|
|||
),
|
||||
```
|
||||
|
||||
Add the information we made a note of earlier when creating the `App Registation` and optionally the Azure AD groups you created.
|
||||
Add the information we made a note of earlier when creating the `App Registration` and optionally the Azure AD groups you created.
|
||||
|
||||
![AadAuth.configuration](.images/Picture38.png)
|
||||
|
||||
|
@ -139,4 +139,12 @@ Additionally, it is recommended to set the following settings in the MISP config
|
|||
* `MISP.disable_user_login_change => true`: Removes the ability of users to change their username (email), except for site admins.
|
||||
* `MISP.disable_user_password_change => true`: Removes the ability of users to change their own password.
|
||||
|
||||
This way users will not be able to change their passwords and by-pass the AAD login flow.
|
||||
This way users will not be able to change their passwords and by-pass the AAD login flow.
|
||||
|
||||
# Create users via the MISP REST API
|
||||
|
||||
Because users already need to exist in MISP before they can authenticate with AAD it can be useful to provision them in an automated fashion. This can be done by creating the users via the MISP REST API. The below `curl` command provides an example on how to do this. Note that you need an API key.
|
||||
|
||||
```
|
||||
curl -k -d '{"email":"newuser@mycompany.com", "role_id":"3", "org_id":"1", "enable_password":"1", "change_pw":"0"}' -H "Authorization: API_KEY" -H "Accept: application/json" -H "Content-type: application/json" -X POST htps://misp.mycompany.com/admin/users/add
|
||||
```
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
<?php
|
||||
$edit = $this->request->params['action'] === 'edit' ? true : false;
|
||||
$fields = [
|
||||
[
|
||||
'field' => 'object_type',
|
||||
'class' => 'span2',
|
||||
'disabled' => !empty($this->data[$modelSelection]['object_type']),
|
||||
'default' => empty($this->data[$modelSelection]['object_type']) ? null : $this->data[$modelSelection]['object_type'],
|
||||
'options' => $dropdownData['valid_targets'],
|
||||
'type' => 'dropdown',
|
||||
'stayInLine' => 1
|
||||
],
|
||||
[
|
||||
'field' => 'object_uuid',
|
||||
'class' => 'span4',
|
||||
'disabled' => !empty($this->data[$modelSelection]['object_uuid']),
|
||||
'default' => empty($this->data[$modelSelection]['object_uuid']) ? null : $this->data[$modelSelection]['object_uuid']
|
||||
],
|
||||
[
|
||||
'field' => 'distribution',
|
||||
'class' => 'input',
|
||||
'options' => $dropdownData['distributionLevels'],
|
||||
'default' => isset($attribute['Attribute']['distribution']) ? $attribute['Attribute']['distribution'] : $initialDistribution,
|
||||
'stayInLine' => 1,
|
||||
'type' => 'dropdown'
|
||||
],
|
||||
[
|
||||
'field' => 'sharing_group_id',
|
||||
'class' => 'input',
|
||||
'options' => $dropdownData['sgs'],
|
||||
'label' => __("Sharing Group"),
|
||||
'type' => 'dropdown'
|
||||
],
|
||||
[
|
||||
'field' => 'authors',
|
||||
'class' => 'span3',
|
||||
'stayInLine' => $modelSelection === 'Note',
|
||||
'default' => $me['email'],
|
||||
],
|
||||
];
|
||||
|
||||
if ($modelSelection === 'Note') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'field' => 'language',
|
||||
'class' => 'span3',
|
||||
'options' => $languageRFC5646,
|
||||
'type' => 'dropdown',
|
||||
],
|
||||
[
|
||||
'field' => 'note',
|
||||
'type' => 'textarea',
|
||||
'class' => 'input span6',
|
||||
]
|
||||
]
|
||||
);
|
||||
} else if ($modelSelection === 'Opinion') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'field' => 'opinion',
|
||||
'class' => '',
|
||||
'type' => 'opinion',
|
||||
],
|
||||
[
|
||||
'field' => 'comment',
|
||||
'type' => 'textarea',
|
||||
'class' => 'input span6',
|
||||
]
|
||||
]
|
||||
);
|
||||
} else if ($modelSelection === 'Relationship') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'field' => 'relationship_type',
|
||||
'class' => 'span4',
|
||||
'options' => $existingRelations,
|
||||
'type' => 'text',
|
||||
'picker' => array(
|
||||
'text' => __('Pick Relationship'),
|
||||
'function' => 'pickerRelationshipTypes',
|
||||
)
|
||||
],
|
||||
[
|
||||
'field' => 'related_object_type',
|
||||
'class' => 'span2',
|
||||
'options' => $dropdownData['valid_targets'],
|
||||
'type' => 'dropdown',
|
||||
'stayInLine' => 1,
|
||||
'disabled' => !empty($this->data[$modelSelection]['related_object_type']),
|
||||
],
|
||||
[
|
||||
'field' => 'related_object_uuid',
|
||||
'class' => 'span4',
|
||||
'disabled' => !empty($this->data[$modelSelection]['related_object_uuid']),
|
||||
],
|
||||
sprintf('<div style="max-width: 960px;"><label>%s:</label><div id="related-object-container">%s</div></div>', __('Related Object'), __('- No UUID provided -'))
|
||||
]
|
||||
);
|
||||
}
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => false,
|
||||
'model' => $modelSelection,
|
||||
'title' => $edit ? __('Edit %s', $modelSelection) : __('Add new %s', $modelSelection),
|
||||
'fields' => $fields,
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
'ajaxSubmit' => 'submitGenericFormInPlace(analystDataSubmitSuccess, true);'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
if (!$ajax) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
|
||||
}
|
||||
?>
|
||||
|
||||
<script>
|
||||
function analystDataSubmitSuccess(data) {
|
||||
<?php if ($edit): ?>
|
||||
replaceNoteInUI(data)
|
||||
<?php else: ?>
|
||||
addNoteInUI(data)
|
||||
<?php endif; ?>
|
||||
}
|
||||
|
||||
function replaceNoteInUI(data) {
|
||||
var noteType = Object.keys(data)[0]
|
||||
var noteHTMLID = '#' + data[noteType].note_type_name + '-' + data[noteType].id
|
||||
var $noteToReplace = $(noteHTMLID)
|
||||
if ($noteToReplace.length == 1) {
|
||||
var relatedObjects = {}
|
||||
if (noteType == 'Relationship') {
|
||||
var relationship = data[noteType]
|
||||
relatedObjects[relationship['object_type']] = {}
|
||||
relatedObjects[relationship['object_type']][relationship['related_object_uuid']] = relationship['related_object'][relationship['object_type']]
|
||||
}
|
||||
var compiledUpdatedNote = renderNote(data[noteType], relatedObjects)
|
||||
$noteToReplace[0].outerHTML = compiledUpdatedNote
|
||||
$(noteHTMLID).css({'opacity': 0})
|
||||
setTimeout(() => {
|
||||
$(noteHTMLID).css({'opacity': 1})
|
||||
}, 750);
|
||||
}
|
||||
}
|
||||
|
||||
function addNoteInUI(data) {
|
||||
location.reload()
|
||||
}
|
||||
|
||||
function displayRelatedObject(data) {
|
||||
if (Object.keys(data).length == 0) {
|
||||
$('#related-object-container').html('<span class="text-muted"><?= __('Could not fetch remote object or fetching not supported yet.') ?></span>')
|
||||
} else {
|
||||
var parsed = syntaxHighlightJson(data)
|
||||
$('#related-object-container').html(parsed)
|
||||
}
|
||||
}
|
||||
|
||||
function fetchAndDisplayRelatedObject(type, uuid) {
|
||||
if (!type || !uuid) {
|
||||
return
|
||||
}
|
||||
var url = baseurl + '/analystData/getRelatedElement/' + type + '/' + uuid
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: url,
|
||||
headers: { Accept: "application/json" },
|
||||
success: function (data) {
|
||||
displayRelatedObject(data)
|
||||
},
|
||||
error: function (data, textStatus, errorThrown) {
|
||||
showMessage('fail', textStatus + ": " + errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#<?= h($modelSelection) ?>Distribution').change(function() {
|
||||
checkSharingGroup('<?= h($modelSelection) ?>');
|
||||
});
|
||||
checkSharingGroup('<?= h($modelSelection) ?>');
|
||||
|
||||
$('#RelationshipRelatedObjectType').change(function(e) {
|
||||
if ($('#RelationshipRelatedObjectUuid').val().length == 36) {
|
||||
fetchAndDisplayRelatedObject($('#RelationshipRelatedObjectType').val(),$('#RelationshipRelatedObjectUuid').val())
|
||||
}
|
||||
})
|
||||
$('#RelationshipRelatedObjectUuid').on('input', function(e) {
|
||||
if ($('#RelationshipRelatedObjectUuid').val().length == 36) {
|
||||
fetchAndDisplayRelatedObject($('#RelationshipRelatedObjectType').val(),$('#RelationshipRelatedObjectUuid').val())
|
||||
}
|
||||
})
|
||||
fetchAndDisplayRelatedObject($('#RelationshipRelatedObjectType').val(),$('#RelationshipRelatedObjectUuid').val())
|
||||
})
|
||||
|
||||
<?php if ($modelSelection === 'Relationship'): ?>
|
||||
function pickerRelationshipTypes() {
|
||||
var existingRelationTypes = <?= json_encode(array_values($existingRelations)) ?> ;
|
||||
var $select = $('<select id="pickerRelationshipTypeSelect"/>');
|
||||
existingRelationTypes.forEach(function(type) {
|
||||
$select.append($('<option/>').val(type).text(type))
|
||||
})
|
||||
var html = '<div>' + $select[0].outerHTML + '</div>';
|
||||
var that = this
|
||||
openPopover(this, html, false, 'right', function($popover) {
|
||||
$popover.css('z-index', 1060)
|
||||
$popover.find('select').chosen({
|
||||
width: '300px',
|
||||
}).on('change', function(evt, param) {
|
||||
$('#RelationshipRelationshipType').val($('#pickerRelationshipTypeSelect').val());
|
||||
$(that).popover('hide')
|
||||
});
|
||||
});
|
||||
}
|
||||
<?php endif; ?>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#related-object-container {
|
||||
box-shadow: 0 0 5px 0px #22222266;
|
||||
padding: 0.5rem;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
$fields = [
|
||||
[
|
||||
'name' => __('Id'),
|
||||
'sort' => $modelSelection . '.id',
|
||||
'data_path' => $modelSelection . '.id'
|
||||
],
|
||||
[
|
||||
'name' => __('OrgC'),
|
||||
'element' => 'org',
|
||||
'data_path' => $modelSelection . '.Orgc'
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'data_path' => $modelSelection . '.uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Parent Object Type'),
|
||||
'sort' => $modelSelection . '.object_type',
|
||||
'data_path' => $modelSelection . '.object_type'
|
||||
],
|
||||
[
|
||||
'name' => __('Target Object'),
|
||||
'sort' => $modelSelection . '.object_type',
|
||||
'data_path' => $modelSelection . '.object_uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Creator org'),
|
||||
'data_path' => $modelSelection . '.orgc_uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Created'),
|
||||
'sort' => $modelSelection . '.created',
|
||||
'data_path' => $modelSelection . '.created'
|
||||
],
|
||||
[
|
||||
'name' => __('Modified'),
|
||||
'sort' => $modelSelection . '.modified',
|
||||
'data_path' => $modelSelection . '.modified'
|
||||
],
|
||||
[
|
||||
'name' => __('Distribution'),
|
||||
'element' => 'distribution_levels',
|
||||
'sort' => $modelSelection . '.distribution',
|
||||
'class' => 'short',
|
||||
'data_path' => $modelSelection . '.distribution',
|
||||
'sg_path' => $modelSelection . '.SharingGroup',
|
||||
]
|
||||
];
|
||||
|
||||
if ($modelSelection === 'Note') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'name' => __('Language'),
|
||||
'sort' => $modelSelection . '.language',
|
||||
'data_path' => $modelSelection . '.language'
|
||||
],
|
||||
[
|
||||
'name' => __('Note'),
|
||||
'sort' => $modelSelection . '.note',
|
||||
'data_path' => $modelSelection . '.note'
|
||||
]
|
||||
]
|
||||
);
|
||||
} else if ($modelSelection === 'Opinion') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'name' => __('Comment'),
|
||||
'data_path' => $modelSelection . '.comment'
|
||||
],
|
||||
[
|
||||
'name' => __('Opinion'),
|
||||
'data_path' => $modelSelection . '.opinion',
|
||||
'element' => 'opinion_scale',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
} else if ($modelSelection === 'Relationship') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'name' => __('Related Object'),
|
||||
'element' => 'custom',
|
||||
'function' => function (array $row) use ($baseurl, $modelSelection) {
|
||||
$path = Inflector::pluralize(strtolower($row[$modelSelection]['related_object_type']));
|
||||
return sprintf(
|
||||
'<span class="bold">%s</span>: <a href="%s/%s/view/%s">%s</a>',
|
||||
h($row[$modelSelection]['related_object_type']),
|
||||
h($baseurl),
|
||||
h($path),
|
||||
h($row[$modelSelection]['related_object_uuid']),
|
||||
h($row[$modelSelection]['related_object_uuid'])
|
||||
);
|
||||
}
|
||||
],
|
||||
[
|
||||
'name' => __('Relationship_type'),
|
||||
'sort' => $modelSelection . '.relationship_type',
|
||||
'data_path' => $modelSelection . '.relationship_type'
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
echo $this->element('genericElements/IndexTable/scaffold', [
|
||||
'scaffold_data' => [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'pull' => 'right',
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
[
|
||||
'active' => $modelSelection === 'Note',
|
||||
'url' => sprintf('%s/analyst_data/index/Note', $baseurl),
|
||||
'text' => __('Note'),
|
||||
],
|
||||
[
|
||||
'active' => $modelSelection === 'Opinion',
|
||||
'class' => 'defaultContext',
|
||||
'url' => sprintf('%s/analyst_data/index/Opinion', $baseurl),
|
||||
'text' => __('Opinion'),
|
||||
],
|
||||
[
|
||||
'active' => $modelSelection === 'Relationship',
|
||||
'url' => sprintf('%s/analyst_data/index/Relationship', $baseurl),
|
||||
'text' => __('Relationship'),
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'quickFilter'
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => $fields,
|
||||
'title' => empty($ajax) ? __('%s index', Inflector::pluralize($modelSelection)) : false,
|
||||
'actions' => [
|
||||
[
|
||||
'url' => $baseurl . '/analystData/view/' . $modelSelection,
|
||||
'url_params_data_paths' => [$modelSelection . '.id'],
|
||||
'icon' => 'eye',
|
||||
'dbclickAction' => true,
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/analystData/edit/' . $modelSelection . '/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => $modelSelection . '.id',
|
||||
'title' => __('Edit %s', $modelSelection),
|
||||
'icon' => 'edit',
|
||||
'complex_requirement' => function($item) use ($modelSelection) {
|
||||
return !empty($item[$modelSelection]['_canEdit']);
|
||||
}
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/analystData/delete/' . $modelSelection . '/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => $modelSelection . '.id',
|
||||
'icon' => 'trash',
|
||||
'complex_requirement' => function($item) use ($modelSelection) {
|
||||
return !empty($item[$modelSelection]['_canEdit']);
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
?>
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
$fields = [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => $modelSelection . '.id'
|
||||
],
|
||||
[
|
||||
'key' => 'UUID',
|
||||
'path' => $modelSelection . '.uuid',
|
||||
'class' => '',
|
||||
'type' => 'uuid',
|
||||
'object_type' => $modelSelection,
|
||||
'notes_path' => $modelSelection . '.Note',
|
||||
'opinions_path' => $modelSelection . '.Opinion',
|
||||
'relationships_path' => $modelSelection . '.Relationship',
|
||||
],
|
||||
[
|
||||
'key' => __('Note Type'),
|
||||
'path' => $modelSelection . '.note_type_name'
|
||||
],
|
||||
[
|
||||
'key' => __('Target Object'),
|
||||
'type' => 'custom',
|
||||
'function' => function (array $row) use ($baseurl, $modelSelection) {
|
||||
$path = Inflector::pluralize(strtolower($row[$modelSelection]['object_type']));
|
||||
return sprintf(
|
||||
'<span class="bold">%s</span>: <a href="%s/%s/view/%s">%s</a>',
|
||||
h($row[$modelSelection]['object_type']),
|
||||
h($baseurl),
|
||||
h($path),
|
||||
h($row[$modelSelection]['object_uuid']),
|
||||
h($row[$modelSelection]['object_uuid'])
|
||||
);
|
||||
}
|
||||
],
|
||||
[
|
||||
'key' => __('Creator org'),
|
||||
'path' => $modelSelection . '.Orgc',
|
||||
'pathName' => $modelSelection . '.orgc_uuid',
|
||||
'type' => 'org',
|
||||
'model' => 'organisations'
|
||||
],
|
||||
[
|
||||
'key' => __('Created'),
|
||||
'path' => $modelSelection . '.created'
|
||||
],
|
||||
[
|
||||
'key' => __('Modified'),
|
||||
'path' => $modelSelection . '.modified'
|
||||
],
|
||||
[
|
||||
'key' => __('Distribution'),
|
||||
'path' => $modelSelection . '.distribution',
|
||||
'event_id_path' => $modelSelection . '.id',
|
||||
'disable_distribution_graph' => true,
|
||||
'sg_path' => $modelSelection . '.SharingGroup',
|
||||
'type' => 'distribution'
|
||||
],
|
||||
[
|
||||
'key' => __('Authors'),
|
||||
'path' => $modelSelection . '.authors'
|
||||
],
|
||||
];
|
||||
|
||||
if ($modelSelection === 'Note') {
|
||||
$fields[] = [
|
||||
'key' => __('Language'),
|
||||
'path' => $modelSelection . '.language'
|
||||
];
|
||||
$fields[] = [
|
||||
'key' => __('Note'),
|
||||
'path' => $modelSelection . '.note'
|
||||
];
|
||||
} else if ($modelSelection === 'Opinion') {
|
||||
$fields[] = [
|
||||
'key' => __('Comment'),
|
||||
'path' => $modelSelection . '.comment'
|
||||
];
|
||||
$fields[] = [
|
||||
'key' => __('Opinion'),
|
||||
'path' => $modelSelection . '.opinion',
|
||||
'type' => 'opinion_scale',
|
||||
];
|
||||
} else if ($modelSelection === 'Relationship') {
|
||||
$fields[] = [
|
||||
'key' => __('Related Object'),
|
||||
'type' => 'custom',
|
||||
'function' => function (array $row) use ($baseurl, $modelSelection) {
|
||||
$path = Inflector::pluralize(strtolower($row[$modelSelection]['related_object_type']));
|
||||
return sprintf(
|
||||
'<span class="bold">%s</span>: <a href="%s/%s/view/%s">%s</a>',
|
||||
h($row[$modelSelection]['related_object_type']),
|
||||
h($baseurl),
|
||||
h($path),
|
||||
h($row[$modelSelection]['related_object_uuid']),
|
||||
h($row[$modelSelection]['related_object_uuid'])
|
||||
);
|
||||
}
|
||||
];
|
||||
$fields[] = [
|
||||
'key' => __('Relationship_type'),
|
||||
'path' => $modelSelection . '.relationship_type'
|
||||
];
|
||||
}
|
||||
|
||||
echo $this->element(
|
||||
'genericElements/SingleViews/single_view',
|
||||
[
|
||||
'title' => __('%s view', h($modelSelection)),
|
||||
'data' => $data,
|
||||
'fields' => $fields,
|
||||
'side_panels' => [
|
||||
[
|
||||
'type' => 'html',
|
||||
'html' => '<div id="analyst_data_thread" class="panel-container"></div>',
|
||||
]
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$object_uuid = Hash::get($data, $modelSelection . '.uuid');
|
||||
$options = [
|
||||
'container_id' => 'analyst_data_thread',
|
||||
'object_type' => $modelSelection,
|
||||
'object_uuid' => $object_uuid,
|
||||
'shortDist' => $shortDist,
|
||||
'notes' => $data[$modelSelection]['Note'] ?? [],
|
||||
'opinions' => $data[$modelSelection]['Opinion'] ?? [],
|
||||
'relationships' => $data[$modelSelection]['Relationship'] ?? [],
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/assetLoader', [
|
||||
'js' => ['doT', 'moment.min'],
|
||||
'css' => ['analyst-data',],
|
||||
]);
|
||||
echo $this->element('genericElements/Analyst_data/thread', $options);
|
||||
?>
|
||||
|
||||
<?php if ($modelSelection == 'Relationship') : ?>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#analyst_data_thread').find('li > a[href^="#relationship"]').tab('show')
|
||||
})
|
||||
</script>
|
||||
<?php endif; ?>
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
$fieldDesc = array();
|
||||
$fieldDesc['uuids'] = __('Enter a single or a list of UUIDs');
|
||||
$fieldDesc['analyst_data_orgc'] = __('(Optional) The organisation that the event is associated with');
|
||||
$fieldDesc['analyst_data_info'] = __('(Optional) The analyst data value that you would like to block');
|
||||
$fieldDesc['comment'] = __('(Optional) Any comments you would like to add regarding this (or these) entries');
|
||||
echo $this->element('genericElements/Form/genericForm', array(
|
||||
'form' => $this->Form,
|
||||
'data' => array(
|
||||
'model' => 'AnalystDataBlocklist',
|
||||
'title' => $action == 'add' ? __('Add block entry for Analyst Data') : __('Edit block entry for Analyst Data'),
|
||||
'fields' => array(
|
||||
array(
|
||||
'disabled' => $action != 'add' ? 'disabled' : '',
|
||||
'field' => 'uuids',
|
||||
'class' => 'span6',
|
||||
'label' => __('Analyst Data UUID'),
|
||||
'type' => 'textarea',
|
||||
'default' => isset($blockEntry['AnalystDataBlocklist']['analyst_data_uuid']) ? $blockEntry['AnalystDataBlocklist']['analyst_data_uuid'] : '',
|
||||
),
|
||||
array(
|
||||
'field' => 'analyst_data_orgc',
|
||||
'label' => __('Creating organisation'),
|
||||
'class' => 'span6',
|
||||
'type' => 'text',
|
||||
'default' => isset($blockEntry['AnalystDataBlocklist']['analyst_data_orgc']) ? $blockEntry['AnalystDataBlocklist']['analyst_data_orgc'] : ''
|
||||
),
|
||||
array(
|
||||
'field' => 'analyst_data_info',
|
||||
'label' => __('Analyst Data value'),
|
||||
'class' => 'span6',
|
||||
'type' => 'text',
|
||||
'default' => isset($blockEntry['AnalystDataBlocklist']['analyst_data_info']) ? $blockEntry['AnalystDataBlocklist']['analyst_data_info'] : ''
|
||||
),
|
||||
array(
|
||||
'field' => 'comment',
|
||||
'label' => __('Comment'),
|
||||
'class' => 'span6',
|
||||
'type' => 'text',
|
||||
'default' => isset($blockEntry['AnalystDataBlocklist']['comment']) ? $blockEntry['AnalystDataBlocklist']['comment'] : ''
|
||||
),
|
||||
),
|
||||
'submit' => array(
|
||||
'ajaxSubmit' => ''
|
||||
)
|
||||
),
|
||||
'fieldDesc' => $fieldDesc
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'analyst_data', 'menuItem' => 'index_blocklist'));
|
||||
?>
|
||||
|
||||
<?php echo $this->Js->writeBuffer(); // Write cached scripts
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
echo '<div class="index">';
|
||||
echo $this->element('/genericElements/IndexTable/index_table', array(
|
||||
'data' => array(
|
||||
'data' => $response,
|
||||
'top_bar' => array(
|
||||
'children' => array(
|
||||
array(
|
||||
'type' => 'simple',
|
||||
'children' => array(
|
||||
array(
|
||||
'url' => sprintf('%s/analyst_data_blocklists/add/', $baseurl),
|
||||
'text' => __('+ Add entry to blocklist'),
|
||||
),
|
||||
)
|
||||
),
|
||||
array(
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'searchall'
|
||||
)
|
||||
)
|
||||
),
|
||||
'fields' => array(
|
||||
array(
|
||||
'name' => __('Id'),
|
||||
'sort' => 'id',
|
||||
'class' => 'short',
|
||||
'data_path' => 'AnalystDataBlocklist.id',
|
||||
),
|
||||
array(
|
||||
'name' => __('Org'),
|
||||
'class' => 'short',
|
||||
'data_path' => 'AnalystDataBlocklist.analyst_data_orgc',
|
||||
),
|
||||
array(
|
||||
'name' => __('Analyst Data UUID'),
|
||||
'class' => 'short',
|
||||
'data_path' => 'AnalystDataBlocklist.analyst_data_uuid',
|
||||
),
|
||||
array(
|
||||
'name' => __('Created'),
|
||||
'sort' => 'created',
|
||||
'class' => 'short',
|
||||
'data_path' => 'AnalystDataBlocklist.created',
|
||||
),
|
||||
array(
|
||||
'name' => __('Analyst Data value'),
|
||||
'sort' => 'value',
|
||||
'class' => 'short',
|
||||
'data_path' => 'AnalystDataBlocklist.analyst_data_info',
|
||||
),
|
||||
array(
|
||||
'name' => __('Comment'),
|
||||
'sort' => 'comment',
|
||||
'class' => 'short',
|
||||
'data_path' => 'AnalystDataBlocklist.comment',
|
||||
),
|
||||
),
|
||||
'title' => __('Analyst Data Blocklist Index'),
|
||||
'description' => __('List all analyst data that will be prevented to be created (also via synchronization) on this instance'),
|
||||
'actions' => array(
|
||||
array(
|
||||
'title' => 'Delete',
|
||||
'url' => $baseurl . '/analyst_data_blocklists/delete',
|
||||
'url_params_data_paths' => array(
|
||||
'AnalystDataBlocklist.id'
|
||||
),
|
||||
'postLink' => true,
|
||||
'postLinkConfirm' => __('Are you sure you want to delete the entry?'),
|
||||
'icon' => 'trash'
|
||||
),
|
||||
)
|
||||
)
|
||||
));
|
||||
echo '</div>';
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'analyst_data', 'menuItem' => 'index_blocklist'));
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var passedArgsArray = <?php echo $passedArgs; ?>;
|
||||
if (passedArgsArray['context'] === undefined) {
|
||||
passedArgsArray['context'] = 'pending';
|
||||
}
|
||||
$(document).ready(function() {
|
||||
$('#quickFilterButton').click(function() {
|
||||
runIndexQuickFilter('/context:' + passedArgsArray['context']);
|
||||
});
|
||||
$('#quickFilterField').on('keypress', function (e) {
|
||||
if(e.which === 13) {
|
||||
runIndexQuickFilter('/context:' + passedArgsArray['context']);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
$edit = $this->request->params['action'] === 'edit' ? true : false;
|
||||
$fields = [
|
||||
[
|
||||
'field' => 'element_uuid',
|
||||
'class' => 'input span6',
|
||||
'onChange' => 'alert(1);'
|
||||
],
|
||||
[
|
||||
'field' => 'element_type',
|
||||
'class' => 'input span6',
|
||||
'options' => $dropdownData['types'],
|
||||
'type' => 'dropdown'
|
||||
],
|
||||
[
|
||||
'field' => 'description',
|
||||
'class' => 'span6',
|
||||
'type' => 'textarea'
|
||||
]
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => null,
|
||||
'model' => 'CollectionElement',
|
||||
'title' => __('Add element to Collection'),
|
||||
'fields' => $fields,
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
'ajaxSubmit' => 'submitGenericFormInPlace();'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
if (!$ajax) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
$fields = [
|
||||
[
|
||||
'field' => 'collection_id',
|
||||
'class' => 'input span6',
|
||||
'options' => $dropdownData['collections'],
|
||||
'type' => 'dropdown'
|
||||
],
|
||||
[
|
||||
'field' => 'description',
|
||||
'class' => 'span6',
|
||||
'type' => 'textarea'
|
||||
]
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => null,
|
||||
'model' => 'CollectionElement',
|
||||
'title' => __('Add element to Collection'),
|
||||
'fields' => $fields,
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
//'ajaxSubmit' => 'submitGenericFormInPlace();'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
$fields = [
|
||||
[
|
||||
'name' => __('Id'),
|
||||
'sort' => 'CollectionElement.id',
|
||||
'data_path' => 'CollectionElement.id'
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'data_path' => 'CollectionElement.uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Element'),
|
||||
'sort' => 'CollectionElement.element_type',
|
||||
'element' => 'model',
|
||||
'model_name' => 'CollectionElement.element_type',
|
||||
'model_id' => 'CollectionElement.element_uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Element type'),
|
||||
'data_path' => 'CollectionElement.element_type'
|
||||
],
|
||||
[
|
||||
'name' => __('Description'),
|
||||
'data_path' => 'CollectionElement.description'
|
||||
]
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/IndexTable/scaffold', [
|
||||
'scaffold_data' => [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'pull' => 'right',
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'quickFilter'
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => $fields,
|
||||
'title' => empty($ajax) ? __('Collection element index') : false,
|
||||
'actions' => [
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/collectionElements/delete/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => 'CollectionElement.id',
|
||||
'icon' => 'trash'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
?>
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
$edit = $this->request->params['action'] === 'edit' ? true : false;
|
||||
$fields = [
|
||||
[
|
||||
'field' => 'name',
|
||||
'class' => 'span6'
|
||||
],
|
||||
[
|
||||
'field' => 'type',
|
||||
'class' => 'input span6',
|
||||
'options' => $dropdownData['types'],
|
||||
'type' => 'dropdown'
|
||||
],
|
||||
[
|
||||
'field' => 'description',
|
||||
'class' => 'span6',
|
||||
'type' => 'textarea'
|
||||
],
|
||||
[
|
||||
'field' => 'distribution',
|
||||
'class' => 'input',
|
||||
'options' => $dropdownData['distributionLevels'],
|
||||
'default' => isset($data['Collection']['distribution']) ? $data['Collection']['distribution'] : $initialDistribution,
|
||||
'stayInLine' => 1,
|
||||
'type' => 'dropdown'
|
||||
],
|
||||
[
|
||||
'field' => 'sharing_group_id',
|
||||
'class' => 'input',
|
||||
'options' => $dropdownData['sgs'],
|
||||
'label' => __("Sharing Group"),
|
||||
'type' => 'dropdown'
|
||||
]
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => __('Create collections to organise data shared by the community into buckets based on commonalities or as part of your research process. Collections are first class citizens and adhere to the same sharing rules as for example events do.'),
|
||||
'model' => 'Collection',
|
||||
'title' => $edit ? __('Edit collection') : __('Add new collection'),
|
||||
'fields' => $fields,
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
'ajaxSubmit' => 'submitGenericFormInPlace();'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
if (!$ajax) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
$fields = [
|
||||
[
|
||||
'name' => __('Id'),
|
||||
'sort' => 'Collection.id',
|
||||
'data_path' => 'Collection.id'
|
||||
],
|
||||
[
|
||||
'name' => __('Name'),
|
||||
'sort' => 'Collection.name',
|
||||
'data_path' => 'Collection.name'
|
||||
],
|
||||
[
|
||||
'name' => __('Organisation'),
|
||||
'sort' => 'Orgc.name',
|
||||
'data_path' => 'Orgc',
|
||||
'element' => 'org'
|
||||
],
|
||||
[
|
||||
'name' => __('Elements'),
|
||||
'sort' => 'Collection.element_count',
|
||||
'data_path' => 'Collection.element_count'
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'data_path' => 'Collection.uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Type'),
|
||||
'data_path' => 'Collection.type'
|
||||
],
|
||||
[
|
||||
'name' => __('Created'),
|
||||
'sort' => 'Collection.created',
|
||||
'data_path' => 'Collection.created'
|
||||
],
|
||||
[
|
||||
'name' => __('Modified'),
|
||||
'sort' => 'Collection.modified',
|
||||
'data_path' => 'Collection.modified'
|
||||
],
|
||||
[
|
||||
'name' => __('Distribution'),
|
||||
'sort' => 'distribution',
|
||||
'data_path' => 'Collection.distribution',
|
||||
'element' => 'distribution_levels'
|
||||
],
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/IndexTable/scaffold', [
|
||||
'scaffold_data' => [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'pull' => 'right',
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'quickFilter'
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => $fields,
|
||||
'title' => empty($ajax) ? __('Collections index') : false,
|
||||
'actions' => [
|
||||
[
|
||||
'url' => $baseurl . '/collections/view',
|
||||
'url_params_data_paths' => ['Collection.id'],
|
||||
'icon' => 'eye'
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/collections/edit/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => 'Collection.id',
|
||||
'title' => __('Edit Collection'),
|
||||
'icon' => 'edit'
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/collections/delete/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => 'Collection.id',
|
||||
'icon' => 'trash'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
?>
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
echo $this->element(
|
||||
'genericElements/SingleViews/single_view',
|
||||
[
|
||||
'title' => __('Collection view'),
|
||||
'data' => $data,
|
||||
'fields' => [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'Collection.id'
|
||||
],
|
||||
[
|
||||
'key' => __('UUID'),
|
||||
'path' => 'Collection.uuid'
|
||||
],
|
||||
[
|
||||
'key' => __('Creator org'),
|
||||
'path' => 'Collection.orgc_id',
|
||||
'pathName' => 'Collection.Orgc.name',
|
||||
'type' => 'model',
|
||||
'model' => 'organisations'
|
||||
],
|
||||
[
|
||||
'key' => __('Owner org'),
|
||||
'path' => 'Collection.org_id',
|
||||
'pathName' => 'Collection.Org.name',
|
||||
'type' => 'model',
|
||||
'model' => 'organisations'
|
||||
],
|
||||
[
|
||||
'key' => __('Created'),
|
||||
'path' => 'Collection.created'
|
||||
],
|
||||
[
|
||||
'key' => __('Modified'),
|
||||
'path' => 'Collection.modified'
|
||||
],
|
||||
[
|
||||
'key' => __('Name'),
|
||||
'path' => 'Collection.name'
|
||||
],
|
||||
[
|
||||
'key' => __('Description'),
|
||||
'path' => 'Collection.description'
|
||||
],
|
||||
[
|
||||
'key' => __('Distribution'),
|
||||
'path' => 'Collection.distribution',
|
||||
'event_id_path' => 'Collection.id',
|
||||
'disable_distribution_graph' => true,
|
||||
'sg_path' => 'Collection.sharing_group_id',
|
||||
'type' => 'distribution'
|
||||
]
|
||||
],
|
||||
'children' => [
|
||||
[
|
||||
'url' => '/collectionElements/index/{{0}}/',
|
||||
'url_params' => ['Collection.id'],
|
||||
'title' => __('Collection elements'),
|
||||
'elementId' => 'preview_elements_container'
|
||||
]
|
||||
]
|
||||
]
|
||||
);
|
|
@ -68,11 +68,37 @@
|
|||
</td>
|
||||
<?php endif; ?>
|
||||
<td class="short context hidden"><?= $objectId ?></td>
|
||||
<td class="short context hidden uuid quickSelect"><?php echo h($object['uuid']); ?></td>
|
||||
<td class="short context hidden uuid">
|
||||
<span class="quickSelect"><?php echo h($object['uuid']); ?></span>
|
||||
<?php
|
||||
$notes = !empty($object['Note']) ? $object['Note'] : [];
|
||||
$opinions = !empty($object['Opinion']) ? $object['Opinion'] : [];
|
||||
$relationships = !empty($object['Relationship']) ? $object['Relationship'] : [];
|
||||
echo $this->element('genericElements/Analyst_data/generic', [
|
||||
'analyst_data' => ['notes' => $notes, 'opinions' => $opinions, 'relationships' => $relationships],
|
||||
'object_uuid' => $object['uuid'],
|
||||
'object_type' => 'Attribute'
|
||||
]);
|
||||
?>
|
||||
</td>
|
||||
<td class="short context hidden">
|
||||
<?php echo $this->element('/Events/View/seen_field', array('object' => $object)); ?>
|
||||
</td>
|
||||
<td class="short timestamp <?= $isNew ? 'bold red' : '' ?>" <?= $isNew ? 'title="' . __('Element or modification to an existing element has not been published yet.') . '"' : '' ?>><?= $this->Time->date($object['timestamp']) . ($isNew ? '*' : '') ?></td>
|
||||
<td class="short context">
|
||||
<?php
|
||||
$notes = !empty($object['Note']) ? $object['Note'] : [];
|
||||
$opinions = !empty($object['Opinion']) ? $object['Opinion'] : [];
|
||||
$relationships = !empty($object['Relationship']) ? $object['Relationship'] : [];
|
||||
echo $this->element('genericElements/shortUuidWithNotes', [
|
||||
'uuid' => $object['uuid'],
|
||||
'object_type' => 'Attribute',
|
||||
'notes' => $notes,
|
||||
'opinions' => $opinions,
|
||||
'relationships' => $relationships,
|
||||
]);
|
||||
?>
|
||||
</td>
|
||||
<?php
|
||||
if (!empty($extended)):
|
||||
?>
|
||||
|
|
|
@ -30,11 +30,37 @@ $objectId = intval($object['id']);
|
|||
endif;
|
||||
?>
|
||||
<td class="short context hidden"><?= $objectId ?></td>
|
||||
<td class="short context hidden uuid quickSelect"><?php echo h($object['uuid']); ?></td>
|
||||
<td class="short context hidden uuid">
|
||||
<span class="quickSelect"><?php echo h($object['uuid']); ?></span>
|
||||
<?php
|
||||
$notes = !empty($object['Note']) ? $object['Note'] : [];
|
||||
$opinions = !empty($object['Opinion']) ? $object['Opinion'] : [];
|
||||
$relationships = !empty($object['Relationship']) ? $object['Relationship'] : [];
|
||||
echo $this->element('genericElements/Analyst_data/generic', [
|
||||
'analyst_data' => ['notes' => $notes, 'opinions' => $opinions, 'relationships' => $relationships],
|
||||
'object_uuid' => $object['uuid'],
|
||||
'object_type' => 'Attribute'
|
||||
]);
|
||||
?>
|
||||
</td>
|
||||
<td class="short context hidden">
|
||||
<?php echo $this->element('/Events/View/seen_field', array('object' => $object)); ?>
|
||||
</td>
|
||||
<td class="short timestamp <?= $isNew ? 'bold red' : '' ?>" <?= $isNew ? 'title="' . __('Element or modification to an existing element has not been published yet.') . '"' : '' ?>><?= $this->Time->date($object['timestamp']) . ($isNew ? '*' : '') ?></td>
|
||||
<td class="short context">
|
||||
<?php
|
||||
$notes = !empty($object['Note']) ? $object['Note'] : [];
|
||||
$opinions = !empty($object['Opinion']) ? $object['Opinion'] : [];
|
||||
$relationships = !empty($object['Relationship']) ? $object['Relationship'] : [];
|
||||
echo $this->element('genericElements/shortUuidWithNotes', [
|
||||
'uuid' => $object['uuid'],
|
||||
'object_type' => 'Attribute',
|
||||
'notes' => $notes,
|
||||
'opinions' => $opinions,
|
||||
'relationships' => $relationships,
|
||||
]);
|
||||
?>
|
||||
</td>
|
||||
<?php
|
||||
if ($extended):
|
||||
?>
|
||||
|
|
|
@ -45,7 +45,19 @@
|
|||
echo h($object['id']);
|
||||
?>
|
||||
</td>
|
||||
<td class="short context hidden uuid quickSelect"><?= h($object['uuid']) ?></td>
|
||||
<td class="short context hidden uuid">
|
||||
<span class="quickSelect"><?php echo h($object['uuid']); ?></span>
|
||||
<?php
|
||||
$notes = !empty($object['Note']) ? $object['Note'] : [];
|
||||
$opinions = !empty($object['Opinion']) ? $object['Opinion'] : [];
|
||||
$relationships = !empty($object['Relationship']) ? $object['Relationship'] : [];
|
||||
echo $this->element('genericElements/Analyst_data/generic', [
|
||||
'analyst_data' => ['notes' => $notes, 'opinions' => $opinions, 'relationships' => $relationships],
|
||||
'object_uuid' => $object['uuid'],
|
||||
'object_type' => 'Attribute'
|
||||
]);
|
||||
?>
|
||||
</td>
|
||||
<td class="short context hidden">
|
||||
<?php echo $this->element('/Events/View/seen_field', array('object' => $object)); ?>
|
||||
</td>
|
||||
|
@ -55,6 +67,20 @@
|
|||
else echo ' ';
|
||||
?>
|
||||
</td>
|
||||
<td class="short context">
|
||||
<?php
|
||||
$notes = !empty($object['Note']) ? $object['Note'] : [];
|
||||
$opinions = !empty($object['Opinion']) ? $object['Opinion'] : [];
|
||||
$relationships = !empty($object['Relationship']) ? $object['Relationship'] : [];
|
||||
echo $this->element('genericElements/shortUuidWithNotes', [
|
||||
'uuid' => $object['uuid'],
|
||||
'object_type' => 'Attribute',
|
||||
'notes' => $notes,
|
||||
'opinions' => $opinions,
|
||||
'relationships' => $relationships,
|
||||
]);
|
||||
?>
|
||||
</td>
|
||||
<?php
|
||||
if ($extended):
|
||||
?>
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
<th class="context hidden">UUID</th>
|
||||
<th class="context hidden"><?= $this->Paginator->sort('first_seen', __('First seen')) ?> <i class="fas fa-arrow-right"></i> <?= $this->Paginator->sort('last_seen', __('Last seen')) ?></th>
|
||||
<th><?php echo $this->Paginator->sort('timestamp', __('Date'), array('direction' => 'desc'));?></th>
|
||||
<th class="context"><?= __('Context') ?></th>
|
||||
<?php if ($extended): ?>
|
||||
<th class="event_id"><?php echo $this->Paginator->sort('event_id', __('Event'));?></th>
|
||||
<?php endif; ?>
|
||||
|
@ -212,7 +213,6 @@ attributes or the appropriate distribution level. If you think there is a mistak
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
setContextFields();
|
||||
popoverStartup();
|
||||
$('.select_attribute').prop('checked', false).click(function(e) {
|
||||
if ($(this).is(':checked')) {
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<div class="pull-right" style="position:relative;padding-top:9px;z-index:2;">
|
||||
<?php
|
||||
if (Configure::read('MISP.footer_logo')) {
|
||||
echo $this->Html->image('custom/' . h(Configure::read('MISP.footer_logo')), array('alt' => 'Footer Logo', 'onerror' => "this.style.display='none';", 'style' => 'height:24px'));
|
||||
echo '<img src="' . $this->Image->base64(APP . 'files/img/custom/' . Configure::read('MISP.footer_logo')) . '" alt="' . __('Footer logo') . '" style="height:24px" onerror="this.style.display=\'none\';">';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
$seed = mt_rand();
|
||||
|
||||
$notes = $analyst_data['notes'] ?? [];
|
||||
$opinions = $analyst_data['opinions'] ?? [];
|
||||
$relationships = $analyst_data['relationships'] ?? [];
|
||||
|
||||
$notesOpinions = array_merge($notes, $opinions);
|
||||
$notesOpinionsRelationships = array_merge($notesOpinions, $relationships);
|
||||
|
||||
if(!function_exists("countNotes")) {
|
||||
function countNotes($notesOpinions) {
|
||||
$notesTotalCount = count($notesOpinions);
|
||||
$notesCount = 0;
|
||||
$relationsCount = 0;
|
||||
foreach ($notesOpinions as $notesOpinion) {
|
||||
if ($notesOpinion['note_type'] == 2) { // relationship
|
||||
$relationsCount += 1;
|
||||
} else {
|
||||
$notesCount += 1;
|
||||
}
|
||||
if (!empty($notesOpinion['Note'])) {
|
||||
$nestedCounts = countNotes($notesOpinion['Note']);
|
||||
$notesTotalCount += $nestedCounts['total'];
|
||||
$notesCount += $nestedCounts['notesOpinions'];
|
||||
$relationsCount += $nestedCounts['relations'];
|
||||
}
|
||||
if (!empty($notesOpinion['Opinion'])) {
|
||||
$nestedCounts = countNotes($notesOpinion['Opinion']);
|
||||
$notesTotalCount += $nestedCounts['total'];
|
||||
$notesCount += $nestedCounts['notesOpinions'];
|
||||
$relationsCount += $nestedCounts['relations'];
|
||||
}
|
||||
}
|
||||
return ['total' => $notesTotalCount, 'notesOpinions' => $notesCount, 'relations' => $relationsCount];
|
||||
}
|
||||
}
|
||||
$counts = countNotes($notesOpinions);
|
||||
$notesOpinionCount = $counts['notesOpinions'];
|
||||
$relationshipsCount = count($relationships);
|
||||
?>
|
||||
|
||||
<?php if (empty($notesOpinions) && empty($relationshipsCount)): ?>
|
||||
<i class="<?= $this->FontAwesome->getClass('sticky-note') ?> useCursorPointer node-opener-<?= $seed ?>" title="<?= __('Notes and opinions for this UUID') ?>"></i>
|
||||
<?php else: ?>
|
||||
<span class="label label-info useCursorPointer node-opener-<?= $seed ?> highlight-on-hover">
|
||||
<i class="<?= $this->FontAwesome->getClass('sticky-note') ?> useCursorPointer" title="<?= __('Notes and opinions for this UUID') ?>"></i>
|
||||
<?= $notesOpinionCount; ?>
|
||||
<i class="<?= $this->FontAwesome->getClass('project-diagram') ?> useCursorPointer" title="<?= __('Relationships for this UUID') ?>"></i>
|
||||
<?= $relationshipsCount; ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.node-opener-<?= $seed ?>').click(function() {
|
||||
openNotes(this)
|
||||
})
|
||||
|
||||
function adjustPopoverPosition() {
|
||||
var $popover = $('.popover:last');
|
||||
$popover.css('top', Math.max($popover.position().top, 50) + 'px')
|
||||
}
|
||||
|
||||
function openNotes(clicked) {
|
||||
openPopover(clicked, renderedNotes<?= $seed ?>, undefined, undefined, function() {
|
||||
adjustPopoverPosition()
|
||||
$(clicked).removeClass('have-a-popover') // avoid closing the popover if a confirm popover (like the delete one) is called
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<?php
|
||||
echo $this->element('genericElements/Analyst_data/thread', [
|
||||
'seed' => $seed,
|
||||
'notes' => $notes,
|
||||
'opinions' => $opinions,
|
||||
'relationships' => $relationships,
|
||||
'object_type' => $object_type,
|
||||
'object_uuid' => $object_uuid,
|
||||
'shortDist' => $shortDist,
|
||||
]);
|
||||
?>
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
echo $this->element('genericElements/assetLoader', [
|
||||
'css' => ['analyst-data',],
|
||||
]);
|
||||
|
||||
$seed = mt_rand();
|
||||
$forceInline = empty($forceInline) ? false : !empty($forceInline);
|
||||
$opinion_color_scale_100 = ['rgb(164, 0, 0)', 'rgb(166, 15, 0)', 'rgb(169, 25, 0)', 'rgb(171, 33, 0)', 'rgb(173, 40, 0)', 'rgb(175, 46, 0)', 'rgb(177, 52, 0)', 'rgb(179, 57, 0)', 'rgb(181, 63, 0)', 'rgb(183, 68, 0)', 'rgb(186, 72, 0)', 'rgb(188, 77, 0)', 'rgb(190, 82, 0)', 'rgb(191, 86, 0)', 'rgb(193, 90, 0)', 'rgb(195, 95, 0)', 'rgb(197, 98, 0)', 'rgb(198, 102, 0)', 'rgb(200, 106, 0)', 'rgb(201, 110, 0)', 'rgb(203, 114, 0)', 'rgb(204, 118, 0)', 'rgb(206, 121, 0)', 'rgb(208, 125, 0)', 'rgb(209, 128, 0)', 'rgb(210, 132, 0)', 'rgb(212, 135, 0)', 'rgb(213, 139, 0)', 'rgb(214, 143, 0)', 'rgb(216, 146, 0)', 'rgb(217, 149, 0)', 'rgb(218, 153, 0)', 'rgb(219, 156, 0)', 'rgb(220, 160, 0)', 'rgb(222, 163, 0)', 'rgb(223, 166, 0)', 'rgb(224, 169, 0)', 'rgb(225, 173, 0)', 'rgb(226, 176, 0)', 'rgb(227, 179, 0)', 'rgb(228, 182, 0)', 'rgb(229, 186, 0)', 'rgb(230, 189, 0)', 'rgb(231, 192, 0)', 'rgb(232, 195, 0)', 'rgb(233, 198, 0)', 'rgb(234, 201, 0)', 'rgb(235, 204, 0)', 'rgb(236, 207, 0)', 'rgb(237, 210, 0)', 'rgb(237, 212, 0)', 'rgb(234, 211, 0)', 'rgb(231, 210, 0)', 'rgb(229, 209, 1)', 'rgb(226, 208, 1)', 'rgb(223, 207, 1)', 'rgb(220, 206, 1)', 'rgb(218, 204, 1)', 'rgb(215, 203, 2)', 'rgb(212, 202, 2)', 'rgb(209, 201, 2)', 'rgb(206, 200, 2)', 'rgb(204, 199, 2)', 'rgb(201, 198, 3)', 'rgb(198, 197, 3)', 'rgb(195, 196, 3)', 'rgb(192, 195, 3)', 'rgb(189, 194, 3)', 'rgb(186, 193, 3)', 'rgb(183, 192, 4)', 'rgb(180, 190, 4)', 'rgb(177, 189, 4)', 'rgb(174, 188, 4)', 'rgb(171, 187, 4)', 'rgb(168, 186, 4)', 'rgb(165, 185, 4)', 'rgb(162, 183, 4)', 'rgb(159, 182, 4)', 'rgb(156, 181, 4)', 'rgb(153, 180, 4)', 'rgb(149, 179, 5)', 'rgb(146, 178, 5)', 'rgb(143, 177, 5)', 'rgb(139, 175, 5)', 'rgb(136, 174, 5)', 'rgb(133, 173, 5)', 'rgb(130, 172, 5)', 'rgb(126, 170, 5)', 'rgb(123, 169, 5)', 'rgb(119, 168, 5)', 'rgb(115, 167, 5)', 'rgb(112, 165, 6)', 'rgb(108, 164, 6)', 'rgb(104, 163, 6)', 'rgb(100, 162, 6)', 'rgb(96, 160, 6)', 'rgb(92, 159, 6)', 'rgb(88, 157, 6)', 'rgb(84, 156, 6)', 'rgb(80, 155, 6)', 'rgb(78, 154, 6)'];
|
||||
$opinion = min(100, max(0, intval($opinion)));
|
||||
$opinionText = ($opinion >= 81) ? __("Strongly Agree") : (($opinion >= 61) ? __("Agree") : (($opinion >= 41) ? __("Neutral") : (($opinion >= 21) ? __("Disagree") : __("Strongly Disagree"))));
|
||||
$opinionColor = $opinion == 50 ? '#333' : ( $opinion > 50 ? '#468847' : '#b94a48');
|
||||
?>
|
||||
|
||||
<div class="main-container-<?= $seed ?>" style="margin: 0.75rem 0 0.25rem 0;" title="<?= __('Opinion:') ?> 50 /100">
|
||||
<div class="opinion-gradient-container" style="width: 10rem; height: 6px; position: relative;">
|
||||
<span class="opinion-gradient-dot"></span>
|
||||
<div class="opinion-gradient opinion-gradient-negative"></div>
|
||||
<div class="opinion-gradient opinion-gradient-positive"></div>
|
||||
</div>
|
||||
<span style="line-height: 1em; margin-left: 0.25rem; margin-top: -3px;">
|
||||
<b class="opinion-text" style="margin-left: 0.5rem; color: <?= $opinionColor ?>"><?= $opinionText ?></b>
|
||||
<b class="opinion-value" style="margin-left: 0.25rem; color: <?= $opinionColor ?>"><?= $opinion ?></b>
|
||||
<span style="font-size: 0.7em; font-weight: lighter; color: #999">/100</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.main-container-<?= $seed ?> {
|
||||
<?php if ($forceInline): ?>
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
<?php endif; ?>
|
||||
}
|
||||
|
||||
.main-container-<?= $seed ?> .opinion-gradient-<?= $opinion >= 50 ? 'negative' : 'positive' ?> {
|
||||
opacity: 0;
|
||||
}
|
||||
.main-container-<?= $seed ?> .opinion-gradient-dot {
|
||||
left: calc(<?= $opinion ?>% - 6px);
|
||||
background-color: <?= $opinion == 50 ? '#555' : $opinion_color_scale_100[$opinion] ?>;
|
||||
}
|
||||
<?php if ($opinion >= 50): ?>
|
||||
.main-container-<?= $seed ?> .opinion-gradient-positive {
|
||||
-webkit-mask-image: linear-gradient(90deg, black 0 <?= abs(-50 + $opinion)*2 ?>%, transparent <?= abs(-50 + $opinion)*2 ?>% 100%);
|
||||
mask-image: linear-gradient(90deg, black 0 <?= abs(-50 + $opinion)*2 ?>%, transparent <?= abs(-50 + $opinion)*2 ?>% 100%);
|
||||
}
|
||||
<?php else: ?>
|
||||
.main-container-<?= $seed ?> .opinion-gradient-negative {
|
||||
-webkit-mask-image: linear-gradient(90deg, transparent 0 <?= 100-(abs(-50 + $opinion)*2) ?>%, black <?= 100-(abs(-50 + $opinion)*2) ?>% 100%);
|
||||
mask-image: linear-gradient(90deg, transparent 0 <?= 100-(abs(-50 + $opinion)*2) ?>%, black <?= 100-(abs(-50 + $opinion)*2) ?>% 100%);
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,608 @@
|
|||
<?php
|
||||
$URL_ADD = '/analystData/add/';
|
||||
$URL_EDIT = '/analystData/edit/';
|
||||
$URL_DELETE = '/analystData/delete/';
|
||||
|
||||
$seed = isset($seed) ? $seed : mt_rand();
|
||||
|
||||
$notes = !empty($notes) ? $notes : [];
|
||||
$opinions = !empty($opinions) ? $opinions : [];
|
||||
$relationships = !empty($relationships) ? $relationships : [];
|
||||
|
||||
$related_objects = [
|
||||
'Attribute' => [],
|
||||
'Event' => [],
|
||||
'Object' => [],
|
||||
'Organisation' => [],
|
||||
'GalaxyCluster' => [],
|
||||
'Galaxy' => [],
|
||||
'Note' => [],
|
||||
'Opinion' => [],
|
||||
'SharingGroup' => [],
|
||||
];
|
||||
foreach ($relationships as $relationship) {
|
||||
if (!empty($relationship['related_object'][$relationship['related_object_type']])) {
|
||||
$related_objects[$relationship['related_object_type']][$relationship['related_object_uuid']] = $relationship['related_object'][$relationship['related_object_type']];
|
||||
}
|
||||
}
|
||||
|
||||
$notesOpinions = array_merge($notes, $opinions);
|
||||
$notesOpinionsRelationships = array_merge($notesOpinions, $relationships);
|
||||
?>
|
||||
|
||||
<script>
|
||||
|
||||
if (!window.shortDist) {
|
||||
var shortDist = <?= json_encode($shortDist) ?>;
|
||||
}
|
||||
var renderedNotes<?= $seed ?> = null
|
||||
|
||||
function renderNotes(notes, relationship_related_object) {
|
||||
var renderedNotesArray = []
|
||||
if (notes.length == 0) {
|
||||
var emptyHtml = '<span style="text-align: center; color: #777;"><?= __('No notes for this UUID.') ?></span>'
|
||||
renderedNotesArray.push(emptyHtml)
|
||||
} else {
|
||||
notes.forEach(function(note) {
|
||||
var noteHtml = renderNote(note, relationship_related_object)
|
||||
|
||||
if (note.Opinion && note.Opinion.length > 0) { // The notes has more notes attached
|
||||
noteHtml += replyNoteTemplate({notes_html: renderNotes(note.Opinion, relationship_related_object), })
|
||||
}
|
||||
if (note.Note && note.Note.length > 0) { // The notes has more notes attached
|
||||
noteHtml += replyNoteTemplate({notes_html: renderNotes(note.Note, relationship_related_object), })
|
||||
}
|
||||
if (note._max_depth_reached) {
|
||||
noteHtml += replyNoteTemplate({notes_html: maxDepthReachedTemplate({note: note}), })
|
||||
}
|
||||
|
||||
renderedNotesArray.push(noteHtml)
|
||||
});
|
||||
}
|
||||
return renderedNotesArray.join('')
|
||||
}
|
||||
|
||||
function renderNote(note, relationship_related_object) {
|
||||
note.modified_relative = note.modified ? moment(note.modified).fromNow() : note.modified
|
||||
note.created_relative = note.created ? moment(note.created).fromNow() : note.created
|
||||
note.modified = note.modified ? (new Date(note.modified)).toLocaleString() : note.modified
|
||||
note.created = note.created ? (new Date(note.created)).toLocaleString() : note.created
|
||||
note.distribution_text = note.distribution != 4 ? shortDist[note.distribution] : note.SharingGroup.name
|
||||
note.distribution_color = note.distribution == 0 ? '#ff0000' : (note.distribution == 4 ? '#0088cc' : '#000')
|
||||
note.authors = Array.isArray(note.authors) ? note.authors.join(', ') : note.authors;
|
||||
|
||||
if (note.note_type == 0) { // analyst note
|
||||
note.content = analystTemplate(note)
|
||||
} else if (note.note_type == 1) { // opinion
|
||||
note.opinion_color = note.opinion == 50 ? '#333' : ( note.opinion > 50 ? '#468847' : '#b94a48');
|
||||
note.opinion_text = (note.opinion >= 81) ? '<?= __("Strongly Agree") ?>' : ((note.opinion >= 61) ? '<?= __("Agree") ?>' : ((note.opinion >= 41) ? '<?= __("Neutral") ?>' : ((note.opinion >= 21) ? '<?= __("Disagree") ?>' : '<?= __("Strongly Disagree") ?>')))
|
||||
note.content = opinionTemplate(note)
|
||||
} else if (note.note_type == 2) {
|
||||
note.content = renderRelationshipEntryFromType(note, relationship_related_object)
|
||||
}
|
||||
var noteHtml = baseNoteTemplate(note)
|
||||
return noteHtml
|
||||
}
|
||||
|
||||
|
||||
function getURLFromRelationship(note) {
|
||||
if (note.related_object_type == 'Event') {
|
||||
return baseurl + '/events/view/' + note.related_object_uuid
|
||||
} else if (note.related_object_type == 'Attribute') {
|
||||
return note?.attribute?.event_id ? baseurl + '/events/view/' + note.attribute.event_id + '/focus:' + note.related_object_uuid : '#'
|
||||
} else if (note.related_object_type == 'Object') {
|
||||
return note?.object?.event_id ? baseurl + '/events/view/' + note.object.event_id + '/focus:' + note.related_object_uuid : '#'
|
||||
}
|
||||
return '#'
|
||||
}
|
||||
|
||||
function renderRelationshipEntryFromType(note, relationship_related_object) {
|
||||
var contentHtml = ''
|
||||
var template = doT.template('\
|
||||
<span style="border: 1px solid #ddd !important; border-radius: 3px; padding: 0.25rem;"> \
|
||||
<span class="ellipsis-overflow" style="max-width: 12em;">{{!it.related_object_type}}</span> \
|
||||
:: \
|
||||
<span class="ellipsis-overflow" style="max-width: 12em;">{{!it.related_object_uuid}}</span> \
|
||||
</span> \
|
||||
')
|
||||
var templateEvent = doT.template('\
|
||||
<span class="misp-element-wrapper attribute" title="<?= __('Event') ?>"> \
|
||||
<span class="bold"> \
|
||||
<span class="attr-type"><span><i class="<?= $this->FontAwesome->getClass('envelope') ?>"></i></span></span> \
|
||||
<span class=""><span class="attr-value"> \
|
||||
<span class="ellipsis-overflow" style="max-width: 12em;"><a href="{{!it.urlEvent}}" target="_blank">{{!it.content}}</a></span> \
|
||||
</span></span> \
|
||||
</span> \
|
||||
</span> \
|
||||
')
|
||||
if (note.related_object_type == 'Event' && relationship_related_object.Event[note.related_object_uuid]) {
|
||||
note.event = relationship_related_object.Event[note.related_object_uuid]
|
||||
template = doT.template(templateEvent({content: '{{!it.event.info}}', urlEvent: '{{!it.url}}'}))
|
||||
} else if (note.related_object_type == 'Attribute' && relationship_related_object.Attribute[note.related_object_uuid]) {
|
||||
var event = templateEvent({content: '{{!it.attribute.Event.info}}', urlEvent: baseurl + '/events/view/{{!it.attribute.event_id}}'})
|
||||
note.attribute = relationship_related_object.Attribute[note.related_object_uuid]
|
||||
if (note.attribute.object_relation !== undefined && note.attribute.object_relation !== null) {
|
||||
template = doT.template('\
|
||||
' + event + ' \
|
||||
<b>↦</b> \
|
||||
<span class="misp-element-wrapper object"> \
|
||||
<span class="bold"> \
|
||||
<span class="obj-type"> \
|
||||
<span class="object-name" title="<?= __('Object') ?>">{{!it.attribute.Object.name}}</span> \
|
||||
↦ <span class="object-attribute-type" title="<?= __('Object Relation') ?>">{{!it.attribute.object_relation}}</span> \
|
||||
</span> \
|
||||
<span class="obj-value"><span class="ellipsis-overflow" style="max-width: 12em;"><a href="{{!it.url}}" target="_blank">{{!it.attribute.value}}</a></span></span> \
|
||||
</span> \
|
||||
')
|
||||
} else if (relationship_related_object.Attribute[note.related_object_uuid]) {
|
||||
var event = templateEvent({content: '{{!it.attribute.Event.info}}', urlEvent: baseurl + '/events/view/{{!it.attribute.event_id}}'})
|
||||
template = doT.template('\
|
||||
' + event + ' \
|
||||
<b>↦</b> \
|
||||
<span class="misp-element-wrapper attribute"> \
|
||||
<span class="bold"> \
|
||||
<span class="attr-type"><span title="<?= __('Attribute') ?>">{{!it.attribute.type}}</span></span> \
|
||||
<span class="blue"><span class="attr-value"><span class="ellipsis-overflow" style="max-width: 12em;"><a href="{{!it.url}}" target="_blank">{{!it.attribute.value}}</a></span></span></span> \
|
||||
</span> \
|
||||
</span> \
|
||||
')
|
||||
}
|
||||
} else if (note.related_object_type == 'Object') {
|
||||
var event = templateEvent({content: '{{!it.object.Event.info}}', urlEvent: baseurl + '/events/view/{{!it.object.event_id}}'})
|
||||
note.object = relationship_related_object.Object[note.related_object_uuid]
|
||||
template = doT.template('\
|
||||
' + event + ' \
|
||||
<b>↦</b> \
|
||||
<span class="misp-element-wrapper object"> \
|
||||
<span class="bold"> \
|
||||
<span class="obj-type"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('cubes') ?>" title="<?= __('Object') ?>" style="margin: 0 0 0 0.25rem;"></i> \
|
||||
<span>{{!it.object.name}}</span> \
|
||||
</span> \
|
||||
<span class="blue"><span class="obj-value"><span class="ellipsis-overflow" style="max-width: 12em;"><a href="{{!it.url}}" target="_blank">{{!it.object.id}}</a></span></span></span> \
|
||||
</span> \
|
||||
</span> \
|
||||
')
|
||||
}
|
||||
note.url = getURLFromRelationship(note)
|
||||
contentHtml = template(note)
|
||||
return relationshipDefaultEntryTemplate({content: contentHtml, relationship_type: note.relationship_type, comment: note.comment})
|
||||
}
|
||||
|
||||
var noteFilteringTemplate = '\
|
||||
<div class="btn-group notes-filtering-container" style="margin-bottom: 0.5rem"> \
|
||||
<btn class="btn btn-small btn-primary" href="#" onclick="filterNotes(this, \'all\')"><?= __('All notes') ?></btn> \
|
||||
<btn class="btn btn-small btn-inverse" href="#" onclick="filterNotes(this, \'org\')"><?= __('Organisation notes') ?></btn> \
|
||||
<btn class="btn btn-small btn-inverse" href="#" onclick="filterNotes(this, \'notorg\')"><?= __('Non-Org notes') ?></btn> \
|
||||
</div> \
|
||||
'
|
||||
|
||||
var baseNoteTemplate = doT.template('\
|
||||
<div id="{{!it.note_type_name}}-{{!it.id}}" \
|
||||
class="analyst-note" \
|
||||
style="display: flex; flex-direction: row; align-items: center; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 1px 5px -2px rgb(0 0 0 / 0.5); border-radius: 0.25rem; padding: 0.25rem; margin-bottom: 0.0rem; background-color: #fff; transition: ease-out opacity 0.5s;" \
|
||||
data-org-uuid="{{!it.orgc_uuid}}" \
|
||||
> \
|
||||
<div style="flex-grow: 1;"> \
|
||||
<div style="display: flex; flex-direction: column;"> \
|
||||
<div style="display: flex; min-width: 250px; gap: 0.5rem;"> \
|
||||
<img src="<?= $baseurl ?>/img/orgs/{{!it.Orgc.id}}.png" width="20" height="20" class="orgImg" style="width: 20px; height: 20px;" onerror="this.remove()" alt="Organisation logo"></object> \
|
||||
<span style="margin-left: 0rem; margin-right: 0.5rem;"> \
|
||||
<span>{{!it.Orgc.name}}</span> \
|
||||
<i class="<?= $this->FontAwesome->getClass('angle-right') ?>" style="color: #999; margin: 0 0.25rem;"></i> \
|
||||
<b>{{!it.authors}}</b> \
|
||||
</span> \
|
||||
<span style="display: inline-block; font-weight: lighter; color: #999">{{!it.modified_relative}} • {{!it.modified}}</span> \
|
||||
<span style="margin-left: 0.5rem; flex-grow: 1; text-align: right; color: {{!it.distribution_color}}"> \
|
||||
{{? it.distribution == 4 }} \
|
||||
<a href="<?= $baseurl ?>/sharingGroups/view/{{!it.SharingGroup.id}}" target="_blank">{{!it.distribution_text}}</a> \
|
||||
{{??}} \
|
||||
{{!it.distribution_text}} \
|
||||
{{?}} \
|
||||
</span> \
|
||||
<span class="action-button-container" style="margin-left: auto; display: flex; gap: 0.2rem;"> \
|
||||
{{? 1 == <?= $me['Role']['perm_modify'] ? 1 : 0 ?> }} \
|
||||
<span role="button" onclick="addOpinion(this, \'{{!it.uuid}}\', \'{{!it.note_type_name}}\')" title="<?= __('Add an opinion to this note') ?>"><i class="<?= $this->FontAwesome->getClass('gavel') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? 1 == <?= $me['Role']['perm_modify'] ? 1 : 0 ?> }} \
|
||||
<span role="button" onclick="addNote(this, \'{{!it.uuid}}\', \'{{!it.note_type_name}}\')" title="<?= __('Add a note to this ') ?>{{!it.note_type_name}}"><i class="<?= $this->FontAwesome->getClass('comment-alt') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? it._canEdit }} \
|
||||
<span role="button" onclick="editNote(this, {{!it.id}}, \'{{!it.note_type_name}}\')" title="<?= __('Edit this note') ?>"><i class="<?= $this->FontAwesome->getClass('edit') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? it._canEdit }} \
|
||||
<span role="button" onclick="deleteNote(this, {{!it.id}})" title="<?= __('Delete this note') ?>" href="<?= $baseurl . $URL_DELETE ?>{{!it.note_type_name}}/{{!it.id}}"><i class="<?= $this->FontAwesome->getClass('trash') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
</span> \
|
||||
</div> \
|
||||
<div style="">{{=it.content}}</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
')
|
||||
var analystTemplate = doT.template('\
|
||||
<div style="max-width: 40vw; margin-top: 0.5rem; font-size:"> \
|
||||
{{!it.note}} \
|
||||
</div> \
|
||||
')
|
||||
var opinionGradient = '\
|
||||
<div class="opinion-gradient-container" style="width: 10rem; height: 6px;">\
|
||||
<span class="opinion-gradient-dot"></span> \
|
||||
<div class="opinion-gradient opinion-gradient-negative"></div> \
|
||||
<div class="opinion-gradient opinion-gradient-positive"></div> \
|
||||
</div> \
|
||||
'
|
||||
var opinionTemplate = doT.template('\
|
||||
<div style="margin: 0.75rem 0 0.25rem 0; display: flex; flex-direction: row;" title="<?= __('Opinion:') ?> {{!it.opinion}} /100"> \
|
||||
' + opinionGradient + ' \
|
||||
<span style="line-height: 1em; margin-left: 0.25rem; margin-top: -3px;"> \
|
||||
<b style="margin-left: 0.5rem; color: {{!it.opinion_color}}">{{!it.opinion_text}}</b> \
|
||||
<b style="margin-left: 0.25rem; color: {{!it.opinion_color}}">{{!it.opinion}}</b> \
|
||||
<span style="font-size: 0.7em; font-weight: lighter; color: #999">/100</span> \
|
||||
</span> \
|
||||
</div> \
|
||||
{{? it.comment }} \
|
||||
<div style="max-width: 40vw; margin: 0.5rem 0 0 0.5rem; position: relative;" class="v-bar-text-opinion"> \
|
||||
{{!it.comment}} \
|
||||
</div> \
|
||||
{{?}} \
|
||||
')
|
||||
var relationshipDefaultEntryTemplate = doT.template('\
|
||||
<div style="max-width: 40vw; margin: 0.5rem 0 0.5rem 0.25rem;"> \
|
||||
<div style="display: flex; flex-direction: row; align-items: center; flex-wrap: nowrap;"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('minus') ?>" style="font-size: 1.5em; color: #555"></i> \
|
||||
<span style="text-wrap: nowrap; padding: 0 0.25rem; border: 2px solid #555; border-radius: 0.25rem; max-width: 20rem; overflow-x: hidden; text-overflow: ellipsis;"> \
|
||||
{{? it.relationship_type }} \
|
||||
{{!it.relationship_type}} \
|
||||
{{??}} \
|
||||
<i style="font-weight: lighter; color: #999;"> - empty -</i> \
|
||||
{{?}} \
|
||||
</span> \
|
||||
<i class="<?= $this->FontAwesome->getClass('long-arrow-alt-right') ?>" style="font-size: 1.5em; color: #555"></i> \
|
||||
<div style="margin-left: 0.5rem;">{{=it.content}}</div> \
|
||||
</div> \
|
||||
{{? it.comment }} \
|
||||
<div style="max-width: 40vw; margin: 0.5rem 0 0 0.5rem; position: relative;" class="v-bar-text-opinion"> \
|
||||
{{!it.comment}} \
|
||||
</div> \
|
||||
{{?}} \
|
||||
</div> \
|
||||
')
|
||||
var replyNoteTemplate = doT.template('\
|
||||
<span class="reply-to-note-collapse-button reply-to-group" onclick="$(this).toggleClass(\'collapsed\').next().toggle()" title="<?= __('Toggle annotation for this note') ?>" \
|
||||
style="width: 12px; height: 12px; border-radius: 50%; border: 1px solid #0035dc20; background: #ccccccdd; box-sizing: border-box; line-height: 12px; padding: 0 1px; cursor: pointer; margin: calc(-0.5rem - 6px) 0 calc(-0.5rem - 6px) -1px; z-index: 2;" \
|
||||
> \
|
||||
<i class="<?= $this->FontAwesome->getClass('angle-up') ?>" style="line-height: 8px;"></i> \
|
||||
</span> \
|
||||
<div class="reply-to-note reply-to-group" style="position: relative; display: flex; flex-direction: column; gap: 0.5rem; margin-left: 3px; border-left: 4px solid #ccccccaa; background: #0035dc10; padding: 0.5rem; border-radius: 5px; border-top-left-radius: 0;"> \
|
||||
{{=it.notes_html}} \
|
||||
</div> \
|
||||
')
|
||||
|
||||
var maxDepthReachedTemplate = doT.template('\
|
||||
<div class="max-depth-container"> \
|
||||
<div> \
|
||||
<span style="font-weight: lighter; color: #999;"> \
|
||||
- Max depth reached, there is at least one entry remaining - \
|
||||
<a href="<?= $baseurl ?>/analystData/view/{{!it.note.note_type_name}}/{{!it.note.id}}" target="_blank"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('search') ?>"></i> \
|
||||
<?= __('View entry') ?> \
|
||||
</a> \
|
||||
</span> \
|
||||
</div> \
|
||||
<div> \
|
||||
<span> \
|
||||
<a onclick="fetchMoreNotes(this, \'{{!it.note.note_type_name}}\', \'{{!it.note.uuid}}\')" target="_blank" class="useCursorPointer"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('plus') ?>"></i> \
|
||||
<?= __('Load more notes') ?> \
|
||||
</a> \
|
||||
</span> \
|
||||
</div> \
|
||||
</div> \
|
||||
')
|
||||
|
||||
function filterNotes(clicked, filter) {
|
||||
$(clicked).closest('.notes-filtering-container').find('.btn').addClass('btn-inverse').removeClass('btn-primary')
|
||||
$(clicked).removeClass('btn-inverse').addClass('btn-primary')
|
||||
var $container = $(clicked).parent().parent().find('.all-notes')
|
||||
var $addButtonContainer = $('#add-button-container');
|
||||
if (filter == 'notorg') {
|
||||
$addButtonContainer.hide()
|
||||
} else {
|
||||
$addButtonContainer.show()
|
||||
}
|
||||
$container.find('.analyst-note').show()
|
||||
$container.find('.reply-to-group').show()
|
||||
$container.find('.analyst-note').filter(function() {
|
||||
var $note = $(this)
|
||||
// WEIRD. reply-to-group is not showing up!
|
||||
if (filter == 'all') {
|
||||
return false
|
||||
} else if (filter == 'org') {
|
||||
var shouldHide = $note.data('org-uuid') != '<?= $me['Organisation']['uuid'] ?>'
|
||||
if (shouldHide && $note.next().hasClass('reply-to-group')) { // Also hide reply to button and container
|
||||
$note.next().hide().next().hide()
|
||||
}
|
||||
return shouldHide
|
||||
} else if (filter == 'notorg') {
|
||||
var shouldHide = $note.data('org-uuid') == '<?= $me['Organisation']['uuid'] ?>'
|
||||
if (shouldHide && $note.next().hasClass('reply-to-group')) { // Also hide reply to button and container
|
||||
$note.next().hide().next().hide()
|
||||
|
||||
}
|
||||
return shouldHide
|
||||
}
|
||||
}).hide()
|
||||
}
|
||||
|
||||
function fetchMoreNotes(clicked, noteType, uuid) {
|
||||
var depth = 3
|
||||
var $maxDepthContainer = $(clicked).closest('.max-depth-container')
|
||||
var url = '<?= $baseurl ?>/analystData/getChildren/' + noteType + '/' + uuid + '/' + depth + '.json'
|
||||
$.ajax({
|
||||
beforeSend: function () {
|
||||
$maxDepthContainer.css('filter', 'blur(2px)')
|
||||
},
|
||||
cache: false,
|
||||
success:function (data, textStatus) {
|
||||
var notesOpinions = [].concat(data.Note ?? [], data.Opinion ?? [])
|
||||
var renderedAdditionalNotes = renderNotes(notesOpinions, [])
|
||||
$maxDepthContainer[0].outerHTML = renderedAdditionalNotes
|
||||
},
|
||||
error:function(xhr) {
|
||||
showMessage('fail', 'Could not fetch additional analyst data.');
|
||||
},
|
||||
complete: function() {
|
||||
$maxDepthContainer.css('filter', 'unset')
|
||||
},
|
||||
url: url
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
(function() {
|
||||
var notes = <?= json_encode($notesOpinions) ?>;
|
||||
var relationships = <?= json_encode($relationships) ?>;
|
||||
var relationship_related_object = <?= json_encode($related_objects) ?>;
|
||||
var container_id = false
|
||||
<?php if (isset($container_id)): ?>
|
||||
container_id = '<?= h($container_id) ?>'
|
||||
<?php endif; ?>
|
||||
|
||||
var nodeContainerTemplate = doT.template('\
|
||||
<div> \
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 10px;"> \
|
||||
<li class="active"><a href="#notes-<?= $seed ?>" data-toggle="tab"><?= __('Notes & Opinions') ?></a></li> \
|
||||
<li><a href="#relationships-<?= $seed ?>" data-toggle="tab"><?= __('Relationships') ?></a></li> \
|
||||
</ul> \
|
||||
<div class="tab-content" style="padding: 0.25rem; max-width: 1200px; min-width: 400px;"> \
|
||||
<div id="notes-<?= $seed ?>" class="tab-pane active"> \
|
||||
' + noteFilteringTemplate + ' \
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;" class="all-notes">{{=it.content_notes}}</div>\
|
||||
</div> \
|
||||
<div id="relationships-<?= $seed ?>" class="tab-pane"> \
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">{{=it.content_relationships}}</div>\
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
')
|
||||
|
||||
function renderAllNotesWithForm(relationship_related_object) {
|
||||
var buttonContainer = '<div id="add-button-container" style="margin-top: 0.5rem;">' + addNoteButton + addOpinionButton + '</div>'
|
||||
renderedNotes<?= $seed ?> = nodeContainerTemplate({
|
||||
content_notes: renderNotes(notes.filter(function(note) { return note.note_type != 2}), relationship_related_object) + buttonContainer,
|
||||
content_relationships: renderNotes(relationships, relationship_related_object) + addRelationshipButton,
|
||||
})
|
||||
if (container_id) {
|
||||
$('#' + container_id).html(renderedNotes<?= $seed ?>)
|
||||
}
|
||||
}
|
||||
|
||||
var addNoteButton = '<button class="btn btn-small btn-block btn-primary" type="button" onclick="createNewNote(this, \'<?= $object_type ?>\', \'<?= $object_uuid ?>\')"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('plus') ?>"></i> <?= __('Add a note') ?> \
|
||||
</button>'
|
||||
var addOpinionButton = '<button class="btn btn-small btn-block btn-primary" style="margin-top: 2px;" type="button" onclick="createNewOpinion(this, \'<?= $object_type ?>\', \'<?= $object_uuid ?>\')"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('gavel') ?>"></i> <?= __('Add an opinion') ?> \
|
||||
</button>'
|
||||
var addRelationshipButton = '<button class="btn btn-small btn-block btn-primary" type="button" onclick="createNewRelationship(this, \'<?= $object_type ?>\', \'<?= $object_uuid ?>\')"> \
|
||||
<i class="<?= $this->FontAwesome->getClass('plus') ?>"></i> <?= __('Add a relationship') ?> \
|
||||
</button>'
|
||||
|
||||
$(document).ready(function() {
|
||||
renderAllNotesWithForm(relationship_related_object)
|
||||
})
|
||||
})()
|
||||
|
||||
function createNewNote(clicked, object_type, object_uuid) {
|
||||
note_type = 'Note';
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + note_type + '/' + object_uuid + '/' + object_type)
|
||||
}
|
||||
|
||||
function createNewOpinion(clicked, object_type, object_uuid) {
|
||||
note_type = 'Opinion';
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + note_type + '/' + object_uuid + '/' + object_type)
|
||||
}
|
||||
|
||||
function createNewRelationship(clicked, object_type, object_uuid) {
|
||||
note_type = 'Relationship';
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + note_type + '/' + object_uuid + '/' + object_type)
|
||||
}
|
||||
|
||||
function addNote(clicked, note_uuid, object_type) {
|
||||
note_type = 'Note';
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + note_type + '/' + note_uuid + '/' + object_type)
|
||||
}
|
||||
|
||||
function addOpinion(clicked, note_uuid, object_type) {
|
||||
note_type = 'Opinion';
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + note_type + '/' + note_uuid + '/' + object_type)
|
||||
}
|
||||
|
||||
function editNote(clicked, note_id, note_type) {
|
||||
openGenericModal(baseurl + '<?= $URL_EDIT ?>' + note_type + '/' + note_id)
|
||||
}
|
||||
|
||||
function deleteNote(clicked, note_id) {
|
||||
var deletionSuccessCallback = function(data) {
|
||||
$(clicked).closest('.analyst-note').remove()
|
||||
}
|
||||
popoverConfirm(clicked, '<?= __('Confirm deletion of this note') ?>', undefined, deletionSuccessCallback)
|
||||
}
|
||||
|
||||
function replaceNoteInUI(data) {
|
||||
var noteType = Object.keys(data)[0]
|
||||
var noteHTMLID = '#' + data[noteType].note_type_name + '-' + data[noteType].id
|
||||
var $noteToReplace = $(noteHTMLID)
|
||||
if ($noteToReplace.length == 1) {
|
||||
var compiledUpdatedNote = renderNote(data[noteType])
|
||||
$noteToReplace[0].outerHTML = compiledUpdatedNote
|
||||
$(noteHTMLID).css({'opacity': 0})
|
||||
setTimeout(() => {
|
||||
$(noteHTMLID).css({'opacity': 1})
|
||||
}, 750);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.action-button-container > span {
|
||||
visibility: hidden;
|
||||
}
|
||||
.analyst-note:hover .action-button-container > span {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.reply-to-note-collapse-button.collapsed {
|
||||
margin-bottom: -0.25rem !important;
|
||||
}
|
||||
|
||||
.v-bar-text-opinion::before {
|
||||
content: '';
|
||||
margin-right: 5px;
|
||||
margin-left: 2px;
|
||||
border-left: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
height: 1.3rem;
|
||||
width: 5px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-top: -12px;
|
||||
border-color: #969696;
|
||||
}
|
||||
|
||||
.reply-to-note-collapse-button.collapsed > i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
<?php
|
||||
if(!function_exists("genStyleForOpinionNotes")) {
|
||||
function genStyleForOpinionNotes($notes) {
|
||||
foreach ($notes as $note) {
|
||||
genStyleForOpinionNote($note);
|
||||
if (!empty($note['Note'])) {
|
||||
genStyleForOpinionNotes($note['Note']);
|
||||
}
|
||||
if (!empty($note['Opinion'])) {
|
||||
genStyleForOpinionNotes($note['Opinion']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!function_exists("genStyleForOpinionNote")) {
|
||||
function genStyleForOpinionNote($note) {
|
||||
if ($note['note_type'] != 1) { // opinion
|
||||
return;
|
||||
}
|
||||
$opinion_color_scale_100 = ['rgb(164, 0, 0)', 'rgb(166, 15, 0)', 'rgb(169, 25, 0)', 'rgb(171, 33, 0)', 'rgb(173, 40, 0)', 'rgb(175, 46, 0)', 'rgb(177, 52, 0)', 'rgb(179, 57, 0)', 'rgb(181, 63, 0)', 'rgb(183, 68, 0)', 'rgb(186, 72, 0)', 'rgb(188, 77, 0)', 'rgb(190, 82, 0)', 'rgb(191, 86, 0)', 'rgb(193, 90, 0)', 'rgb(195, 95, 0)', 'rgb(197, 98, 0)', 'rgb(198, 102, 0)', 'rgb(200, 106, 0)', 'rgb(201, 110, 0)', 'rgb(203, 114, 0)', 'rgb(204, 118, 0)', 'rgb(206, 121, 0)', 'rgb(208, 125, 0)', 'rgb(209, 128, 0)', 'rgb(210, 132, 0)', 'rgb(212, 135, 0)', 'rgb(213, 139, 0)', 'rgb(214, 143, 0)', 'rgb(216, 146, 0)', 'rgb(217, 149, 0)', 'rgb(218, 153, 0)', 'rgb(219, 156, 0)', 'rgb(220, 160, 0)', 'rgb(222, 163, 0)', 'rgb(223, 166, 0)', 'rgb(224, 169, 0)', 'rgb(225, 173, 0)', 'rgb(226, 176, 0)', 'rgb(227, 179, 0)', 'rgb(228, 182, 0)', 'rgb(229, 186, 0)', 'rgb(230, 189, 0)', 'rgb(231, 192, 0)', 'rgb(232, 195, 0)', 'rgb(233, 198, 0)', 'rgb(234, 201, 0)', 'rgb(235, 204, 0)', 'rgb(236, 207, 0)', 'rgb(237, 210, 0)', 'rgb(237, 212, 0)', 'rgb(234, 211, 0)', 'rgb(231, 210, 0)', 'rgb(229, 209, 1)', 'rgb(226, 208, 1)', 'rgb(223, 207, 1)', 'rgb(220, 206, 1)', 'rgb(218, 204, 1)', 'rgb(215, 203, 2)', 'rgb(212, 202, 2)', 'rgb(209, 201, 2)', 'rgb(206, 200, 2)', 'rgb(204, 199, 2)', 'rgb(201, 198, 3)', 'rgb(198, 197, 3)', 'rgb(195, 196, 3)', 'rgb(192, 195, 3)', 'rgb(189, 194, 3)', 'rgb(186, 193, 3)', 'rgb(183, 192, 4)', 'rgb(180, 190, 4)', 'rgb(177, 189, 4)', 'rgb(174, 188, 4)', 'rgb(171, 187, 4)', 'rgb(168, 186, 4)', 'rgb(165, 185, 4)', 'rgb(162, 183, 4)', 'rgb(159, 182, 4)', 'rgb(156, 181, 4)', 'rgb(153, 180, 4)', 'rgb(149, 179, 5)', 'rgb(146, 178, 5)', 'rgb(143, 177, 5)', 'rgb(139, 175, 5)', 'rgb(136, 174, 5)', 'rgb(133, 173, 5)', 'rgb(130, 172, 5)', 'rgb(126, 170, 5)', 'rgb(123, 169, 5)', 'rgb(119, 168, 5)', 'rgb(115, 167, 5)', 'rgb(112, 165, 6)', 'rgb(108, 164, 6)', 'rgb(104, 163, 6)', 'rgb(100, 162, 6)', 'rgb(96, 160, 6)', 'rgb(92, 159, 6)', 'rgb(88, 157, 6)', 'rgb(84, 156, 6)', 'rgb(80, 155, 6)', 'rgb(78, 154, 6)'];
|
||||
$opinion = min(100, max(0, intval($note['opinion'])));
|
||||
?>
|
||||
|
||||
#Opinion-<?= $note['id'] ?> .opinion-gradient-<?= $opinion >= 50 ? 'negative' : 'positive' ?> {
|
||||
opacity: 0;
|
||||
}
|
||||
#Opinion-<?= $note['id'] ?> .opinion-gradient-dot {
|
||||
left: calc(<?= $opinion ?>% - 6px);
|
||||
background-color: <?= $opinion == 50 ? '#555' : $opinion_color_scale_100[$opinion] ?>;
|
||||
}
|
||||
<?php if ($opinion >= 50): ?>
|
||||
#Opinion-<?= $note['id'] ?> .opinion-gradient-positive {
|
||||
-webkit-mask-image: linear-gradient(90deg, black 0 <?= abs(-50 + $opinion)*2 ?>%, transparent <?= abs(-50 + $opinion)*2 ?>% 100%);
|
||||
mask-image: linear-gradient(90deg, black 0 <?= abs(-50 + $opinion)*2 ?>%, transparent <?= abs(-50 + $opinion)*2 ?>% 100%);
|
||||
}
|
||||
<?php else: ?>
|
||||
#Opinion-<?= $note['id'] ?> .opinion-gradient-negative {
|
||||
-webkit-mask-image: linear-gradient(90deg, transparent 0 <?= 100-(abs(-50 + $opinion)*2) ?>%, black <?= 100-(abs(-50 + $opinion)*2) ?>% 100%);
|
||||
mask-image: linear-gradient(90deg, transparent 0 <?= 100-(abs(-50 + $opinion)*2) ?>%, black <?= 100-(abs(-50 + $opinion)*2) ?>% 100%);
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
genStyleForOpinionNotes($notesOpinionsRelationships)
|
||||
?>
|
||||
|
||||
</style>
|
||||
|
||||
<style>
|
||||
span.misp-element-wrapper {
|
||||
margin: 3px 3px;
|
||||
border: 1px solid #ddd !important;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
.misp-element-wrapper.attribute .attr-type {
|
||||
background-color: #f5f5f5 !important;
|
||||
border-right: 1px solid #ddd !important;
|
||||
display: inline-block;
|
||||
}
|
||||
.misp-element-wrapper.attribute .attr-type > span {
|
||||
margin: 2px 3px;
|
||||
}
|
||||
.misp-element-wrapper.attribute .attr-value {
|
||||
display: inline-table;
|
||||
margin: 0px 3px;
|
||||
}
|
||||
.misp-element-wrapper.attribute .attr-value > span {
|
||||
max-width: 300px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: table-cell;
|
||||
}
|
||||
span.misp-element-wrapper.object {
|
||||
border: 1px solid #3465a4 !important;
|
||||
}
|
||||
.misp-element-wrapper.object .obj-type {
|
||||
display: inline-block;
|
||||
background-color: #3465a4 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.misp-element-wrapper.object .obj-type .object-attribute-type {
|
||||
margin-left: 0;
|
||||
background-color: #f5f5f5;
|
||||
color: black;
|
||||
padding: 1px 3px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
.misp-element-wrapper.object .obj-type > span {
|
||||
margin: 2px 3px;
|
||||
}
|
||||
.misp-element-wrapper.object .obj-value {
|
||||
display: inline-table;
|
||||
margin: 0px 3px;
|
||||
}
|
||||
.misp-element-wrapper.object .obj-value > span {
|
||||
max-width: 300px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: table-cell;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
$seed = mt_rand();
|
||||
$params['type'] = 'number';
|
||||
$params['min'] = 0;
|
||||
$params['max'] = 100;
|
||||
$params['class'] .= ' opinion-' . $seed;
|
||||
echo $this->Form->input($fieldData['field'], $params);
|
||||
|
||||
echo $this->element('genericElements/assetLoader', [
|
||||
'css' => ['analyst-data',],
|
||||
]);
|
||||
?>
|
||||
|
||||
<script>
|
||||
var opinionGradient<?= $seed ?> = '\
|
||||
<div class="opinion-gradient-container" style="width: 10rem; height: 6px; position: relative;">\
|
||||
<div class="opinion-gradient opinion-gradient-negative"></div> \
|
||||
<div class="opinion-gradient opinion-gradient-positive"></div> \
|
||||
<input type="range" min="0" max="100" value="50" step="10" class="slider" id="opinion-slider">\
|
||||
</div> \
|
||||
'
|
||||
var opinionTemplate<?= $seed ?> = '\
|
||||
<div class="main-container" style="margin: 0.75rem 0 0.25rem 0; display: flex; flex-direction: row;" title="<?= __('Opinion:') ?> 50 /100"> \
|
||||
' + opinionGradient<?= $seed ?> + ' \
|
||||
<span style="line-height: 1em; margin-left: 0.25rem; margin-top: -3px;"> \
|
||||
<b class="opinion-text" style="margin-left: 0.5rem; color: #333"></b> \
|
||||
<b class="opinion-value" style="margin-left: 0.25rem; color: #333"></b> \
|
||||
<span style="font-size: 0.7em; font-weight: lighter; color: #999">/100</span> \
|
||||
</span> \
|
||||
</div> \
|
||||
'
|
||||
var opinionColorScale = ['rgb(164, 0, 0)', 'rgb(166, 15, 0)', 'rgb(169, 25, 0)', 'rgb(171, 33, 0)', 'rgb(173, 40, 0)', 'rgb(175, 46, 0)', 'rgb(177, 52, 0)', 'rgb(179, 57, 0)', 'rgb(181, 63, 0)', 'rgb(183, 68, 0)', 'rgb(186, 72, 0)', 'rgb(188, 77, 0)', 'rgb(190, 82, 0)', 'rgb(191, 86, 0)', 'rgb(193, 90, 0)', 'rgb(195, 95, 0)', 'rgb(197, 98, 0)', 'rgb(198, 102, 0)', 'rgb(200, 106, 0)', 'rgb(201, 110, 0)', 'rgb(203, 114, 0)', 'rgb(204, 118, 0)', 'rgb(206, 121, 0)', 'rgb(208, 125, 0)', 'rgb(209, 128, 0)', 'rgb(210, 132, 0)', 'rgb(212, 135, 0)', 'rgb(213, 139, 0)', 'rgb(214, 143, 0)', 'rgb(216, 146, 0)', 'rgb(217, 149, 0)', 'rgb(218, 153, 0)', 'rgb(219, 156, 0)', 'rgb(220, 160, 0)', 'rgb(222, 163, 0)', 'rgb(223, 166, 0)', 'rgb(224, 169, 0)', 'rgb(225, 173, 0)', 'rgb(226, 176, 0)', 'rgb(227, 179, 0)', 'rgb(228, 182, 0)', 'rgb(229, 186, 0)', 'rgb(230, 189, 0)', 'rgb(231, 192, 0)', 'rgb(232, 195, 0)', 'rgb(233, 198, 0)', 'rgb(234, 201, 0)', 'rgb(235, 204, 0)', 'rgb(236, 207, 0)', 'rgb(237, 210, 0)', 'rgb(237, 212, 0)', 'rgb(234, 211, 0)', 'rgb(231, 210, 0)', 'rgb(229, 209, 1)', 'rgb(226, 208, 1)', 'rgb(223, 207, 1)', 'rgb(220, 206, 1)', 'rgb(218, 204, 1)', 'rgb(215, 203, 2)', 'rgb(212, 202, 2)', 'rgb(209, 201, 2)', 'rgb(206, 200, 2)', 'rgb(204, 199, 2)', 'rgb(201, 198, 3)', 'rgb(198, 197, 3)', 'rgb(195, 196, 3)', 'rgb(192, 195, 3)', 'rgb(189, 194, 3)', 'rgb(186, 193, 3)', 'rgb(183, 192, 4)', 'rgb(180, 190, 4)', 'rgb(177, 189, 4)', 'rgb(174, 188, 4)', 'rgb(171, 187, 4)', 'rgb(168, 186, 4)', 'rgb(165, 185, 4)', 'rgb(162, 183, 4)', 'rgb(159, 182, 4)', 'rgb(156, 181, 4)', 'rgb(153, 180, 4)', 'rgb(149, 179, 5)', 'rgb(146, 178, 5)', 'rgb(143, 177, 5)', 'rgb(139, 175, 5)', 'rgb(136, 174, 5)', 'rgb(133, 173, 5)', 'rgb(130, 172, 5)', 'rgb(126, 170, 5)', 'rgb(123, 169, 5)', 'rgb(119, 168, 5)', 'rgb(115, 167, 5)', 'rgb(112, 165, 6)', 'rgb(108, 164, 6)', 'rgb(104, 163, 6)', 'rgb(100, 162, 6)', 'rgb(96, 160, 6)', 'rgb(92, 159, 6)', 'rgb(88, 157, 6)', 'rgb(84, 156, 6)', 'rgb(80, 155, 6)', 'rgb(78, 154, 6)'];
|
||||
|
||||
$(document).ready(function() {
|
||||
initOpinionSlider()
|
||||
})
|
||||
|
||||
function getOpinionColor(opinion) {
|
||||
return opinion == 50 ? '#333' : ( opinion > 50 ? '#468847' : '#b94a48');
|
||||
}
|
||||
function getOpinionText(opinion) {
|
||||
return (opinion >= 81) ? '<?= __("Strongly Agree") ?>' : ((opinion >= 61) ? '<?= __("Agree") ?>' : ((opinion >= 41) ? '<?= __("Neutral") ?>' : ((opinion >= 21) ? '<?= __("Disagree") ?>' : '<?= __("Strongly Disagree") ?>')))
|
||||
}
|
||||
|
||||
function setOpinionLevel(opinion) {
|
||||
opinion = Number.parseInt(opinion)
|
||||
var $formContainer = $('.opinion-<?= $seed ?>').parent()
|
||||
var $mainContainer = $formContainer.find('.main-container')
|
||||
var $gradientContainer = $formContainer.find('.opinion-gradient-container')
|
||||
var $opinionSlider = $gradientContainer.find('#opinion-slider')
|
||||
var backgroundColor = getOpinionColor(opinion)
|
||||
var backgroundColorDot = opinion == 50 ? '#555' : opinionColorScale[opinion]
|
||||
|
||||
$mainContainer.attr('title', '<?= __('Opinion:') ?> ' + opinion + ' /100')
|
||||
$mainContainer.find('.opinion-text')
|
||||
.css('color', backgroundColor)
|
||||
.text(getOpinionText(opinion))
|
||||
$mainContainer.find('.opinion-value')
|
||||
.css('color', backgroundColor)
|
||||
.text(opinion)
|
||||
|
||||
if (opinion >= 50) {
|
||||
var opinionMask = Math.abs(-50 + opinion)*2
|
||||
$gradientContainer.find('.opinion-gradient-negative').css({
|
||||
'opacity': 0,
|
||||
'-webkit-mask-image': 'unset',
|
||||
'mask-image': 'unset',
|
||||
})
|
||||
$gradientContainer.find('.opinion-gradient-positive').css({
|
||||
'opacity': 1,
|
||||
'-webkit-mask-image': 'linear-gradient(90deg, black 0 ' + opinionMask + '%, transparent ' + opinionMask + '% 100%)',
|
||||
'mask-image': 'linear-gradient(90deg, black 0 ' + opinionMask + '%, transparent ' + opinionMask + '% 100%)',
|
||||
})
|
||||
} else {
|
||||
var opinionMask = 100-(Math.abs(-50 + opinion)*2)
|
||||
$gradientContainer.find('.opinion-gradient-negative').css({
|
||||
'opacity': 1,
|
||||
'-webkit-mask-image': 'linear-gradient(90deg, transparent 0 ' + opinionMask + '%, black ' + opinionMask + '% 100%)',
|
||||
'mask-image': 'linear-gradient(90deg, transparent 0 ' + opinionMask + '%, black ' + opinionMask + '% 100%)',
|
||||
})
|
||||
$gradientContainer.find('.opinion-gradient-positive').css({
|
||||
'opacity': 0,
|
||||
'-webkit-mask-image': 'unset',
|
||||
'mask-image': 'unset'
|
||||
})
|
||||
}
|
||||
$opinionSlider.val(opinion)
|
||||
$opinionSlider[0].style.setProperty('--color', backgroundColorDot);
|
||||
$('input#OpinionOpinion').val(opinion)
|
||||
}
|
||||
|
||||
function genSlider() {
|
||||
var $div = $('<div style="display: inline-block;"></div>')
|
||||
var $opinionTemplate = $(opinionTemplate<?= $seed ?>)
|
||||
var $div = $div.append($opinionTemplate)
|
||||
return $div
|
||||
}
|
||||
|
||||
function initOpinionSlider() {
|
||||
var $input = $('.opinion-<?= $seed ?>')
|
||||
$input.css({
|
||||
'width': '2.5rem',
|
||||
'margin': '0 0.5rem 0 0',
|
||||
})
|
||||
$input.parent().append(genSlider())
|
||||
var currentOpinionValue = !Number.isNaN(Number.parseInt($input.val())) ? Number.parseInt($input.val()) : 50
|
||||
setOpinionLevel(currentOpinionValue)
|
||||
|
||||
$('.opinion-<?= $seed ?>').parent().find('#opinion-slider')
|
||||
.on('input', function(e) {
|
||||
setOpinionLevel(this.value)
|
||||
})
|
||||
$input.on('input', function(e) {
|
||||
setOpinionLevel(this.value)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
input#opinion-slider {
|
||||
position: absolute;
|
||||
width: 160px;
|
||||
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 6px;
|
||||
background: #ffffff00;
|
||||
outline: none;
|
||||
opacity: 0.8;
|
||||
-webkit-transition: .2s;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
#opinion-slider:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
|
||||
#opinion-slider::-webkit-slider-thumb {
|
||||
border-radius: 50%;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-shadow: 0 0 2px 0px #00000066;
|
||||
background-color: var(--color, white);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#opinion-slider::-moz-range-thumb {
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-shadow: 0 0 2px 0px #00000066;
|
||||
background-color: var(--color, white);
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -7,6 +7,9 @@ if ($quickedit) {
|
|||
}
|
||||
|
||||
$distributionLevel = Hash::extract($row, $field['data_path'])[0];
|
||||
if ($distributionLevel == 4) {
|
||||
$sg = empty($field['sg_path']) ? $row['SharingGroup'] : Hash::extract($row, $field['sg_path']);
|
||||
}
|
||||
|
||||
echo sprintf('<div%s>', $quickedit ? sprintf(
|
||||
" onmouseenter=\"quickEditHover(this, '%s', %s, 'distribution');\"",
|
||||
|
@ -25,8 +28,8 @@ echo sprintf(
|
|||
sprintf(
|
||||
'<a href="%s/sharing_groups/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($row['SharingGroup']['id']),
|
||||
h($row['SharingGroup']['name'])
|
||||
h($sg['id']),
|
||||
h($sg['name'])
|
||||
)
|
||||
);
|
||||
if ($quickedit) {
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
<?php
|
||||
$data = Hash::extract($row, $field['data_path']);
|
||||
echo h($data['model']) . ' #' . intval($data['model_id']);
|
||||
if (!empty($field['data_path'])) {
|
||||
$data = Hash::extract($row, $field['data_path']);
|
||||
if (isset($data['model_id'])) {
|
||||
echo h($data['model']) . ' #' . intval($data['model_id']);
|
||||
}
|
||||
} else {
|
||||
$model_name = Hash::extract($row, $field['model_name'])[0];
|
||||
$model_path = Inflector::Pluralize($model_name);
|
||||
$model_id = Hash::extract($row, $field['model_id'])[0];
|
||||
echo sprintf(
|
||||
'<a href="%s/%s/view/%s">%s (%s)</a>',
|
||||
$baseurl,
|
||||
h($model_path),
|
||||
h($model_id),
|
||||
h($model_name),
|
||||
h($model_id)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$opinion = Hash::get($row, $field['data_path']);
|
||||
|
||||
echo $this->element('genericElements/Analyst_data/opinion_scale', [
|
||||
'opinion' => $opinion,
|
||||
]);
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
$uuid = Hash::get($row, $field['data_path']);
|
||||
if (empty($uuid) || empty($field['object_type'])) {
|
||||
throw new MethodNotAllowedException(__('No UUID or object_type provided'), 500);
|
||||
|
||||
}
|
||||
$notes = Hash::extract($row, $field['notes_data_path'] ?? 'Note');
|
||||
$opinions = Hash::extract($row, $field['opinions_data_path'] ?? 'Opnion');
|
||||
$relationships = Hash::extract($row, $field['relationships_data_path'] ?? 'Relationship');
|
||||
echo $this->element('genericElements/shortUuidWithNotes', [
|
||||
'uuid' => $uuid,
|
||||
'object_type' => $field['object_type'],
|
||||
'notes' => $notes,
|
||||
'opinions' => $opinions,
|
||||
'relationships' => $relationships,
|
||||
]);
|
|
@ -265,6 +265,22 @@ $divider = '<li class="divider"></li>';
|
|||
'text' => __('Download as…')
|
||||
));
|
||||
echo $divider;
|
||||
if ($me['Role']['perm_modify']) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'onClick' => array(
|
||||
'function' => 'openGenericModal',
|
||||
'params' => [
|
||||
sprintf(
|
||||
'%s/collectionElements/addElementToCollection/Event/%s',
|
||||
$baseurl,
|
||||
h($event['Event']['uuid'])
|
||||
)
|
||||
]
|
||||
),
|
||||
'text' => __('Add Event to Collection')
|
||||
));
|
||||
echo $divider;
|
||||
}
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'url' => $baseurl . '/events/index',
|
||||
'text' => __('List Events')
|
||||
|
@ -314,7 +330,50 @@ $divider = '<li class="divider"></li>';
|
|||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'collections':
|
||||
if ($menuItem === 'edit' || $menuItem === 'view') {
|
||||
if ($this->Acl->canAccess('collections', 'add') && $mayModify) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', [
|
||||
'element_id' => 'edit',
|
||||
'url' => $baseurl . '/collections/edit/' . h($id),
|
||||
'text' => __('Edit Collection')
|
||||
]);
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', [
|
||||
'element_id' => 'delete',
|
||||
'onClick' => [
|
||||
'function' => 'openGenericModal',
|
||||
'params' => array($baseurl . '/Collections/delete/' . h($id))
|
||||
],
|
||||
'text' => __('Delete Collection')
|
||||
]);
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', [
|
||||
'text' => __('Add Element to Collection'),
|
||||
'onClick' => [
|
||||
'function' => 'openGenericModal',
|
||||
'params' => array($baseurl . '/CollectionElements/add/' . h($id))
|
||||
],
|
||||
]);
|
||||
echo $divider;
|
||||
}
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'view',
|
||||
'url' => $baseurl . '/collections/view/' . h($id),
|
||||
'text' => __('View Collection')
|
||||
));
|
||||
}
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'index',
|
||||
'url' => $baseurl . '/collections/index',
|
||||
'text' => __('List Collections')
|
||||
));
|
||||
if ($this->Acl->canAccess('collection', 'add')) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'add',
|
||||
'url' => $baseurl . '/collections/add',
|
||||
'text' => __('Add Collection')
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 'event-collection':
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'index',
|
||||
|
@ -1493,6 +1552,22 @@ $divider = '<li class="divider"></li>';
|
|||
'text' => __('View Correlation Graph')
|
||||
));
|
||||
}
|
||||
if ($me['Role']['perm_modify']) {
|
||||
echo $divider;
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'onClick' => array(
|
||||
'function' => 'openGenericModal',
|
||||
'params' => [
|
||||
sprintf(
|
||||
'%s/collectionElements/addElementToCollection/GalaxyCluster/%s',
|
||||
$baseurl,
|
||||
h($cluster['GalaxyCluster']['uuid'])
|
||||
)
|
||||
]
|
||||
),
|
||||
'text' => __('Add Cluster to Collection')
|
||||
));
|
||||
}
|
||||
}
|
||||
if ($menuItem === 'view' || $menuItem === 'export') {
|
||||
echo $divider;
|
||||
|
@ -1726,6 +1801,54 @@ $divider = '<li class="divider"></li>';
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'analyst_data':
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'index',
|
||||
'url' => '/analystData/index',
|
||||
'text' => __('List Analyst Data')
|
||||
));
|
||||
if ($this->Acl->canAccess('analyst_data_blocklists', 'index')) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'index_blocklist',
|
||||
'url' => $baseurl . '/analyst_data_blocklists/index',
|
||||
'text' => __('List Analyst-Data Blocklists')
|
||||
));
|
||||
}
|
||||
if ($this->Acl->canAccess('analyst_data', 'add')) {
|
||||
echo $divider;
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'add_note',
|
||||
'url' => sprintf('/analystData/add/Note'),
|
||||
'text' => __('Add Analyst Note')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'add_opinion',
|
||||
'url' => sprintf('/analystData/add/Opinion'),
|
||||
'text' => __('Add Analyst Opinion')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'add_relationship',
|
||||
'url' => sprintf('/analystData/add/Relationship'),
|
||||
'text' => __('Add Analyst Relationship')
|
||||
));
|
||||
}
|
||||
if ($menuItem === 'view' || $menuItem === 'edit') {
|
||||
echo $divider;
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'view',
|
||||
'url' => sprintf('/analystData/view/%s/%s', h($modelSelection), h($id)),
|
||||
'text' => __('View Analyst Data')
|
||||
));
|
||||
if ($isSiteAdmin) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'edit',
|
||||
'url' => sprintf('/analystData/edit/%s/%s', h($modelSelection), h($id)),
|
||||
'text' => __('Edit Analyst Data')
|
||||
));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
echo !empty($side_panel['html']) ? $side_panel['html'] : '';
|
||||
?>
|
|
@ -24,25 +24,27 @@ if ($distribution == 4) {
|
|||
}
|
||||
|
||||
$eventDistributionGraph = '';
|
||||
if (!($distribution == 4 && empty($sg))) {
|
||||
$eventDistributionGraph = sprintf(
|
||||
'%s %s %s',
|
||||
sprintf(
|
||||
'<span id="distribution_graph_bar" style="margin-left: 5px;" data-object-id="%s" data-object-context="event"></span>',
|
||||
h($event_id_path)
|
||||
),
|
||||
sprintf(
|
||||
'<it class="%s" data-object-id="%s" data-object-context="event" data-shown="false"></it><div style="display: none">%s</div>',
|
||||
'useCursorPointer fa fa-info-circle distribution_graph',
|
||||
h($event_id_path),
|
||||
$this->element('view_event_distribution_graph')
|
||||
),
|
||||
sprintf(
|
||||
'<it type="button" id="showAdvancedSharingButton" title="%s" class="%s" aria-hidden="true" style="margin-left: 5px;"></it>',
|
||||
__('Toggle advanced sharing network viewer'),
|
||||
'fa fa-share-alt useCursorPointer'
|
||||
)
|
||||
);
|
||||
if (empty($field['disable_distribution_graph'])) {
|
||||
if (!($distribution == 4 && empty($sg))) {
|
||||
$eventDistributionGraph = sprintf(
|
||||
'%s %s %s',
|
||||
sprintf(
|
||||
'<span id="distribution_graph_bar" style="margin-left: 5px;" data-object-id="%s" data-object-context="event"></span>',
|
||||
h($event_id_path)
|
||||
),
|
||||
sprintf(
|
||||
'<it class="%s" data-object-id="%s" data-object-context="event" data-shown="false"></it><div style="display: none">%s</div>',
|
||||
'useCursorPointer fa fa-info-circle distribution_graph',
|
||||
h($event_id_path),
|
||||
$this->element('view_event_distribution_graph')
|
||||
),
|
||||
sprintf(
|
||||
'<it type="button" id="showAdvancedSharingButton" title="%s" class="%s" aria-hidden="true" style="margin-left: 5px;"></it>',
|
||||
__('Toggle advanced sharing network viewer'),
|
||||
'fa fa-share-alt useCursorPointer'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
echo sprintf(
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
$opinion = Hash::get($data, $field['path']);
|
||||
|
||||
echo $this->element('genericElements/Analyst_data/opinion_scale', [
|
||||
'opinion' => $opinion,
|
||||
'forceInline' => true,
|
||||
]);
|
|
@ -4,3 +4,17 @@
|
|||
'<span class="quickSelect">%s</span>',
|
||||
h($uuid)
|
||||
);
|
||||
|
||||
if (!empty($field['object_type'])) {
|
||||
$field['notes_path'] = !empty($field['notes_path']) ? $field['notes_path'] : 'Note';
|
||||
$field['opinions_path'] = !empty($field['opinions_path']) ? $field['opinions_path'] : 'Opinion';
|
||||
$field['relationships_path'] = !empty($field['relationships_path']) ? $field['relationships_path'] : 'Relationship';
|
||||
$notes = !empty($field['notes']) ? $field['notes'] : Hash::extract($data, $field['notes_path']);
|
||||
$opinions = !empty($field['opinions']) ? $field['opinions'] : Hash::extract($data, $field['opinions_path']);
|
||||
$relationships = !empty($field['relationships']) ? $field['relationships'] : Hash::extract($data, $field['relationships_path']);
|
||||
echo $this->element('genericElements/Analyst_data/generic', [
|
||||
'analyst_data' => ['notes' => $notes, 'opinions' => $opinions, 'relationships' => $relationships],
|
||||
'object_uuid' => $uuid,
|
||||
'object_type' => $field['object_type']
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
$uuidHalfWidth = 3;
|
||||
$shortUUID = sprintf('%s...%s', substr($uuid, 0, $uuidHalfWidth), substr($uuid, 36-$uuidHalfWidth, $uuidHalfWidth));
|
||||
echo sprintf('<span title="%s">%s</span>', $uuid, $shortUUID);
|
||||
|
||||
if (!empty($object_type)) {
|
||||
$notes = !empty($notes) ? $notes : [];
|
||||
$opinions = !empty($opinions) ? $opinions : [];
|
||||
$relationships = !empty($relationships) ? $relationships : [];
|
||||
echo $this->element('genericElements/Analyst_data/generic', [
|
||||
'analyst_data' => ['notes' => $notes, 'opinions' => $opinions, 'relationships' => $relationships,],
|
||||
'object_uuid' => $uuid,
|
||||
'object_type' => $object_type
|
||||
]);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -371,17 +371,23 @@ $humanReadableFilesize = function ($bytes, $dec = 2) {
|
|||
<b><?= __('PHP extension version') ?>:</b> <?= $redisInfo['extensionVersion'] ?: ('<span class="red bold">' . __('Not installed.') . '</span>') ?><br>
|
||||
<?php if ($redisInfo['connection']): ?>
|
||||
<b><?= __('Redis version') ?>:</b> <?= h($redisInfo['redis_version']) ?><br>
|
||||
<?php if (isset($redisInfo['dfly_version']) || isset($redisInfo['dragonfly_version'])): ?>
|
||||
<b><?= __('Dragonfly version') ?>:</b> <?= h($redisInfo['dfly_version'] ?? $redisInfo['dragonfly_version']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($redisInfo['mem_allocator'])): ?>
|
||||
<b><?= __('Memory allocator') ?>:</b> <?= h($redisInfo['mem_allocator']) ?><br>
|
||||
<?php endif; ?>
|
||||
<b><?= __('Memory usage') ?>:</b> <?= $humanReadableFilesize($redisInfo['used_memory']) ?><br>
|
||||
<?php if (isset($redisInfo['mem_allocator'])): ?>
|
||||
<?php if (isset($redisInfo['used_memory_peak'])): ?>
|
||||
<b><?= __('Peak memory usage') ?>:</b> <?= $humanReadableFilesize($redisInfo['used_memory_peak']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($redisInfo['mem_fragmentation_ratio'])): ?>
|
||||
<b><?= __('Fragmentation ratio') ?>:</b> <?= h($redisInfo['mem_fragmentation_ratio']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($redisInfo['total_system_memory_human'])): ?>
|
||||
<?php if (isset($redisInfo['maxmemory'])): ?>
|
||||
<b><?= __('Maximum memory') ?>:</b> <?= $humanReadableFilesize($redisInfo['maxmemory']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($redisInfo['total_system_memory'])): ?>
|
||||
<b><?= __('Total system memory') ?>:</b> <?= $humanReadableFilesize($redisInfo['total_system_memory']) ?>
|
||||
<?php endif; ?>
|
||||
<?php elseif ($redisInfo['extensionVersion']): ?>
|
||||
|
@ -390,23 +396,23 @@ $humanReadableFilesize = function ($bytes, $dec = 2) {
|
|||
</div>
|
||||
|
||||
<h3><?php echo __('Advanced attachment handler');?></h3>
|
||||
<?php echo __('The advanced attachment tools are used by the add attachment functionality to extract additional data about the uploaded sample.');?>
|
||||
<div class="diagnostics-box">
|
||||
<?php
|
||||
if (empty($advanced_attachments)):
|
||||
?>
|
||||
<b><?php echo __('PyMISP');?></b>:… <span class="red bold"><?php echo __('Not installed or version outdated.');?></span><br />
|
||||
<?php
|
||||
endif;
|
||||
if (!empty($advanced_attachments)):
|
||||
foreach ($advanced_attachments as $k => $v):
|
||||
?>
|
||||
<b><?php echo h($k); ?></b>:… <?php echo $v === false ? '<span class="green bold">' . __('OK') . '</span>' : '<span class="red bold">' . h($v) . '</span>'; ?><br />
|
||||
<?php
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
<?php echo __('The advanced attachment tools are used by the add attachment functionality to extract additional data about the uploaded sample.');?>
|
||||
<div class="diagnostics-box">
|
||||
<?php
|
||||
if (empty($advanced_attachments)):
|
||||
?>
|
||||
<b><?php echo __('PyMISP');?></b>:… <span class="red bold"><?php echo __('Not installed or version outdated.');?></span><br>
|
||||
<?php
|
||||
endif;
|
||||
if (!empty($advanced_attachments)):
|
||||
foreach ($advanced_attachments as $k => $v):
|
||||
?>
|
||||
<b><?php echo h($k); ?></b>:… <?php echo $v === false ? '<span class="green bold">' . __('OK') . '</span>' : '<span class="red bold">' . h($v) . '</span>'; ?><br>
|
||||
<?php
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
|
||||
<h3><?= __('Attachment scan module') ?></h3>
|
||||
<div class="diagnostics-box">
|
||||
|
|
|
@ -73,6 +73,17 @@
|
|||
'class' => 'short',
|
||||
'data_path' => 'EventReport.id',
|
||||
),
|
||||
array(
|
||||
'name' => __('Context'),
|
||||
'sort' => 'uuid',
|
||||
'class' => 'short',
|
||||
'data_path' => 'EventReport.uuid',
|
||||
'notes_data_path' => 'Note',
|
||||
'opinions_data_path' => 'Opinion',
|
||||
'relationships_data_path' => 'Relationship',
|
||||
'element' => 'shortUUIDWithNotes',
|
||||
'object_type' => 'EventReport',
|
||||
),
|
||||
array(
|
||||
'name' => __('Name'),
|
||||
'class' => 'useCursorPointer',
|
||||
|
|
|
@ -52,6 +52,17 @@
|
|||
'element' => 'links',
|
||||
'url' => $baseurl . '/eventReports/view/%s'
|
||||
),
|
||||
array(
|
||||
'name' => __('Context'),
|
||||
'sort' => 'uuid',
|
||||
'class' => 'short',
|
||||
'data_path' => 'EventReport.uuid',
|
||||
'notes_data_path' => 'Note',
|
||||
'opinions_data_path' => 'Opinion',
|
||||
'relationships_data_path' => 'Relationship',
|
||||
'element' => 'shortUUIDWithNotes',
|
||||
'object_type' => 'EventReport',
|
||||
),
|
||||
array(
|
||||
'name' => __('Name'),
|
||||
'data_path' => 'EventReport.name',
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/assetLoader', [
|
||||
'js' => ['doT', 'moment.min'],
|
||||
'css' => ['analyst-data',],
|
||||
]);
|
||||
|
||||
$table_data = array();
|
||||
$table_data[] = array('key' => __('ID'), 'value' => $report['EventReport']['id']);
|
||||
$table_data[] = array('key' => __('UUID'), 'value' => $report['EventReport']['uuid'], 'value_class' => 'quickSelect');
|
||||
$table_data[] = [
|
||||
'key' => __('UUID'),
|
||||
'element' => 'genericElements/SingleViews/Fields/uuidField',
|
||||
'element_params' => [
|
||||
'data' => $report,
|
||||
'field' => [
|
||||
'path' => 'EventReport.uuid',
|
||||
'object_type' => 'EventReport',
|
||||
]
|
||||
],
|
||||
];
|
||||
$table_data[] = array(
|
||||
'key' => __('Event'),
|
||||
'html' => sprintf(
|
||||
|
|
|
@ -28,10 +28,13 @@
|
|||
'selected' => $initialDistribution,
|
||||
));
|
||||
if (!empty($sharingGroups)) {
|
||||
echo $this->Form->input('sharing_group_id', array(
|
||||
'options' => array($sharingGroups),
|
||||
'label' => __('Sharing Group'),
|
||||
));
|
||||
$SGContainer = $this->Form->input(
|
||||
'sharing_group_id', array(
|
||||
'options' => array($sharingGroups),
|
||||
'label' => __('Sharing Group'),
|
||||
)
|
||||
);
|
||||
echo '<div id="SGContainer" style="display:none;">' . $SGContainer . '</div>';
|
||||
}
|
||||
?>
|
||||
<div class="input clear"></div>
|
||||
|
@ -64,6 +67,36 @@
|
|||
'label' => __('How to handle Galaxies and Clusters') . $galaxiesFormInfo,
|
||||
'selected' => 0
|
||||
));
|
||||
?>
|
||||
<div class="input clear"></div>
|
||||
<?php
|
||||
$clusterDistributionFormInfo = $this->element(
|
||||
'genericElements/Form/formInfo',
|
||||
[
|
||||
'field' => [
|
||||
'field' => 'cluster_distribution'
|
||||
],
|
||||
'modelForForm' => 'Event',
|
||||
'fieldDesc' => $fieldDesc['distribution'],
|
||||
]
|
||||
);
|
||||
$clusterDistribution = $this->Form->input(
|
||||
'cluster_distribution', array(
|
||||
'options' => $distributionLevels,
|
||||
'label' => __('Cluster distribution ') . $clusterDistributionFormInfo,
|
||||
'selected' => $initialDistribution,
|
||||
)
|
||||
);
|
||||
echo '<div id="ClusterDistribution" style="display:none;">' . $clusterDistribution . '</div>';
|
||||
if (!empty($sharingGroups)) {
|
||||
$clusterSGContainer = $this->Form->input(
|
||||
'cluster_sharing_group_id', array(
|
||||
'options' => array($sharingGroups),
|
||||
'label' => __('Cluster Sharing Group'),
|
||||
)
|
||||
);
|
||||
echo '<div id="ClusterSGContainer" style="display:none;">' . $clusterSGContainer . '</div>';
|
||||
}
|
||||
}
|
||||
if ($me['Role']['perm_site_admin'] && Configure::read('debug') > 0) {
|
||||
$debugFormInfo = $this->element(
|
||||
|
@ -101,4 +134,26 @@ $(function(){
|
|||
});
|
||||
checkSharingGroup('Event');
|
||||
});
|
||||
$(function(){
|
||||
$('#EventGalaxiesHandling').change(function() {
|
||||
if ($(this).val() == 0) {
|
||||
$('#ClusterDistribution').show();
|
||||
if ($('#EventClusterDistribution').val() == 4) {
|
||||
$('#ClusterSGContainer').show();
|
||||
}
|
||||
} else {
|
||||
$('#ClusterDistribution').hide();
|
||||
$('#ClusterSGContainer').hide();
|
||||
}
|
||||
}).change();
|
||||
});
|
||||
$(function(){
|
||||
$('#EventClusterDistribution').change(function() {
|
||||
if ($(this).val() == 4 && $('#EventGalaxiesHandling').val() == 0) {
|
||||
$('#ClusterSGContainer').show();
|
||||
} else {
|
||||
$('#ClusterSGContainer').hide();
|
||||
}
|
||||
}).change();
|
||||
});
|
||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/assetLoader', [
|
||||
'css' => ['query-builder.default', 'attack_matrix'],
|
||||
'css' => ['query-builder.default', 'attack_matrix', 'analyst-data'],
|
||||
'js' => ['doT', 'extendext', 'moment.min', 'query-builder', 'network-distribution-graph', 'd3', 'd3.custom', 'jquery-ui.min'],
|
||||
]);
|
||||
echo $this->element(
|
||||
|
@ -16,8 +16,12 @@
|
|||
[
|
||||
'key' => 'UUID',
|
||||
'path' => 'Event.uuid',
|
||||
'class' => 'quickSelect',
|
||||
'class' => '',
|
||||
'type' => 'uuid',
|
||||
'object_type' => 'Event',
|
||||
'notes_path' => 'Note',
|
||||
'opinions_path' => 'Opinion',
|
||||
'relationship_path' => 'Relationship',
|
||||
'action_buttons' => [
|
||||
[
|
||||
'url' => $baseurl . '/events/add/extends:' . h($event['Event']['uuid']),
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/assetLoader', [
|
||||
'js' => ['doT', 'moment.min'],
|
||||
'css' => ['analyst-data',],
|
||||
]);
|
||||
|
||||
$extendedFromHtml = '';
|
||||
if (!empty($cluster['GalaxyCluster']['extended_from'])) {
|
||||
$element = $this->element('genericElements/IndexTable/Fields/links', array(
|
||||
|
@ -50,7 +55,20 @@ if (!$cluster['GalaxyCluster']['default']) {
|
|||
}
|
||||
$table_data[] = array('key' => __('Default'), 'boolean' => $cluster['GalaxyCluster']['default'], 'class' => 'black');
|
||||
$table_data[] = array('key' => __('Version'), 'value' => $cluster['GalaxyCluster']['version']);
|
||||
$table_data[] = array('key' => __('UUID'), 'value' => $cluster['GalaxyCluster']['uuid'], 'value_class' => 'quickSelect');
|
||||
$table_data[] = [
|
||||
'key' => __('UUID'),
|
||||
'element' => 'genericElements/SingleViews/Fields/uuidField',
|
||||
'element_params' => [
|
||||
'data' => $cluster,
|
||||
'field' => [
|
||||
'path' => 'GalaxyCluster.uuid',
|
||||
'object_type' => 'GalaxyCluster',
|
||||
'notes_path' => 'GalaxyCluster.Note',
|
||||
'opinions_path' => 'GalaxyCluster.Opinion',
|
||||
'relationships_path' => 'GalaxyCluster.Relationship',
|
||||
]
|
||||
],
|
||||
];
|
||||
$table_data[] = array('key' => __('Collection UUID'), 'value' => $cluster['GalaxyCluster']['collection_uuid'], 'value_class' => 'quickSelect');
|
||||
$table_data[] = array(
|
||||
'key' => __('Source'),
|
||||
|
@ -94,6 +112,9 @@ if (!empty($extendedByHtml)) {
|
|||
</h2>
|
||||
<?php echo $this->element('genericElements/viewMetaTable', array('table_data' => $table_data)); ?>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<div id="analyst_data_thread" class="panel-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fuild">
|
||||
<div id="matrix_container"></div>
|
||||
|
@ -143,4 +164,21 @@ md.disable(['image'])
|
|||
var $md = $('.md');
|
||||
$md.html(md.render($md.text()));
|
||||
</script>
|
||||
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'galaxies', 'menuItem' => 'view_cluster'));
|
||||
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'galaxies', 'menuItem' => 'view_cluster')); ?>
|
||||
|
||||
<?php
|
||||
|
||||
$object_uuid = $cluster['GalaxyCluster']['uuid'];
|
||||
$options = [
|
||||
'container_id' => 'analyst_data_thread',
|
||||
'object_type' => 'GalaxyCluster',
|
||||
'object_uuid' => $object_uuid,
|
||||
'shortDist' => $shortDist,
|
||||
'notes' => $cluster['GalaxyCluster']['Note'] ?? [],
|
||||
'opinions' => $cluster['GalaxyCluster']['Opinion'] ?? [],
|
||||
'relationships' => $cluster['GalaxyCluster']['Relationship'] ?? [],
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/Analyst_data/thread', $options);
|
||||
|
||||
?>
|
|
@ -115,4 +115,13 @@ class AclHelper extends Helper
|
|||
{
|
||||
return $this->ACL->canModifyGalaxyCluster($this->me, $cluster);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $cluster
|
||||
* @return bool
|
||||
*/
|
||||
public function canEditAnalystData(array $analystData, $modelType): bool
|
||||
{
|
||||
return $this->ACL->canEditAnalystData($this->me, $analystData, $modelType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
App::uses('AppHelper', 'View/Helper');
|
||||
App::uses('FileAccessTool', 'Lib/Tools');
|
||||
|
||||
class ImageHelper extends AppHelper
|
||||
{
|
||||
/** @var array */
|
||||
private $imageCache = [];
|
||||
|
||||
/**
|
||||
* @param string $imagePath Path to file
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function base64($imagePath)
|
||||
{
|
||||
if (isset($this->imageCache[$imagePath])) {
|
||||
return $this->imageCache[$imagePath];
|
||||
}
|
||||
|
||||
$ext = pathinfo($imagePath, PATHINFO_EXTENSION);
|
||||
if ($ext === 'svg') {
|
||||
$mime = 'image/svg+xml';
|
||||
} else if ($ext === 'png') {
|
||||
$mime = 'image/png';
|
||||
} else {
|
||||
throw new InvalidArgumentException("Only SVG and PNG images are supported");
|
||||
}
|
||||
|
||||
try {
|
||||
$fileContent = FileAccessTool::readFromFile($imagePath);
|
||||
} catch (Exception $e) {
|
||||
CakeLog::warning($e);
|
||||
return 'data:null'; // in case file doesn't exists or is not readable
|
||||
}
|
||||
|
||||
$fileContentEncoded = base64_encode($fileContent);
|
||||
$base64 = "data:$mime;base64,$fileContentEncoded";
|
||||
|
||||
return $this->imageCache[$imagePath] = $base64;
|
||||
}
|
||||
}
|
|
@ -22,8 +22,8 @@ class OrgImgHelper extends AppHelper
|
|||
$link = $baseurl . '/organisations/view/' . (empty($organisation['Organisation']['id']) ? h($organisation['Organisation']['name']) : h($organisation['Organisation']['id']));
|
||||
}
|
||||
if ($orgImgName) {
|
||||
$orgImgUrl = $baseurl . '/img/orgs/' . $orgImgName;
|
||||
return sprintf('<a href="%s" style="background-image: url(\'%s\')" class="orgImg">%s</a>', $link, $orgImgUrl, h($organisation['Organisation']['name']));
|
||||
$base64 = $this->_View->Image->base64(self::IMG_PATH . $orgImgName);
|
||||
return sprintf('<a href="%s" style="background-image: url(\'%s\')" class="orgImg">%s</a>', $link, $base64, h($organisation['Organisation']['name']));
|
||||
} else {
|
||||
return sprintf('<a href="%s">%s</a>', $link, h($organisation['Organisation']['name']));
|
||||
}
|
||||
|
@ -56,9 +56,8 @@ class OrgImgHelper extends AppHelper
|
|||
if ($orgImgName) {
|
||||
$size = !empty($options['size']) ? $options['size'] : 48;
|
||||
$result = sprintf(
|
||||
'<img src="data:image/%s;base64,%s" title="%s" width="%s" height="%s">',
|
||||
'png',
|
||||
base64_encode(FileAccessTool::readFromFile(self::IMG_PATH . $orgImgName)),
|
||||
'<img src="%s" title="%s" width="%s" height="%s">',
|
||||
$this->_View->Image->base64(self::IMG_PATH . $orgImgName),
|
||||
isset($options['name']) ? h($options['name']) : h($options['id']),
|
||||
(int)$size,
|
||||
(int)$size
|
||||
|
|
|
@ -83,7 +83,7 @@ foreach ($list as $template):
|
|||
<?php
|
||||
if ($template['ObjectTemplate']['fixed']):
|
||||
?>
|
||||
<img src="<?php echo $baseurl;?>/img/orgs/MISP.png" width="24" height="24" style="padding-bottom:3px;" />
|
||||
<?php echo '<img src="' . $this->Image->base64(APP . 'files/img/orgs/MISP.png') . '" alt="' . __('MISP logo') . '" width="24" height="24" style="padding-bottom:3px" onerror="this.style.display=\'none\';">';?>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
|
|
|
@ -93,6 +93,8 @@
|
|||
echo $this->Form->input('caching_enabled', array());
|
||||
echo $this->Form->input('push_galaxy_clusters', array());
|
||||
echo $this->Form->input('pull_galaxy_clusters', array());
|
||||
echo $this->Form->input('push_analyst_data', array());
|
||||
echo $this->Form->input('pull_analyst_data', array());
|
||||
echo '<div class="input clear" style="width:100%;"><hr><h4>' . __('Misc settings') . '</h4></div>';
|
||||
echo $this->Form->input('unpublish_event', array(
|
||||
'type' => 'checkbox',
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
<th><?php echo $this->Paginator->sort('push_sightings', 'Push Sightings');?></th>
|
||||
<th><?php echo $this->Paginator->sort('push_galaxy_clusters', 'Push Clusters');?></th>
|
||||
<th><?php echo $this->Paginator->sort('pull_galaxy_clusters', 'Pull Clusters');?></th>
|
||||
<th><?php echo $this->Paginator->sort('push_analyst_data', 'Push Analyst Data');?></th>
|
||||
<th><?php echo $this->Paginator->sort('pull_analyst_data', 'Pull Analyst Data');?></th>
|
||||
<th><?php echo $this->Paginator->sort('caching_enabled', 'Cache');?></th>
|
||||
<th><?php echo $this->Paginator->sort('unpublish_event');?></th>
|
||||
<th><?php echo $this->Paginator->sort('publish_without_email');?></th>
|
||||
|
@ -120,6 +122,8 @@ foreach ($servers as $server):
|
|||
<td class="short"><span class="<?= $server['Server']['push_sightings'] ? 'fa fa-check' : 'fa fa-times' ?>" role="img" aria-label="<?= $server['Server']['push_sightings'] ? __('Yes') : __('No'); ?>"></span></td>
|
||||
<td class="short"><span class="<?= $server['Server']['push_galaxy_clusters'] ? 'fa fa-check' : 'fa fa-times' ?>" role="img" aria-label="<?= $server['Server']['push_galaxy_clusters'] ? __('Yes') : __('No'); ?>"></span></td>
|
||||
<td class="short"><span class="<?= $server['Server']['pull_galaxy_clusters'] ? 'fa fa-check' : 'fa fa-times' ?>" role="img" aria-label="<?= $server['Server']['pull_galaxy_clusters'] ? __('Yes') : __('No'); ?>"></span></td>
|
||||
<td class="short"><span class="<?= $server['Server']['push_analyst_data'] ? 'fa fa-check' : 'fa fa-times' ?>" role="img" aria-label="<?= $server['Server']['push_analyst_data'] ? __('Yes') : __('No'); ?>"></span></td>
|
||||
<td class="short"><span class="<?= $server['Server']['pull_analyst_data'] ? 'fa fa-check' : 'fa fa-times' ?>" role="img" aria-label="<?= $server['Server']['pull_analyst_data'] ? __('Yes') : __('No'); ?>"></span></td>
|
||||
<td>
|
||||
<?php
|
||||
if ($server['Server']['caching_enabled']) {
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
<table style="margin-left:auto;margin-right:auto;">
|
||||
<tr>
|
||||
<td style="text-align:right;width:250px;padding-right:50px">
|
||||
<?php if (Configure::read('MISP.welcome_logo')) echo $this->Html->image('custom/' . h(Configure::read('MISP.welcome_logo')), array('alt' => __('Logo'), 'onerror' => "this.style.display='none';")); ?>
|
||||
<?php if (Configure::read('MISP.welcome_logo') && file_exists(APP . '/files/img/custom/' . Configure::read('MISP.welcome_logo'))): ?>
|
||||
<img src="<?= $this->Image->base64(APP . 'files/img/custom/' . Configure::read('MISP.welcome_logo')) ?>" alt="<?= __('Logo') ?>" onerror="this.style.display='none';">
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td style="width:460px">
|
||||
<span style="font-size:18px;">
|
||||
|
@ -16,10 +18,10 @@
|
|||
?>
|
||||
</span><br /><br />
|
||||
<div>
|
||||
<?php if (Configure::read('MISP.main_logo') && file_exists(APP . '/webroot/img/custom/' . Configure::read('MISP.main_logo'))): ?>
|
||||
<img src="<?php echo $baseurl?>/img/custom/<?php echo h(Configure::read('MISP.main_logo'));?>" style=" display:block; margin-left: auto; margin-right: auto;" />
|
||||
<?php if (Configure::read('MISP.main_logo') && file_exists(APP . '/files/img/custom/' . Configure::read('MISP.main_logo'))): ?>
|
||||
<img src="<?= $this->Image->base64(APP . 'files/img/custom/' . Configure::read('MISP.main_logo')) ?>" style=" display:block; margin-left: auto; margin-right: auto;">
|
||||
<?php else: ?>
|
||||
<img src="<?php echo $baseurl?>/img/misp-logo-s-u.png" style="display:block; margin-left: auto; margin-right: auto;"/>
|
||||
<img src="<?php echo $baseurl?>/img/misp-logo-s-u.png" style="display:block; margin-left: auto; margin-right: auto;">
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php
|
||||
|
@ -82,7 +84,9 @@
|
|||
?>
|
||||
</td>
|
||||
<td style="width:250px;padding-left:50px">
|
||||
<?php if (Configure::read('MISP.welcome_logo2')) echo $this->Html->image('custom/' . h(Configure::read('MISP.welcome_logo2')), array('alt' => 'Logo2', 'onerror' => "this.style.display='none';")); ?>
|
||||
<?php if (Configure::read('MISP.welcome_logo2') && file_exists(APP . '/files/img/custom/' . Configure::read('MISP.welcome_logo2'))): ?>
|
||||
<img src="<?= $this->Image->base64(APP . 'files/img/custom/' . Configure::read('MISP.welcome_logo2')) ?>" alt="<?= __('Logo2') ?>" onerror="this.style.display='none';">
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
<td style="width:460px">
|
||||
<br /><br />
|
||||
<div>
|
||||
<?php if (Configure::read('MISP.main_logo') && file_exists(APP . '/webroot/img/custom/' . Configure::read('MISP.main_logo'))): ?>
|
||||
<img src="<?php echo $baseurl?>/img/custom/<?php echo h(Configure::read('MISP.main_logo'));?>" style=" display:block; margin-left: auto; margin-right: auto;" />
|
||||
<?php if (Configure::read('MISP.main_logo') && file_exists(APP . '/files/img/custom/' . Configure::read('MISP.main_logo'))): ?>
|
||||
<img src="<?= $this->Image->base64(APP . 'files/img/custom/' . Configure::read('MISP.main_logo')) ?>" style=" display:block; margin-left: auto; margin-right: auto;">
|
||||
<?php else: ?>
|
||||
<img src="<?php echo $baseurl?>/img/misp-logo-s-u.png" style="display:block; margin-left: auto; margin-right: auto;"/>
|
||||
<?php endif;?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 7e8d57e741ee1ba6e764c1a5e0ba236fc2f64126
|
||||
Subproject commit 838f6497660af602c5d7bde93ce34be1783287dd
|
|
@ -1 +1 @@
|
|||
Subproject commit 260920bf7c9d8f678b0d69730acb17e9a34811f2
|
||||
Subproject commit 0428b4a43a67feed33837107ece1b144042c619e
|
|
@ -29,10 +29,31 @@ sys.path.insert(2, str(_scripts_path / 'python-cybox'))
|
|||
sys.path.insert(3, str(_scripts_path / 'mixbox'))
|
||||
sys.path.insert(4, str(_scripts_path / 'misp-stix'))
|
||||
from misp_stix_converter import (
|
||||
ExternalSTIX2toMISPParser, InternalSTIX2toMISPParser, _from_misp)
|
||||
ExternalSTIX2toMISPParser, InternalSTIX2toMISPParser,
|
||||
MISP_org_uuid, _from_misp)
|
||||
from stix2.parsing import parse as stix2_parser
|
||||
|
||||
|
||||
def _get_stix_parser(from_misp, args):
|
||||
arguments = {
|
||||
'distribution': args.distribution,
|
||||
'galaxies_as_tags': args.galaxies_as_tags
|
||||
}
|
||||
if args.distribution == 4 and args.sharing_group_id is not None:
|
||||
arguments['sharing_group_id'] = args.sharing_group_id
|
||||
if from_misp:
|
||||
return 'InternalSTIX2toMISPParser', arguments
|
||||
arguments.update(
|
||||
{
|
||||
'cluster_distribution': args.cluster_distribution,
|
||||
'organisation_uuid': args.org_uuid
|
||||
}
|
||||
)
|
||||
if args.cluster_distribution == 4 and args.cluster_sharing_group_id is not None:
|
||||
arguments['cluster_sharing_group_id'] = args.cluster_sharing_group_id
|
||||
return 'ExternalSTIX2toMISPParser', arguments
|
||||
|
||||
|
||||
def _handle_return_message(traceback):
|
||||
if isinstance(traceback, dict):
|
||||
messages = []
|
||||
|
@ -51,16 +72,10 @@ def _process_stix_file(args: argparse.Namespace):
|
|||
f.read(), allow_custom=True, interoperability=True
|
||||
)
|
||||
stix_version = getattr(bundle, 'version', '2.1')
|
||||
to_call = 'Internal' if _from_misp(bundle.objects) else 'External'
|
||||
arguments = {
|
||||
'distribution': args.distribution,
|
||||
'galaxies_as_tags': args.galaxies_as_tags
|
||||
}
|
||||
if args.distribution == 4 and args.sharing_group_id is not None:
|
||||
arguments['sharing_group_id'] = args.sharing_group_id
|
||||
parser = globals()[f'{to_call}STIX2toMISPParser'](**arguments)
|
||||
to_call, arguments = _get_stix_parser(_from_misp(bundle.objects), args)
|
||||
parser = globals()[to_call](**arguments)
|
||||
parser.load_stix_bundle(bundle)
|
||||
parser.parse_stix_bundle()
|
||||
parser.parse_stix_bundle(single_event=True)
|
||||
with open(f'{args.input}.out', 'wt', encoding='utf-8') as f:
|
||||
f.write(parser.misp_event.to_json())
|
||||
print(
|
||||
|
@ -94,6 +109,10 @@ if __name__ == '__main__':
|
|||
'-i', '--input', required=True, type=Path,
|
||||
help='Input file containing STIX 2 content.'
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--org_uuid', default=MISP_org_uuid,
|
||||
help='Organisation UUID to use when creating custom Galaxy clusters.'
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--distribution', type=int, default=0,
|
||||
help='Distribution level for the resulting MISP Event.'
|
||||
|
@ -110,6 +129,14 @@ if __name__ == '__main__':
|
|||
'--galaxies_as_tags', action='store_true',
|
||||
help='Import MISP Galaxies as tag names.'
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--cluster_distribution', type=int, default=0,
|
||||
help='Cluster distribution level for clusters generated from STIX 2.x objects'
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--cluster_sharing_group_id', type=int,
|
||||
help='Cluster sharing group id when the cluster distribution level is 4.'
|
||||
)
|
||||
try:
|
||||
args = argparser.parse_args()
|
||||
except SystemExit as e:
|
||||
|
@ -122,4 +149,4 @@ if __name__ == '__main__':
|
|||
)
|
||||
sys.exit(1)
|
||||
|
||||
_process_stix_file(args)
|
||||
_process_stix_file(args)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 80eb7028f9de974d7f163a7563e66b582f61cec0
|
||||
Subproject commit 2765252e7d097703828cd8b3bdd63ba8ba75c63b
|
|
@ -0,0 +1,31 @@
|
|||
.opinion-gradient-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.opinion-gradient {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
}
|
||||
.opinion-gradient-positive {
|
||||
border-radius: 0 3px 3px 0;
|
||||
background-image: linear-gradient(90deg, rgb(237, 212, 0), rgb(236, 211, 0), rgb(234, 211, 0), rgb(233, 210, 0), rgb(231, 210, 0), rgb(230, 209, 1), rgb(229, 209, 1), rgb(227, 208, 1), rgb(226, 208, 1), rgb(224, 207, 1), rgb(223, 207, 1), rgb(222, 206, 1), rgb(220, 206, 1), rgb(219, 205, 1), rgb(218, 204, 1), rgb(216, 204, 2), rgb(215, 203, 2), rgb(213, 203, 2), rgb(212, 202, 2), rgb(211, 202, 2), rgb(209, 201, 2), rgb(208, 201, 2), rgb(206, 200, 2), rgb(205, 200, 2), rgb(204, 199, 2), rgb(202, 199, 2), rgb(201, 198, 3), rgb(199, 197, 3), rgb(198, 197, 3), rgb(197, 196, 3), rgb(195, 196, 3), rgb(194, 195, 3), rgb(192, 195, 3), rgb(191, 194, 3), rgb(189, 194, 3), rgb(188, 193, 3), rgb(186, 193, 3), rgb(185, 192, 4), rgb(183, 192, 4), rgb(182, 191, 4), rgb(180, 190, 4), rgb(179, 190, 4), rgb(177, 189, 4), rgb(175, 189, 4), rgb(174, 188, 4), rgb(173, 188, 4), rgb(171, 187, 4), rgb(170, 186, 4), rgb(168, 186, 4), rgb(167, 185, 4), rgb(165, 185, 4), rgb(164, 184, 4), rgb(162, 183, 4), rgb(161, 183, 4), rgb(159, 182, 4), rgb(158, 182, 4), rgb(156, 181, 4), rgb(154, 180, 4), rgb(153, 180, 4), rgb(151, 179, 4), rgb(149, 179, 5), rgb(148, 178, 5), rgb(146, 178, 5), rgb(144, 177, 5), rgb(143, 177, 5), rgb(141, 176, 5), rgb(139, 175, 5), rgb(138, 175, 5), rgb(136, 174, 5), rgb(134, 173, 5), rgb(133, 173, 5), rgb(131, 172, 5), rgb(130, 172, 5), rgb(128, 171, 5), rgb(126, 170, 5), rgb(125, 170, 5), rgb(123, 169, 5), rgb(121, 168, 5), rgb(119, 168, 5), rgb(117, 167, 5), rgb(115, 167, 5), rgb(113, 166, 6), rgb(112, 165, 6), rgb(110, 165, 6), rgb(108, 164, 6), rgb(106, 163, 6), rgb(104, 163, 6), rgb(102, 162, 6), rgb(100, 162, 6), rgb(98, 161, 6), rgb(96, 160, 6), rgb(94, 159, 6), rgb(92, 159, 6), rgb(90, 158, 6), rgb(88, 157, 6), rgb(86, 157, 6), rgb(84, 156, 6), rgb(82, 155, 6), rgb(80, 155, 6),rgb(78, 154, 6))
|
||||
}
|
||||
.opinion-gradient-negative {
|
||||
border-radius: 3px 0 0 3px;
|
||||
background-image: linear-gradient(90deg, rgb(164, 0, 0), rgb(165, 8, 0), rgb(166, 15, 0), rgb(167, 21, 0), rgb(169, 25, 0), rgb(170, 30, 0), rgb(171, 33, 0), rgb(172, 37, 0), rgb(173, 40, 0), rgb(174, 43, 0), rgb(175, 46, 0), rgb(176, 49, 0), rgb(177, 52, 0), rgb(178, 55, 0), rgb(179, 57, 0), rgb(180, 60, 0), rgb(181, 63, 0), rgb(182, 65, 0), rgb(183, 68, 0), rgb(184, 70, 0), rgb(186, 72, 0), rgb(187, 75, 0), rgb(188, 77, 0), rgb(189, 80, 0), rgb(190, 82, 0), rgb(190, 84, 0), rgb(191, 86, 0), rgb(192, 88, 0), rgb(193, 90, 0), rgb(194, 92, 0), rgb(195, 95, 0), rgb(196, 96, 0), rgb(197, 98, 0), rgb(197, 100, 0), rgb(198, 102, 0), rgb(199, 104, 0), rgb(200, 106, 0), rgb(201, 108, 0), rgb(201, 110, 0), rgb(202, 112, 0), rgb(203, 114, 0), rgb(204, 116, 0), rgb(204, 118, 0), rgb(205, 119, 0), rgb(206, 121, 0), rgb(207, 123, 0), rgb(208, 125, 0), rgb(208, 127, 0), rgb(209, 128, 0), rgb(210, 130, 0), rgb(210, 132, 0), rgb(211, 134, 0), rgb(212, 135, 0), rgb(212, 137, 0), rgb(213, 139, 0), rgb(214, 141, 0), rgb(214, 143, 0), rgb(215, 144, 0), rgb(216, 146, 0), rgb(216, 148, 0), rgb(217, 149, 0), rgb(217, 151, 0), rgb(218, 153, 0), rgb(219, 154, 0), rgb(219, 156, 0), rgb(220, 158, 0), rgb(220, 160, 0), rgb(221, 161, 0), rgb(222, 163, 0), rgb(222, 164, 0), rgb(223, 166, 0), rgb(223, 168, 0), rgb(224, 169, 0), rgb(225, 171, 0), rgb(225, 173, 0), rgb(226, 174, 0), rgb(226, 176, 0), rgb(227, 178, 0), rgb(227, 179, 0), rgb(227, 181, 0), rgb(228, 182, 0), rgb(228, 184, 0), rgb(229, 186, 0), rgb(229, 187, 0), rgb(230, 189, 0), rgb(230, 190, 0), rgb(231, 192, 0), rgb(231, 193, 0), rgb(232, 195, 0), rgb(232, 196, 0), rgb(233, 198, 0), rgb(233, 200, 0), rgb(234, 201, 0), rgb(234, 203, 0), rgb(235, 204, 0), rgb(235, 206, 0), rgb(236, 207, 0), rgb(236, 209, 0), rgb(237, 210, 0), rgb(237, 212, 0));
|
||||
}
|
||||
|
||||
.opinion-gradient-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
z-index: 10;
|
||||
box-shadow: 0 0 2px 0px #00000066;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
}
|
|
@ -2936,6 +2936,10 @@ Query builder
|
|||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.highlight-on-hover:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.special-tag {
|
||||
animation: special-tag-color 4s infinite linear;
|
||||
}
|
||||
|
|
|
@ -1935,7 +1935,9 @@ function popoverConfirm(clicked, message, placement, callback) {
|
|||
var href = $clicked.attr("href");
|
||||
// Load form to get new token
|
||||
fetchFormDataAjax(href, function (form) {
|
||||
var $form = $(form);
|
||||
var $formContainer = $(form);
|
||||
var $form = $formContainer.is('form') ? $formContainer : $formContainer.find('form');
|
||||
$clicked.popover('destroy');
|
||||
xhr({
|
||||
data: $form.serialize(),
|
||||
success: function (data) {
|
||||
|
@ -4285,27 +4287,9 @@ function feedFormUpdate() {
|
|||
checkSharingGroup('Feed');
|
||||
}
|
||||
|
||||
function setContextFields() {
|
||||
if (typeof showContext === "undefined") {
|
||||
showContext = false;
|
||||
}
|
||||
|
||||
var $button = $('#show_attribute_context');
|
||||
if (showContext) {
|
||||
$('.context').show();
|
||||
$button.removeClass("btn-inverse").addClass("btn-primary");
|
||||
} else {
|
||||
$('.context').hide();
|
||||
$button.removeClass("btn-primary").addClass("btn-inverse");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleContextFields() {
|
||||
if (typeof showContext === "undefined") {
|
||||
showContext = false;
|
||||
}
|
||||
showContext = !showContext;
|
||||
setContextFields();
|
||||
$('.context').toggle()
|
||||
$('#show_attribute_context').toggleClass("btn-inverse").toggleClass("btn-primary")
|
||||
}
|
||||
|
||||
function checkOrphanedAttributes() {
|
||||
|
@ -5586,9 +5570,13 @@ function loadClusterRelations(clusterId) {
|
|||
}
|
||||
}
|
||||
|
||||
function submitGenericFormInPlace(callback) {
|
||||
function submitGenericFormInPlace(callback, forceApi=false) {
|
||||
var $genericForm = $('.genericForm');
|
||||
$.ajax({
|
||||
ajaxOptions = {}
|
||||
if (forceApi) {
|
||||
ajaxOptions['headers'] = { Accept: "application/json" }
|
||||
}
|
||||
$.ajax(Object.assign({}, {
|
||||
type: "POST",
|
||||
url: $genericForm.attr('action'),
|
||||
data: $genericForm.serialize(), // serializes the form's elements.
|
||||
|
@ -5606,7 +5594,7 @@ function submitGenericFormInPlace(callback) {
|
|||
$('#genericModal').modal();
|
||||
},
|
||||
error: xhrFailCallback,
|
||||
});
|
||||
}, ajaxOptions));
|
||||
}
|
||||
|
||||
function openIdSelection(clicked, scope, action) {
|
||||
|
|
6457
db_schema.json
6457
db_schema.json
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,7 @@ misp-lib-stix2>=3.0.1.1
|
|||
mixbox>=1.0.5
|
||||
plyara>=2.1.1
|
||||
pydeep2>=0.5.1
|
||||
pymisp==2.4.185
|
||||
pymisp==2.4.186
|
||||
python-magic>=0.4.27
|
||||
pyzmq>=25.1.1
|
||||
redis>=5.0.1
|
||||
|
|
|
@ -87,7 +87,7 @@ assert event_preview["Event"]["uuid"] == event.uuid
|
|||
url = f'servers/pull/{remote_server["id"]}/disable_background_processing:1'
|
||||
pull_response = pymisp._check_response(pymisp._prepare_request('GET', url))
|
||||
check_response(pull_response)
|
||||
assert "Pull completed. 0 events pulled, 0 events could not be pulled, 0 proposals pulled, 0 sightings pulled, 0 clusters pulled." == pull_response["message"], pull_response["message"]
|
||||
assert "Pull completed. 0 events pulled, 0 events could not be pulled, 0 proposals pulled, 0 sightings pulled, 0 clusters pulled, 0 analyst data pulled." == pull_response["message"], pull_response["message"]
|
||||
|
||||
# Test pull background
|
||||
check_response(pymisp.server_pull(remote_server))
|
||||
|
|
Loading…
Reference in New Issue