chg: [object:fromAttributes] Greatly improved UI - WiP

pull/4672/head
mokaddem 2019-05-17 16:02:06 +02:00
parent a90ac883aa
commit 5798a73462
5 changed files with 225 additions and 24 deletions

View File

@ -1006,8 +1006,8 @@ class ObjectsController extends AppController
return 1;
} else if (!is_array($a['ObjectTemplate']['compatibility']) && is_array($b['ObjectTemplate']['compatibility'])) {
return -1;
} else {
return 0;
} else { // sort based on invalidTypes count
return count($a['ObjectTemplate']['invalidTypes']) > count($b['ObjectTemplate']['invalidTypes']) ? 1 : -1;
}
});
$this->set('potential_templates', $potential_templates);
@ -1018,7 +1018,7 @@ class ObjectsController extends AppController
{
if ($this->request->is('post')) {
$event_id = $object['Event']['id'];
$event = $this->Object->Event->find('first', array(
$event = $this->MispObject->Event->find('first', array(
'recursive' => -1,
'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id'),
'conditions' => array('Event.id' => $event_id)
@ -1026,22 +1026,36 @@ class ObjectsController extends AppController
if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) {
throw new NotFoundException(__('Invalid event.'));
}
$template = $this->Object->ObjectTemplate->find('first', array(
$template = $this->MispObject->ObjectTemplate->find('first', array(
'recursive' => -1,
'conditions' => array('ObjectTemplate.id' => $selected_template, 'ObjectTemplate.active' => true)
));
if (empty($template)) {
throw new NotFoundException(__('Invalid event.'));
throw new NotFoundException(__('Invalid template.'));
}
} else {
// TODO: validate
$selected_attribute_ids = json_decode($selected_attribute_ids, true);
$template = $this->MispObject->ObjectTemplate->find('first', array(
'recursive' => -1,
'conditions' => array('ObjectTemplate.id' => $selected_template, 'ObjectTemplate.active' => true),
'contain' => array('ObjectTemplateElement' => array('fields' => array('ObjectTemplateElement.object_relation', 'ObjectTemplateElement.type')))
));
if (empty($template)) {
throw new NotFoundException(__('Invalid template.'));
}
$object_relations = array();
foreach ($template['ObjectTemplateElement'] as $template_element) {
$object_relations[$template_element['type']][] = $template_element['object_relation'];
}
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
$this->set('distributionData', $distributionData);
$this->set('distributionLevels', $this->MispObject->Attribute->distributionLevels);
$this->set('selectedTemplateTd', $selected_template);
$this->set('selectedAttributeIds', $selected_attribute_ids);
$selected_attributes = $this->MispObject->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $selected_attribute_ids)));
$this->set('template', $template);
$this->set('object_relations', $object_relations);
$this->set('attributes', $selected_attributes);
}

View File

@ -732,26 +732,78 @@ class MispObject extends AppModel
array(
'conditions' => array(
'id' => $selected,
'event_id' => $event_id
'event_id' => $event_id,
'object_id' => 0
),
)
);
// $attribute_types = array_keys(Hash::combine($attributes, '{n}.Attribute.type'));
if (empty($attributes)) {
return array();
}
$attribute_types = array();
foreach ($attributes as $i => $attribute) {
$attribute_types[$attribute['Attribute']['type']] = 1;
$attributes[$i]['Attribute']['object_relation'] = $attribute['Attribute']['type'];
}
$attribute_types = array_keys($attribute_types);
array_walk($attribute_types, function(&$value) { $value = '"' . $value . '"'; });
$db = $this->getDataSource();
$potential_templates = $db->fetchAll(
'SELECT object_templates.id, object_templates.name, count(object_template_elements.type) as type_count FROM object_templates '
.'RIGHT JOIN object_template_elements ON object_templates.id = object_template_elements.object_template_id '
.'WHERE object_templates.active=1 AND object_template_elements.type IN (' . implode(',', $attribute_types) . ') '
.'GROUP BY object_templates.name ORDER BY type_count DESC;'
);
$potential_template_ids = Hash::extract($potential_templates, '{n}.object_templates.id');
// # TEST 1
// array_walk($attribute_types, function(&$value) { $value = '"' . $value . '"'; });
// $potential_templates = $db->fetchAll(
// 'SELECT object_templates.id, object_templates.name, count(object_template_elements.type) as type_count FROM object_templates '
// .'RIGHT JOIN object_template_elements ON object_templates.id = object_template_elements.object_template_id '
// .'WHERE object_templates.active=1 AND object_template_elements.type IN (' . implode(',', $attribute_types) . ') '
// .'GROUP BY object_templates.name ORDER BY type_count DESC;'
// );
// $potential_template_ids = Hash::extract($potential_templates, '{n}.object_templates.id');
// TEST 2
// $potential_templates = $this->ObjectTemplate->find('all', array(
// 'recursive' => -1,
// 'fields' => array(
// 'ObjectTemplate.id',
// 'ObjectTemplate.name',
// 'COUNT(ObjectTemplateElement.type) as type_count'
// ),
// 'conditions' => array(
// 'ObjectTemplate.active' => true,
// ),
// 'contain' => array(
// 'ObjectTemplateElement' => array(
// 'fields' => array('ObjectTemplateElement.object_relation', 'ObjectTemplateElement.type'),
// 'conditions' => array('ObjectTemplateElement.type' => $attribute_types)
// )
// ),
// 'group' => 'ObjectTemplate.name',
// 'order' => 'type_count DESC'
// ));
$potential_templates = $this->ObjectTemplate->find('all', array(
'recursive' => -1,
'fields' => array(
'ObjectTemplate.id',
'ObjectTemplate.name',
'COUNT(ObjectTemplateElement.type) as type_count'
),
'conditions' => array(
'ObjectTemplate.active' => true,
'ObjectTemplateElement.type' => $attribute_types
),
'joins' => array(
array(
'table' => $db->fullTableName($this->ObjectTemplate->ObjectTemplateElement),
'alias' => 'ObjectTemplateElement',
'type' => 'RIGHT',
'fields' => array('ObjectTemplateElement.object_relation', 'ObjectTemplateElement.type'),
'conditions' => array('ObjectTemplate.id = ObjectTemplateElement.object_template_id')
)
),
'group' => 'ObjectTemplate.name',
'order' => 'type_count DESC'
));
$potential_template_ids = Hash::extract($potential_templates, '{n}.ObjectTemplate.id');
$templates = $this->ObjectTemplate->find('all', array(
'recursive' => -1,
'conditions' => array('id' => $potential_template_ids),
@ -759,7 +811,10 @@ class MispObject extends AppModel
));
foreach ($templates as $i => $template) {
$templates[$i]['ObjectTemplate']['compatibility'] = $this->ObjectTemplate->checkTemplateConformityBasedOnTypes($template, $attributes);
$res = $this->ObjectTemplate->checkTemplateConformityBasedOnTypes($template, $attributes);
$templates[$i]['ObjectTemplate']['compatibility'] = $res['valid'] ? true : $res['missingTypes'];
$templates[$i]['ObjectTemplate']['invalidTypes'] = $res['invalidTypes'];
$templates[$i]['ObjectTemplate']['invalidTypesMultiple'] = $res['invalidTypesMultiple'];
}
return $templates;
}

View File

@ -206,6 +206,7 @@ class ObjectTemplate extends AppModel
public function checkTemplateConformityBasedOnTypes($template, $attributes)
{
$to_return = array('valid' => true, 'missingTypes' => array());
if (!empty($template['ObjectTemplate']['requirements'])) {
// check for all required attributes
if (!empty($template['ObjectTemplate']['requirements']['required'])) {
@ -218,7 +219,7 @@ class ObjectTemplate extends AppModel
}
}
if (!$found) {
return array($requiredType);
$to_return = array('valid' => false, 'missingTypes' => array($requiredType));
}
}
}
@ -236,12 +237,38 @@ class ObjectTemplate extends AppModel
}
}
if (!$found) {
return $all_required_type;
$to_return = array('valid' => false, 'missingTypes' => $all_required_type);
}
}
}
// multiple flag ignored. Will be taken care of in client view. For now?
return true;
// at this point, an object can be created; checking if all attribute are valids
$valid_types = array();
$to_return['invalidTypes'] = array();
$to_return['invalidTypesMultiple'] = array();
foreach ($template['ObjectTemplateElement'] as $templateElement) {
$valid_types[$templateElement['type']] = $templateElement['multiple'];
}
$check_for_multiple_type = array();
foreach ($attributes as $attribute) {
if (isset($valid_types[$attribute['Attribute']['type']])) {
if (!$valid_types[$attribute['Attribute']['type']]) { // is not multiple
if (isset($check_for_multiple_type[$attribute['Attribute']['type']])) {
$to_return['invalidTypesMultiple'][] = $attribute['Attribute']['type'];
} else {
$check_for_multiple_type[$attribute['Attribute']['type']] = 1;
}
}
} else {
$to_return['invalidTypes'][] = $attribute['Attribute']['type'];
}
}
$to_return['invalidTypes'] = array_unique($to_return['invalidTypes']);
$to_return['invalidTypesMultiple'] = array_unique($to_return['invalidTypesMultiple']);
if (!empty($to_return['invalidTypesMultiple'])) {
$to_return['valid'] = false;
}
return $to_return;
}
// simple test to see if there are any object templates - if not trigger update

View File

@ -0,0 +1,90 @@
<button class="btn btn-inverse" onclick="showObjectProposition()"><i class="fas fa-chevron-left"></i></button>
<?php
echo $this->Form->create('Object');
?>
<dl style="margin-top: 10px;">
<dt><?php echo __('Object Template');?></dt>
<dd><a href="<?php echo $baseurl . '/ObjectTemplates/view/' . h($template['ObjectTemplate']['id']) ?>"><?php echo Inflector::humanize(h($template['ObjectTemplate']['name'])) . ' v' . h($template['ObjectTemplate']['version']); ?></a></dd>
<dt><?php echo __('Description');?></dt>
<dd><?php echo h($template['ObjectTemplate']['description']); ?></dd>
<dt><?php echo __('Meta category');?></dt>
<dd><?php echo h($template['ObjectTemplate']['meta-category']); ?></dd>
<dt><?php echo __('Distribution');?></dt>
<dd>
<?php echo $this->Form->input('Object.distribution', array(
'class' => 'Object_distribution_select',
'options' => $distributionData['levels'],
'default' => $distributionData['initial'],
'label' => false,
'style' => 'margin-bottom:5px;',
'div' => false
)); ?>
<?php echo $this->Form->input('Object.sharing_group_id', array(
'class' => 'Object_sharing_group_id_select',
'options' => $distributionData['sgs'],
'label' => false,
'div' => false,
'style' => 'display:none;margin-bottom:5px;'
)); ?>
<dt><?php echo __('Comment');?></dt>
<dd>
<?php echo $this->Form->input('Object.comment', array(
'type' => 'textarea',
'style' => 'height:20px;width:400px;',
'required' => false,
'allowEmpty' => true,
'label' => false,
'div' => false
)); ?>
<div class="hidden">
<?php
echo $this->Form->input('selectedTemplateId', array('hiddenField' => false, 'value' => $selectedTemplateTd));
echo $this->Form->input('selectedAttributeIds', array('hiddenField' => false, 'value' => $selectedAttributeIds));
echo $this->Form->end();
?>
</div>
</dl>
<div style="border: 2px solid #3465a4 ; border-radius: 3px; overflow: hidden;">
<table class="table table-striped table-condensed" style="margin-bottom: 0px;">
<thead>
<tr>
<th><?php echo __('ID'); ?></th>
<th><?php echo __('Type'); ?></th>
<th><?php echo __('Date'); ?></th>
<th><?php echo __('Category'); ?></th>
<th><?php echo __('Value'); ?></th>
<th><?php echo __('Distribution'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($attributes as $attribute): ?>
<tr>
<td><?php echo h($attribute['Attribute']['id']); ?></td>
<td>
<select>
<?php foreach ($object_relations[$attribute['Attribute']['type']] as $object_relation): ?>
<option value="<?php echo h($object_relation); ?>"><?php echo h($object_relation); ?></option>
<?php endforeach; ?>
</select>
:: <?php echo h($attribute['Attribute']['type']); ?>
</td>
<td><?php echo h(date('Y-m-d', $attribute['Attribute']['timestamp'])); ?></td>
<td><?php echo h($attribute['Attribute']['category']); ?></td>
<td><?php echo h($attribute['Attribute']['value']); ?></td>
<td><?php echo h($distributionLevels[$attribute['Attribute']['distribution']]); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div style="margin-top: 15px; text-align: center;">
<button class="btn btn-primary">Merge Selected Attribute into an Object</button>
</div>
<script>
$(".Object_distribution_select").change(function() {
checkAndEnable($(this).parent().find('.Object_sharing_group_id_select'), $(this).val() == 4);
});
</script>

View File

@ -1,4 +1,7 @@
<div style="max-width: 1000px; max-height: 800px; overflow-y: auto;">
<?php if (empty($potential_templates)): ?>
<?php echo __('No matching Object.'); ?>
<?php else: ?>
<table id="tableGroupAttributeIntoObject" class="table table-condensed table-hover">
<thead>
<tr>
@ -11,7 +14,7 @@
</thead>
<tbody>
<?php foreach ($potential_templates as $i => $potential_template): ?>
<tr class="useCursorPointer" data-objecttemplateid="<?php echo h($potential_template['ObjectTemplate']['id']); ?>">
<tr class="useCursorPointer" style="<?php echo $potential_template['ObjectTemplate']['compatibility'] === true ? '' : 'cursor: not-allowed;' ?>" data-objecttemplateid="<?php echo h($potential_template['ObjectTemplate']['id']); ?>" data-enabled="<?php echo $potential_template['ObjectTemplate']['compatibility'] === true ? 'true' : 'false'; ?>">
<td class="ignoreSelection">
<a href="<?php echo $baseurl . '/ObjectTemplates/view/' . h($potential_template['ObjectTemplate']['id']) ?>"><?php echo h($potential_template['ObjectTemplate']['id']); ?></a>
</td>
@ -25,30 +28,41 @@
<?php echo $v; ?>
</td>
<?php if ($potential_template['ObjectTemplate']['compatibility'] === true): ?>
<td><i class="fa fa-check"></i></td>
<td>
<i class="fa fa-check"></i>
<?php if (!empty($potential_template['ObjectTemplate']['invalidTypes'])): ?>
<?php foreach ($potential_template['ObjectTemplate']['invalidTypes'] as $type): ?>
<span class="label label-warning" title="<?php echo __('This Attribute type cannot be part of this Object template. If you merge the selected Attributes into this object, all Attribute having this type will be ignored.'); ?>"><?php echo h($type); ?></span>
<?php endforeach; ?>
<?php endif; ?>
</td>
<?php else: ?>
<td style="max-width: 500px;">
<?php foreach ($potential_template['ObjectTemplate']['compatibility'] as $type): ?>
<span class="label label-important" title="<?php echo __('This Attribute type is missing from the selection. Add it to the selection to be able to merge the selected Attributes into this Object.'); ?>"><?php echo h($type); ?></span>
<?php endforeach; ?>
<?php foreach ($potential_template['ObjectTemplate']['invalidTypesMultiple'] as $type): ?>
<span class="label" title="<?php echo __('This Attribute type is not allowed to be present multiple time in this Object. Consider only picking one.'); ?>"><?php echo h($type); ?></span>
<?php endforeach; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div id="resultPreview" style="height: 100%;"></div>
<div id="resultPreview" class="hidden" style="height: 100%;"></div>
<?php endif; ?>
</div>
<script>
$('#tableGroupAttributeIntoObject > tbody > tr > td:not(.ignoreSelection)').click(function() {
$('#tableGroupAttributeIntoObject > tbody > tr[data-enabled="true"] > td:not(.ignoreSelection)').click(function() {
var object_template_id = $(this).parent().data('objecttemplateid');
if (object_template_id !== undefined) {
var $parentDIV = $('#tableGroupAttributeIntoObject').parent();
var bb = $parentDIV[0].getBoundingClientRect();
$parentDIV.css({height: bb.height, width: bb.width});
$('#tableGroupAttributeIntoObject').toggle('slide');
$('#resultPreview').html('<div style="align-items: center; justify-content: center; display: flex; height: 100%; width: 100%"><i class="fas fa-spinner fa-spin" style="font-size: xx-large;"></i></div>');
$('#resultPreview').show().html('<div style="align-items: center; justify-content: center; display: flex; height: 100%; width: 100%"><i class="fas fa-spinner fa-spin" style="font-size: xx-large;"></i></div>');
$.get('<?php echo $baseurl . '/objects/mergeObjectsFromAttributes/' . h($event_id) . '/' ?>' + object_template_id + '/' + getSelected(), function(data) {
$('#resultPreview').html(data);
});
@ -60,5 +74,6 @@
$('#tableGroupAttributeIntoObject').toggle('slide', {
direction: 'left',
});
$('#resultPreview').hide();
}
</script>