mirror of https://github.com/MISP/MISP
Merge branch 'feature/analyst-data' into notes
commit
0c53d96d5d
|
@ -23,7 +23,7 @@ class AnalystDataController extends AppController
|
|||
'Relationship'
|
||||
];
|
||||
|
||||
public $modelSelection = 'Note';
|
||||
// public $modelSelection = 'Note';
|
||||
|
||||
private function _setViewElements()
|
||||
{
|
||||
|
@ -65,7 +65,8 @@ class AnalystDataController extends AppController
|
|||
{
|
||||
$this->__typeSelector($type);
|
||||
$this->set('id', $id);
|
||||
$params = [];
|
||||
$params = [
|
||||
];
|
||||
$this->CRUD->edit($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
|
@ -115,10 +116,18 @@ class AnalystDataController extends AppController
|
|||
$this->_setViewElements();
|
||||
}
|
||||
|
||||
public function getRelatedElement($type, $uuid)
|
||||
{
|
||||
$this->__typeSelector('Relationship');
|
||||
$data = $this->AnalystData->getRelatedElement($this->Auth->user(), $type, $uuid);
|
||||
return $this->RestResponse->viewData($data, 'json');
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1233,6 +1233,8 @@ class EventsController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
$this->Event->Attribute->includeAnalystData = true;
|
||||
|
||||
if (isset($filters['focus'])) {
|
||||
$this->set('focus', $filters['focus']);
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -23,8 +23,45 @@ class AnalystData extends AppModel
|
|||
'SharingGroup'
|
||||
];
|
||||
|
||||
const NOTE = 0,
|
||||
OPINION = 1,
|
||||
RELATIONSHIP = 2;
|
||||
|
||||
public $current_user = null;
|
||||
|
||||
public function __construct($id = false, $table = null, $ds = null)
|
||||
{
|
||||
parent::__construct($id, $table, $ds);
|
||||
$this->bindModel([
|
||||
'belongsTo' => [
|
||||
'Organisation' => [
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => false,
|
||||
'conditions' => [
|
||||
sprintf('%s.orgc_uuid = Organisation.uuid', $this->alias)
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
parent::afterFind($results, $primary);
|
||||
foreach ($results as $i => $v) {
|
||||
$results[$i][$this->alias]['note_type'] = $this->current_type_id;
|
||||
$results[$i][$this->alias]['note_type_name'] = $this->current_type;
|
||||
if (!isset($v['Organisation'])) {
|
||||
$this->Organisation = ClassRegistry::init('Organisation');
|
||||
$results[$i][$this->alias]['Organisation'] = $this->Organisation->find('first', ['condition' => ['uuid' => $v[$this->alias]['orgc_uuid']]])['Organisation'];
|
||||
} else {
|
||||
$results[$i][$this->alias]['Organisation'] = $v['Organisation'];
|
||||
}
|
||||
unset($results[$i]['Organisation']);
|
||||
$results[$i][$this->alias] = $this->fetchChildNotesAndOpinions($results[$i][$this->alias]);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
|
@ -39,7 +76,6 @@ class AnalystData extends AppModel
|
|||
$this->data[$this->current_type]['org_uuid'] = $this->current_user['Organisation']['uuid'];
|
||||
$this->data[$this->current_type]['authors'] = $this->current_user['email'];
|
||||
}
|
||||
debug($this->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -57,4 +93,44 @@ class AnalystData extends AppModel
|
|||
}
|
||||
throw new NotFoundException(__('Invalid UUID'));
|
||||
}
|
||||
|
||||
public function fetchChildNotesAndOpinions(array $analystData): array
|
||||
{
|
||||
$this->Note = ClassRegistry::init('Note');
|
||||
$this->Opinion = ClassRegistry::init('Opinion');
|
||||
$paramsNote = [
|
||||
'recursive' => -1,
|
||||
'contain' => ['Organisation'],
|
||||
'conditions' => [
|
||||
'object_type' => $this->current_type,
|
||||
'object_uuid' => $analystData['uuid'],
|
||||
]
|
||||
];
|
||||
$paramsOpinion = [
|
||||
'recursive' => -1,
|
||||
'contain' => ['Organisation'],
|
||||
'conditions' => [
|
||||
'object_type' => $this->current_type,
|
||||
'object_uuid' => $analystData['uuid'],
|
||||
]
|
||||
];
|
||||
|
||||
// recursively fetch and include nested notes and opinions
|
||||
$childNotes = array_map(function ($item) {
|
||||
$expandedNotes = $this->fetchChildNotesAndOpinions($item[$this->Note->current_type]);
|
||||
return $expandedNotes;
|
||||
}, $this->Note->find('all', $paramsNote));
|
||||
$childOpinions = array_map(function ($item) {
|
||||
$expandedNotes = $this->fetchChildNotesAndOpinions($item[$this->Opinion->current_type]);
|
||||
return $expandedNotes;
|
||||
}, $this->Opinion->find('all', $paramsOpinion));
|
||||
|
||||
if (!empty($childNotes)) {
|
||||
$analystData[$this->Note->current_type] = $childNotes;
|
||||
}
|
||||
if (!empty($childOpinions)) {
|
||||
$analystData[$this->Opinion->current_type] = $childOpinions;
|
||||
}
|
||||
return $analystData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2070,6 +2070,7 @@ class AppModel extends Model
|
|||
`modified` datetime ON UPDATE CURRENT_TIMESTAMP,
|
||||
`distribution` tinyint(4) NOT NULL,
|
||||
`sharing_group_id` int(10) unsigned,
|
||||
`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`),
|
||||
|
@ -2080,6 +2081,7 @@ class AppModel extends Model
|
|||
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;";
|
||||
|
|
|
@ -37,7 +37,7 @@ class Attribute extends AppModel
|
|||
'Containable',
|
||||
'Regexp' => array('fields' => array('value')),
|
||||
'LightPaginator',
|
||||
'AnalystDataParent'
|
||||
'AnalystDataParent',
|
||||
);
|
||||
|
||||
public $displayField = 'value';
|
||||
|
|
|
@ -38,7 +38,8 @@ class AnalystDataBehavior extends ModelBehavior
|
|||
}
|
||||
return $Model->find('all', [
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['Organisation'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class AnalystDataParentBehavior extends ModelBehavior
|
|||
|
||||
|
||||
|
||||
public function attachAnalystData(Model $model, array $object, array $types = ['Note', 'Opinion', 'Relationship'])
|
||||
public function attachAnalystData(Model $Model, array $object, array $types = ['Note', 'Opinion', 'Relationship'])
|
||||
{
|
||||
// No uuid, nothing to attach
|
||||
if (empty($object['uuid'])) {
|
||||
|
@ -42,7 +42,7 @@ class AnalystDataParentBehavior extends ModelBehavior
|
|||
if (!empty($model->includeAnalystData)) {
|
||||
foreach ($results as $k => $item) {
|
||||
if (isset($item[$model->alias])) {
|
||||
$results[$k] = array_merge($results[$k], $this->attachAnalystData($item[$model->alias]));
|
||||
$results[$k] = array_merge($results[$k], $this->attachAnalystData($model, $item[$model->alias]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2232,6 +2232,10 @@ class Event extends AppModel
|
|||
unset($attribute['EventTag']);
|
||||
}
|
||||
}
|
||||
$allAnalystData = $this->Attribute->attachAnalystData($attribute); // afterFind from AnalystDataParentBehavior is not called. Probably because we're in an association
|
||||
foreach ($allAnalystData as $type => $analystData) {
|
||||
$attribute[$type] = $analystData;
|
||||
}
|
||||
// If a shadowattribute can be linked to an attribute, link it to it
|
||||
// This is to differentiate between proposals that were made to an attribute for modification and between proposals for new attributes
|
||||
$attribute['ShadowAttribute'] = $shadowAttributeByOldId[$attribute['id']] ?? [];
|
||||
|
|
|
@ -570,11 +570,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;
|
||||
|
|
|
@ -12,6 +12,7 @@ class Note extends AnalystData
|
|||
);
|
||||
|
||||
public $current_type = 'Note';
|
||||
public $current_type_id = 0;
|
||||
|
||||
public $validate = array(
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('AnalystData', 'Model');
|
||||
class Opinion extends AnalystData
|
||||
{
|
||||
|
||||
|
@ -11,6 +12,7 @@ class Opinion extends AnalystData
|
|||
);
|
||||
|
||||
public $current_type = 'Opinion';
|
||||
public $current_type_id = 1;
|
||||
|
||||
public $validate = array(
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('AnalystData', 'Model');
|
||||
class Relationship extends AnalystData
|
||||
{
|
||||
|
||||
|
@ -11,7 +12,114 @@ class Relationship extends AnalystData
|
|||
);
|
||||
|
||||
public $current_type = 'Relationship';
|
||||
public $current_type_id = 2;
|
||||
|
||||
public $validate = array(
|
||||
);
|
||||
|
||||
/** @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) {
|
||||
$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 = [
|
||||
];
|
||||
$data = $this->Event->fetchSimpleEvent($user, $uuid, $params);
|
||||
} else if ($type == 'Attribute') {
|
||||
$this->Attribute = ClassRegistry::init('Attribute');
|
||||
$params = [
|
||||
'conditions' => [
|
||||
['Attribute.uuid' => $uuid],
|
||||
],
|
||||
'contain' => ['Event' => 'Orgc', 'Object',]
|
||||
];
|
||||
$data = $this->Attribute->fetchAttributeSimple($user, $params);
|
||||
$data = $this->rearrangeData($data, 'Attribute');
|
||||
} else if ($type == 'Object') {
|
||||
$this->Object = ClassRegistry::init('MispObject');
|
||||
$params = [
|
||||
'conditions' => [
|
||||
['Object.uuid' => $uuid],
|
||||
],
|
||||
'contain' => ['Event' => 'Orgc',]
|
||||
];
|
||||
$data = $this->Object->fetchObjectSimple($user, $params);
|
||||
if (!empty($data)) {
|
||||
$data = $data[0];
|
||||
}
|
||||
$data = $this->rearrangeData($data, 'Object');
|
||||
} else if ($type == 'Note') {
|
||||
$this->Note = ClassRegistry::init('Note');
|
||||
$params = [
|
||||
|
||||
];
|
||||
$data = $this->Note->fetchNote();
|
||||
} else if ($type == 'Opinion') {
|
||||
$this->Opinion = ClassRegistry::init('Opinion');
|
||||
$params = [
|
||||
|
||||
];
|
||||
$data = $this->Opinion->fetchOpinion();
|
||||
} else if ($type == 'Relationship') {
|
||||
$this->Relationship = ClassRegistry::init('Relationship');
|
||||
$params = [
|
||||
|
||||
];
|
||||
$data = $this->Relationship->fetchRelationship();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function rearrangeData(array $data, $objectType): array
|
||||
{
|
||||
$models = ['Event', 'Attribute', 'Object', 'Organisation', ];
|
||||
if (!empty($data)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,41 @@ if ($modelSelection === 'Note') {
|
|||
]
|
||||
);
|
||||
} 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',
|
||||
],
|
||||
[
|
||||
'field' => 'related_object_type',
|
||||
'class' => 'span2',
|
||||
'options' => $dropdownData['valid_targets'],
|
||||
'type' => 'dropdown',
|
||||
'stayInLine' => 1
|
||||
],
|
||||
[
|
||||
'field' => 'related_object_uuid',
|
||||
'class' => 'span4',
|
||||
],
|
||||
sprintf('<div><label>%s:</label><div id="related-object-container">%s</div></div>', __('Related Object'), __('- No UUID provided -'))
|
||||
]
|
||||
);
|
||||
}
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
|
@ -60,7 +92,7 @@ echo $this->element('genericElements/Form/genericForm', [
|
|||
'fields' => $fields,
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
'ajaxSubmit' => 'submitGenericFormInPlace();'
|
||||
'ajaxSubmit' => 'submitGenericFormInPlace(analystDataSubmitSuccess, true);'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
@ -68,3 +100,85 @@ echo $this->element('genericElements/Form/genericForm', [
|
|||
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) {
|
||||
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() {
|
||||
$('#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())
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#related-object-container {
|
||||
box-shadow: 0 0 5px 0px #22222266;
|
||||
padding: 0.5rem;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -9,6 +9,11 @@
|
|||
'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',
|
||||
|
@ -16,7 +21,7 @@
|
|||
],
|
||||
[
|
||||
'name' => __('Creator org'),
|
||||
'data_path' => $modelSelection . '.orgc_id'
|
||||
'data_path' => $modelSelection . '.orgc_uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Created'),
|
||||
|
@ -51,9 +56,35 @@
|
|||
]
|
||||
);
|
||||
} else if ($modelSelection === 'Opinion') {
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'name' => __('Comment'),
|
||||
'data_path' => $modelSelection . '.comment'
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
} else if ($modelSelection === 'Relationship') {
|
||||
|
||||
$fields = array_merge($fields,
|
||||
[
|
||||
[
|
||||
'name' => __('Related Object Type'),
|
||||
'sort' => $modelSelection . '.related_object_type',
|
||||
'data_path' => $modelSelection . '.related_object_type'
|
||||
],
|
||||
[
|
||||
'name' => __('Related Object UUID'),
|
||||
'sort' => $modelSelection . '.related_object_uuid',
|
||||
'data_path' => $modelSelection . '.related_object_uuid'
|
||||
],
|
||||
[
|
||||
'name' => __('Relationship_type'),
|
||||
'sort' => $modelSelection . '.relationship_type',
|
||||
'data_path' => $modelSelection . '.relationship_type'
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
echo $this->element('genericElements/IndexTable/scaffold', [
|
||||
|
@ -63,6 +94,27 @@
|
|||
'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'),
|
||||
|
@ -91,7 +143,7 @@
|
|||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/analystData/edit/' . $modelSelection . '/[onclick_params_data_path]\');',
|
||||
'openGenericModal(\'%s/analystData/delete/' . $modelSelection . '/[onclick_params_data_path]\');',
|
||||
$baseurl
|
||||
),
|
||||
'onclick_params_data_path' => $modelSelection . '.id',
|
||||
|
|
|
@ -70,14 +70,34 @@
|
|||
<td class="short context hidden"><?= $objectId ?></td>
|
||||
<td class="short context hidden uuid">
|
||||
<span class="quickSelect"><?php echo h($object['uuid']); ?></span>
|
||||
<?php echo $this->element('genericElements/Analyst_notes/notes', ['uuid' => $object['uuid'], 'notes' => !empty($object['notes']) ? $object['notes'] : [], 'object_type' => 'attribute']); ?>
|
||||
<?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 echo $this->element('/genericElements/shortUuidWithNotes', ['uuid' => $object['uuid'], 'notes' => !empty($object['notes']) ? $object['notes'] : [], 'object_type' => 'attribute']); ?>
|
||||
<?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)):
|
||||
|
|
|
@ -32,14 +32,34 @@ $objectId = intval($object['id']);
|
|||
<td class="short context hidden"><?= $objectId ?></td>
|
||||
<td class="short context hidden uuid">
|
||||
<span class="quickSelect"><?php echo h($object['uuid']); ?></span>
|
||||
<?php echo $this->element('genericElements/Analyst_notes/notes', ['uuid' => $object['uuid'], 'notes' => !empty($object['notes']) ? $object['notes'] : [], 'object_type' => 'attribute']); ?>
|
||||
<?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 echo $this->element('/genericElements/shortUuidWithNotes', ['uuid' => $object['uuid'], 'notes' => !empty($object['notes']) ? $object['notes'] : [], 'object_type' => 'object']); ?>
|
||||
<?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):
|
||||
|
|
|
@ -47,7 +47,16 @@
|
|||
</td>
|
||||
<td class="short context hidden uuid">
|
||||
<span class="quickSelect"><?php echo h($object['uuid']); ?></span>
|
||||
<?php echo $this->element('genericElements/Analyst_notes/notes', ['uuid' => $object['uuid'], 'notes' => !empty($object['notes']) ? $object['notes'] : [], 'object_type' => 'attribute']); ?>
|
||||
<?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)); ?>
|
||||
|
@ -59,7 +68,18 @@
|
|||
?>
|
||||
</td>
|
||||
<td class="short context">
|
||||
<?php echo $this->element('/genericElements/shortUuidWithNotes', ['uuid' => $object['uuid'], 'notes' => !empty($object['notes']) ? $object['notes'] : [], 'object_type' => 'proposals']); ?>
|
||||
<?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):
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<?php
|
||||
$seed = mt_rand();
|
||||
|
||||
$URL_ADD = '/analyst-notes/add/';
|
||||
$URL_EDIT = '/analyst-notes/edit/';
|
||||
$URL_DELETE = '/analyst-notes/delete/';
|
||||
$URL_ADD = '/analystData/add/';
|
||||
$URL_EDIT = '/analystData/edit/';
|
||||
$URL_DELETE = '/analystData/delete/';
|
||||
|
||||
$object_uuid = 'bf74e1a4-99c2-4fcb-8a5d-a71118effd1a'; // e.g. $event['Event']['uuid']
|
||||
$object_type = 'event';
|
||||
$notes = [
|
||||
$notes2 = [
|
||||
[
|
||||
'analyst_note' => 'This is a note',
|
||||
'note_type' => 0,
|
||||
|
@ -185,7 +183,7 @@ $notes = [
|
|||
],
|
||||
];
|
||||
|
||||
$related_objects = [
|
||||
$related_objects2 = [
|
||||
'Event' => [
|
||||
'f80a0db2-24bd-4148-929e-7c803ade7ca1' => [
|
||||
'uuid' => 'f80a0db2-24bd-4148-929e-7c803ade7ca1',
|
||||
|
@ -238,48 +236,327 @@ $related_objects = [
|
|||
],
|
||||
];
|
||||
|
||||
$notes = $analyst_data['notes'] ?? [];
|
||||
$opinions = $analyst_data['opinions'] ?? [];
|
||||
$relationships = $analyst_data['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);
|
||||
|
||||
if(!function_exists("countNotes")) {
|
||||
function countNotes($notes) {
|
||||
$notesTotalCount = count($notes);
|
||||
function countNotes($notesOpinions) {
|
||||
$notesTotalCount = count($notesOpinions);
|
||||
$notesCount = 0;
|
||||
$relationsCount = 0;
|
||||
foreach ($notes as $note) {
|
||||
if ($note['note_type'] == 2) { // relationship
|
||||
foreach ($notesOpinions as $notesOpinion) {
|
||||
if ($notesOpinion['note_type'] == 2) { // relationship
|
||||
$relationsCount += 1;
|
||||
} else {
|
||||
$notesCount += 1;
|
||||
}
|
||||
if (!empty($note['notes'])) {
|
||||
$nestedCounts = countNotes($note['notes']);
|
||||
if (!empty($notesOpinion['Note'])) {
|
||||
$nestedCounts = countNotes($notesOpinion['Note']);
|
||||
$notesTotalCount += $nestedCounts['total'];
|
||||
$notesCount += $nestedCounts['notes'];
|
||||
$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, 'notes' => $notesCount, 'relations' => $relationsCount];
|
||||
return ['total' => $notesTotalCount, 'notesOpinions' => $notesCount, 'relations' => $relationsCount];
|
||||
}
|
||||
}
|
||||
$counts = countNotes($notes);
|
||||
$notesCount = $counts['notes'];
|
||||
$relationshipsCount = $counts['relations'];
|
||||
$counts = countNotes($notesOpinions);
|
||||
$notesOpinionCount = $counts['notesOpinions'];
|
||||
$relationshipsCount = count($relationships);
|
||||
?>
|
||||
|
||||
<?php if (empty($notes)): ?>
|
||||
<i class="<?= $this->FontAwesome->getClass('sticky-note') ?> useCursorPointer" onclick="openNotes(this)" title="<?= __('Notes and opinions for this UUID') ?>"></i>
|
||||
<?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" onclick="openNotes(this)" >
|
||||
<span class="label label-info useCursorPointer node-opener-<?= $seed ?>">
|
||||
<i class="<?= $this->FontAwesome->getClass('sticky-note') ?> useCursorPointer" title="<?= __('Notes and opinions for this UUID') ?>"></i>
|
||||
<?= $notesCount; ?>
|
||||
<?= $notesOpinionCount; ?>
|
||||
<i class="<?= $this->FontAwesome->getClass('project-diagram') ?> useCursorPointer" title="<?= __('Relationships for this UUID') ?>"></i>
|
||||
<?= $relationshipsCount; ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>
|
||||
var notes = <?= json_encode($notes) ?>;
|
||||
|
||||
|
||||
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 ? '#0000ff' : '#000')
|
||||
note.authors = Array.isArray(note.authors) ? note.authors.join(', ') : note.authors;
|
||||
note._permissions = {
|
||||
can_edit: true,
|
||||
can_delete: true,
|
||||
can_add: true,
|
||||
}
|
||||
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 baseurl + '/events/view/' + note.attribute.event_id + '/focus:' + note.related_object_uuid
|
||||
} else if (note.related_object_type == 'Object') {
|
||||
return 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.Organisation.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.Organisation.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> \
|
||||
</i><span style="margin-left: 0.5rem; flex-grow: 1; text-align: right; color: {{=it.distribution_color}}">{{=it.distribution_text}}</span> \
|
||||
<span class="action-button-container" style="margin-left: auto; display: flex; gap: 0.2rem;"> \
|
||||
{{? it._permissions.can_add }} \
|
||||
<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> \
|
||||
{{?}} \
|
||||
{{? it._permissions.can_add }} \
|
||||
<span role="button" onclick="addNote(this, \'{{=it.uuid}}\', \'{{=it.note_type_name}}\')" title="<?= __('Add a note to this note') ?>"><i class="<?= $this->FontAwesome->getClass('comment-alt') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? it._permissions.can_edit }} \
|
||||
<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._permissions.can_delete }} \
|
||||
<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;">{{=it.relationship_type}}</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> \
|
||||
')
|
||||
|
||||
function toggleNotes(clicked) {
|
||||
var $container = $('.note-container-<?= $seed ?>')
|
||||
$container.toggle()
|
||||
}
|
||||
|
||||
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')
|
||||
$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 adjustPopoverPosition() {
|
||||
var $popover = $('.popover:last');
|
||||
$popover.css('top', Math.max($popover.position().top, 50) + 'px')
|
||||
}
|
||||
var shortDist = <?= json_encode($shortDist) ?>;
|
||||
|
||||
(function() {
|
||||
var notes = <?= json_encode($notesOpinions) ?>;
|
||||
var relationships = <?= json_encode($relationships) ?>;
|
||||
var relationship_related_object = <?= json_encode($related_objects) ?>;
|
||||
var shortDist = <?= json_encode($shortDist) ?>;
|
||||
var renderedNotes = null
|
||||
var renderedNotes<?= $seed ?> = null
|
||||
|
||||
var nodeContainerTemplate = doT.template('\
|
||||
<div> \
|
||||
|
@ -287,9 +564,10 @@ $relationshipsCount = $counts['relations'];
|
|||
<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;"> \
|
||||
<div class="tab-content" style="padding: 0.25rem; max-width: 992px; min-width: 400px;"> \
|
||||
<div id="notes-<?= $seed ?>" class="tab-pane active"> \
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">{{=it.content_notes}}</div>\
|
||||
' + 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>\
|
||||
|
@ -297,129 +575,28 @@ $relationshipsCount = $counts['relations'];
|
|||
</div> \
|
||||
</div> \
|
||||
')
|
||||
var baseNoteTemplate = doT.template('\
|
||||
<div id="note-{{=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" \
|
||||
> \
|
||||
<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.Organisation.id}}.png" width="20" height="20" class="orgImg" onerror="this.remove()" alt="Organisation logo"></object> \
|
||||
<span style="margin-left: 0rem; margin-right: 0.5rem;"> \
|
||||
<span>{{=it.Organisation.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> \
|
||||
</i><span style="margin-left: 0.5rem; flex-grow: 1; text-align: right; color: {{=it.distribution_color}}">{{=it.distribution_text}}</span> \
|
||||
<span class="action-button-container" style="margin-left: auto; display: flex; gap: 0.2rem;"> \
|
||||
{{? it._permissions.can_add }} \
|
||||
<span role="button" onclick="addOpinion(this, \'{{=it.uuid}}\')" title="<?= __('Add an opinion to this note') ?>"><i class="<?= $this->FontAwesome->getClass('gavel') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? it._permissions.can_add }} \
|
||||
<span role="button" onclick="addNote(this, \'{{=it.uuid}}\')" title="<?= __('Add a note to this note') ?>"><i class="<?= $this->FontAwesome->getClass('comment-alt') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? it._permissions.can_edit }} \
|
||||
<span role="button" onclick="editNote(this, {{=it.id}})" title="<?= __('Edit this note') ?>"><i class="<?= $this->FontAwesome->getClass('edit') ?> useCursorPointer"></i></span> \
|
||||
{{?}} \
|
||||
{{? it._permissions.can_delete }} \
|
||||
<span role="button" onclick="deleteNote(this, {{=it.id}})" title="<?= __('Delete this note') ?>" href="<?= $baseurl . $URL_DELETE ?>{{=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.analyst_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;">{{=it.relationship_type}}</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" 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" 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 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 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>'
|
||||
|
||||
function toggleNotes(clicked) {
|
||||
var $container = $('.note-container-<?= $seed ?>')
|
||||
$container.toggle()
|
||||
}
|
||||
|
||||
function openNotes(clicked) {
|
||||
openPopover(clicked, renderedNotes, undefined, undefined, function() {
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
function adjustPopoverPosition() {
|
||||
var $popover = $('.popover');
|
||||
$popover.css('top', Math.max($popover.position().top, 50) + 'px')
|
||||
}
|
||||
|
||||
function renderNotes(notes) {
|
||||
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)
|
||||
var noteHtml = renderNote(note, relationship_related_object)
|
||||
|
||||
if (note.notes) { // The notes has more notes attached
|
||||
noteHtml += replyNoteTemplate({notes_html: renderNotes(note.notes)})
|
||||
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)})
|
||||
}
|
||||
|
||||
renderedNotesArray.push(noteHtml)
|
||||
|
@ -428,135 +605,63 @@ $relationshipsCount = $counts['relations'];
|
|||
return renderedNotesArray.join('')
|
||||
}
|
||||
|
||||
function renderNote(note) {
|
||||
note.modified_relative = note.modified.date ? moment(note.modified.date).fromNow() : note.modified
|
||||
note.created_relative = note.created.date ? moment(note.created.date).fromNow() : note.created
|
||||
note.modified = note.modified.date ? (new Date(note.modified.date)).toLocaleString() : note.modified
|
||||
note.created = note.created.date ? (new Date(note.created.date)).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 ? '#0000ff' : '#000')
|
||||
note.authors = Array.isArray(note.authors) ? note.authors.join(', ') : note.authors;
|
||||
note._permissions = {
|
||||
can_edit: true,
|
||||
can_delete: true,
|
||||
can_add: true,
|
||||
}
|
||||
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)
|
||||
}
|
||||
var noteHtml = baseNoteTemplate(note)
|
||||
return noteHtml
|
||||
}
|
||||
|
||||
function renderAllNotesWithForm() {
|
||||
renderedNotes = nodeContainerTemplate({
|
||||
content_notes: renderNotes(notes.filter(function(note) { return note.note_type != 2})) + addNoteButton,
|
||||
content_relationships: renderNotes(notes.filter(function(note) { return note.note_type == 2})) + addRelationshipButton,
|
||||
function renderAllNotesWithForm(relationship_related_object) {
|
||||
var buttonContainer = '<div 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,
|
||||
})
|
||||
}
|
||||
|
||||
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 baseurl + '/events/view/' + note.attribute.event_id + '/focus:' + note.related_object_uuid
|
||||
} else if (note.related_object_type == 'Object') {
|
||||
return baseurl + '/events/view/' + note.object.event_id + '/focus:' + note.related_object_uuid
|
||||
}
|
||||
return '#'
|
||||
function registerListeners() {
|
||||
$('.node-opener-<?= $seed ?>').click(function() {
|
||||
openNotes(this)
|
||||
})
|
||||
}
|
||||
|
||||
function renderRelationshipEntryFromType(note) {
|
||||
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> \
|
||||
')
|
||||
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('\
|
||||
<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.url}}">{{=it.event.info}}</a></span> \
|
||||
</span></span> \
|
||||
</span> \
|
||||
</span> \
|
||||
')
|
||||
} else if (note.related_object_type == 'Attribute' && relationship_related_object.Attribute[note.related_object_uuid]) {
|
||||
note.attribute = relationship_related_object.Attribute[note.related_object_uuid]
|
||||
if (note.attribute.object_relation !== undefined && note.attribute.object_relation !== null) {
|
||||
template = doT.template('\
|
||||
<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}}">{{=it.attribute.value}}</a></span></span> \
|
||||
</span> \
|
||||
')
|
||||
} else if (relationship_related_object.Attribute[note.related_object_uuid]) {
|
||||
template = doT.template('\
|
||||
<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}}">{{=it.attribute.value}}</a></span></span></span> \
|
||||
</span> \
|
||||
</span> \
|
||||
')
|
||||
}
|
||||
} else if (note.related_object_type == 'Object') {
|
||||
note.object = relationship_related_object.Object[note.related_object_uuid]
|
||||
template = doT.template('\
|
||||
<i class="<?= $this->FontAwesome->getClass('cubes') ?>" title="<?= __('Object') ?>"></i> \
|
||||
<span class="misp-element-wrapper object"> \
|
||||
<span class="bold"> \
|
||||
<span class="obj-type"><span>{{=it.object.type}}</span></span> \
|
||||
<span class="blue"><span class="obj-value"><span class="ellipsis-overflow" style="max-width: 12em;"><a href="{{=it.url}}">{{=it.object.value}}</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 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)
|
||||
registerListeners()
|
||||
})
|
||||
})()
|
||||
|
||||
function createNewNote(clicked, object_type, object_uuid) {
|
||||
note_type = 0;
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + object_type + '/' + object_uuid + '/' + note_type)
|
||||
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 = 2;
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + object_type + '/' + object_uuid + '/' + note_type)
|
||||
note_type = 'Relationship';
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + note_type + '/' + object_uuid + '/' + object_type)
|
||||
}
|
||||
|
||||
function addNote(clicked, note_uuid) {
|
||||
object_type = 'note';
|
||||
note_type = 0;
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + object_type + '/' + note_uuid + '/' + note_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';
|
||||
note_type = 1;
|
||||
openGenericModal(baseurl + '<?= $URL_ADD ?>' + object_type + '/' + note_uuid + '/' + note_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) {
|
||||
openGenericModal(baseurl + '<?= $URL_EDIT ?>' + note_id)
|
||||
function editNote(clicked, note_id, note_type) {
|
||||
openGenericModal(baseurl + '<?= $URL_EDIT ?>' + note_type + '/' + note_id)
|
||||
}
|
||||
|
||||
function deleteNote(clicked, note_id) {
|
||||
|
@ -566,14 +671,20 @@ $relationshipsCount = $counts['relations'];
|
|||
popoverConfirm(clicked, '<?= __('Confirm deletion of this note') ?>', undefined, deletionSuccessCallback)
|
||||
}
|
||||
|
||||
function registerListeners() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
renderAllNotesWithForm()
|
||||
registerListeners()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -644,8 +755,11 @@ if(!function_exists("genStyleForOpinionNotes")) {
|
|||
function genStyleForOpinionNotes($notes) {
|
||||
foreach ($notes as $note) {
|
||||
genStyleForOpinionNote($note);
|
||||
if (!empty($note['notes'])) {
|
||||
genStyleForOpinionNotes($note['notes']);
|
||||
if (!empty($note['Note'])) {
|
||||
genStyleForOpinionNotes($note['Note']);
|
||||
}
|
||||
if (!empty($note['Opinion'])) {
|
||||
genStyleForOpinionNotes($note['Opinion']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -659,20 +773,20 @@ if(!function_exists("genStyleForOpinionNote")) {
|
|||
$opinion = min(100, max(0, intval($note['opinion'])));
|
||||
?>
|
||||
|
||||
#note-<?= $note['id'] ?> .opinion-gradient-<?= $opinion >= 50 ? 'negative' : 'positive' ?> {
|
||||
#Opinion-<?= $note['id'] ?> .opinion-gradient-<?= $opinion >= 50 ? 'negative' : 'positive' ?> {
|
||||
opacity: 0;
|
||||
}
|
||||
#note-<?= $note['id'] ?> .opinion-gradient-dot {
|
||||
#Opinion-<?= $note['id'] ?> .opinion-gradient-dot {
|
||||
left: calc(<?= $opinion ?>% - 6px);
|
||||
background-color: <?= $opinion == 50 ? '#555' : $opinion_color_scale_100[$opinion] ?>;
|
||||
}
|
||||
<?php if ($opinion >= 50): ?>
|
||||
#note-<?= $note['id'] ?> .opinion-gradient-positive {
|
||||
#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: ?>
|
||||
#note-<?= $note['id'] ?> .opinion-gradient-negative {
|
||||
#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%);
|
||||
}
|
||||
|
@ -682,7 +796,7 @@ if(!function_exists("genStyleForOpinionNote")) {
|
|||
}
|
||||
}
|
||||
|
||||
genStyleForOpinionNotes($notes)
|
||||
genStyleForOpinionNotes($notesOpinionsRelationships)
|
||||
?>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
$seed = mt_rand();
|
||||
$params['type'] = 'number';
|
||||
$params['min'] = 0;
|
||||
$params['max'] = 100;
|
||||
$params['class'] .= ' opinion-' . $seed;
|
||||
echo $this->Form->input($fieldData['field'], $params);
|
||||
?>
|
||||
|
||||
<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" 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>
|
||||
.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));
|
||||
}
|
||||
|
||||
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>
|
|
@ -5,7 +5,18 @@
|
|||
h($uuid)
|
||||
);
|
||||
|
||||
$analyst_data = !empty($analyst_data) ? $analyst_data : [];
|
||||
$object_uuid = !empty($object_uuid) ? $object_uuid : null;
|
||||
$object_type = !empty($object_type) ? $object_type : null;
|
||||
echo $this->element('genericElements/Analyst_notes/notes', ['notes' => $analyst_data, 'object_uuid' => $object_uuid, 'object_type' => $object_type]);
|
||||
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']
|
||||
]);
|
||||
} else {
|
||||
debug('Provide object type to access notes for that object');
|
||||
}
|
||||
|
|
|
@ -2,7 +2,15 @@
|
|||
|
||||
$uuidHalfWidth = 3;
|
||||
$shortUUID = sprintf('%s...%s', substr($uuid, 0, $uuidHalfWidth), substr($uuid, 36-$uuidHalfWidth, $uuidHalfWidth));
|
||||
$notes = !empty($notes) ? $notes : [];
|
||||
$object_type = !empty($object_type) ? $object_type : null;
|
||||
echo sprintf('<span title="%s">%s</span>', $uuid, $shortUUID);
|
||||
echo $this->element('genericElements/Analyst_notes/notes', ['notes' => $notes, 'object_uuid' => $uuid, 'object_type' => $object_type]);
|
||||
|
||||
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
|
||||
]);
|
||||
}
|
|
@ -18,6 +18,9 @@
|
|||
'path' => 'Event.uuid',
|
||||
'class' => '',
|
||||
'type' => 'uuid',
|
||||
'object_type' => 'Event',
|
||||
'notes_path' => 'Note',
|
||||
'opinions_path' => 'Opinion',
|
||||
'action_buttons' => [
|
||||
[
|
||||
'url' => $baseurl . '/events/add/extends:' . h($event['Event']['uuid']),
|
||||
|
|
|
@ -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) {
|
||||
|
@ -5568,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.
|
||||
|
@ -5588,7 +5594,7 @@ function submitGenericFormInPlace(callback) {
|
|||
$('#genericModal').modal();
|
||||
},
|
||||
error: xhrFailCallback,
|
||||
});
|
||||
}, ajaxOptions));
|
||||
}
|
||||
|
||||
function openIdSelection(clicked, scope, action) {
|
||||
|
|
Loading…
Reference in New Issue