diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index edf189ec6..b7524b1a9 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -432,6 +432,7 @@ class ACLComponent extends Component ), 'objectReferences' => array( 'add' => array('perm_add'), + 'bulkAdd' => array('perm_add'), 'delete' => array('perm_add'), 'view' => array('*'), ), diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 73402f1fc..8dc800637 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -209,4 +209,118 @@ class ObjectReferencesController extends AppController } return $this->RestResponse->viewData($objectReference, 'json'); } + + public function bulkAdd($eventId, $selectedAttributes = '[]') + { + if (!$this->request->is('ajax')) { + throw new MethodNotAllowedException(__('This action can only be reached via AJAX.')); + } + + $selectedAttributeIDs = $this->_jsonDecode($selectedAttributes); + $event = $this->ObjectReference->Object->Event->find('first', array( + 'recursive' => -1, + 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.user_id', 'Event.publish_timestamp'), + 'conditions' => array('Event.id' => $eventId) + )); + $event = $this->ObjectReference->Object->Event->fetchEvent($this->Auth->user(), [ + 'eventid' => $eventId, + ]); + if (empty($event)) { + throw new NotFoundException(__('Invalid event.')); + } + $event = $event[0]; + if (!$this->__canModifyEvent($event)) { + throw new ForbiddenException(__('You do not have permission to do that.')); + } + + $validSourceUuid = []; + foreach ($event['Object'] as $object) { + $validSourceUuid[$object['uuid']] = sprintf('[%s] %s ', $object['id'], $object['name']); + } + $selectedAttributes = []; + foreach ($event['Attribute'] as $attribute) { + if (in_array($attribute['id'], $selectedAttributeIDs)) { + $selectedAttributes[$attribute['id']] = $attribute; + } + } + if ($this->request->is('post')) { + $conditions = [ + 'Object.deleted' => 0, + 'Object.uuid' => $this->data['ObjectReference']['source_uuid'], + ]; + $object = $this->ObjectReference->Object->find('first', array( + 'conditions' => $conditions, + 'recursive' => -1, + 'contain' => array( + 'Event' => array( + 'fields' => array('Event.id', 'Event.orgc_id', 'Event.user_id', 'Event.extends_uuid') + ) + ) + )); + if (empty($object) || !$this->__canModifyEvent($object)) { + throw new NotFoundException('Invalid object.'); + } + + if (!empty($this->request->data['ObjectReference']['relationship_type_select']) && $this->request->data['ObjectReference']['relationship_type_select'] != 'custom') { + $this->request->data['ObjectReference']['relationship_type'] = $this->request->data['ObjectReference']['relationship_type_select']; + } + $successCount = 0; + foreach ($selectedAttributes as $attributeID => $attribute) { + $referenced_type = 0; // reference type is always an attribute (for now?) + $newRelationship = array( + 'referenced_id' => $attributeID, + 'referenced_uuid' => $attribute['uuid'], + 'relationship_type' => $this->request->data['ObjectReference']['relationship_type'], + 'comment' => !empty($this->request->data['ObjectReference']['comment']) ? $this->request->data['ObjectReference']['comment'] : '', + 'event_id' => $event['Event']['id'], + 'object_uuid' => $object['Object']['uuid'], + 'source_uuid' => $object['Object']['uuid'], + 'object_id' => $object['Object']['id'], + 'referenced_type' => $referenced_type, + 'uuid' => CakeText::uuid() + ); + + $this->ObjectReference->create(); + $result = $this->ObjectReference->save(['ObjectReference' => $newRelationship]); + if ($result) { + $successCount += 1; + } + } + if ($successCount > 0) { + $this->ObjectReference->updateTimestamps($newRelationship); + if ($this->_isRest()) { + $object = $this->ObjectReference->find('first', [ + 'recursive' => -1, + 'conditions' => ['ObjectReference.id' => $this->ObjectReference->id] + ]); + $object['ObjectReference']['object_uuid'] = $object['Object']['uuid']; + return $this->RestResponse->viewData($object, $this->response->type()); + } elseif ($this->request->is('ajax')) { + $message = __('Added %s Object references.', $successCount); + return $this->RestResponse->saveSuccessResponse('ObjectReference', 'bulkAdd', $object['Object']['id'], false, $message); + } + } else { + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('ObjectReferences', 'bulkAdd', false, $this->ObjectReference->validationErrors, $this->response->type()); + } elseif ($this->request->is('ajax')) { + return $this->RestResponse->saveFailResponse('ObjectReferences', 'bulkAdd', $object['Object']['id'], $this->ObjectReference->validationErrors, $this->response->type()); + } + } + } + + + $this->loadModel('ObjectRelationship'); + $relationships = $this->ObjectRelationship->find('column', array( + 'recursive' => -1, + 'fields' => ['name'], + )); + $relationships = array_combine($relationships, $relationships); + $relationships['custom'] = 'custom'; + ksort($relationships); + $this->set('relationships', $relationships); + $this->set('validSourceUuid', $validSourceUuid); + $this->set('selectedAttributes', $selectedAttributes); + $this->layout = false; + $this->render('ajax/bulkAdd'); + } } diff --git a/app/View/Elements/eventattributetoolbar.ctp b/app/View/Elements/eventattributetoolbar.ctp index b22c57e0c..ea5e5998e 100644 --- a/app/View/Elements/eventattributetoolbar.ctp +++ b/app/View/Elements/eventattributetoolbar.ctp @@ -103,6 +103,15 @@ 'onClick' => 'proposeObjectsFromSelectedAttributes', 'onClickParams' => array('this', $eventId) ), + array( + 'id' => 'multi-relationship-button', + 'title' => __('Create new relationship for selected entities'), + 'class' => 'mass-select hidden', + 'fa-icon' => 'project-diagram', + 'fa-source' => 'fas', + 'onClick' => 'bulkAddRelationshipToSelectedAttributes', + 'onClickParams' => array('this', $eventId) + ), array( 'id' => 'multi-delete-button', 'title' => __('Delete selected Attributes'), diff --git a/app/View/ObjectReferences/ajax/bulkAdd.ctp b/app/View/ObjectReferences/ajax/bulkAdd.ctp new file mode 100644 index 000000000..b329815ee --- /dev/null +++ b/app/View/ObjectReferences/ajax/bulkAdd.ctp @@ -0,0 +1,76 @@ +'; + +foreach ($selectedAttributes as $attribute) { + $selectedAttributeHTML .= sprintf('
  • [%s] %s :: %s
  • ', h($attribute['id']), h($attribute['type']), h($attribute['value'])); +} +$selectedAttributeHTML .= ''; + +$fields = [ + sprintf('

    %s

    ', __n('Target Attribute', 'Target Attributes', count($selectedAttributes))), + $selectedAttributeHTML, + sprintf('

    %s

    ', __('Object Reference')), + [ + 'field' => 'relationship_type_select', + 'type' => 'dropdown', + 'class' => 'span6', + 'options' => $relationships, + 'picker' => true, + '_chosenOptions' => [ + 'width' => '460px', + ], + ], + [ + 'field' => 'relationship_type', + 'class' => 'span6', + 'div' => 'hidden', + ], + [ + 'field' => 'source_uuid', + 'class' => 'span6', + 'type' => 'dropdown', + 'picker' => true, + 'options' => $validSourceUuid, + '_chosenOptions' => [ + 'width' => '460px', + ], + ], + [ + 'field' => 'comment', + 'type' => 'textarea', + 'class' => 'input span6' + ], + +]; +echo $this->element('genericElements/Form/genericForm', [ + 'data' => [ + 'description' => false, + 'model' => 'ObjectReference', + 'title' => __('Bulk add object references to selected attributes'), + 'fields' => $fields, + 'submit' => [ + 'action' => $this->request->params['action'], + 'ajaxSubmit' => 'submitBulkAddForm();' + ] + ] +]); + +if (!$ajax) { + echo $this->element('/genericElements/SideMenu/side_menu', $menuData); +} + +?> + + \ No newline at end of file diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index fe64b3024..10b89952e 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -1046,6 +1046,12 @@ function proposeObjectsFromSelectedAttributes(clicked, event_id) { popoverPopup(clicked, event_id + '/' + selectedAttributeIds, 'objects', 'proposeObjectsFromAttributes'); } +function bulkAddRelationshipToSelectedAttributes(clicked, event_id) { + var selectedAttributeIds = getSelected(); + var url = baseurl + '/objectReferences/bulkAdd/' + event_id + '/' + selectedAttributeIds + openGenericModal(url) +} + function hideSelectedTags(taxonomy) { $.get(baseurl + "/taxonomies/taxonomyMassHide/"+taxonomy, openConfirmation).fail(xhrFailCallback); } @@ -5412,7 +5418,7 @@ function loadClusterRelations(clusterId) { } } -function submitGenericFormInPlace() { +function submitGenericFormInPlace(callback) { var $genericForm = $('.genericForm'); $.ajax({ type: "POST", @@ -5423,6 +5429,9 @@ function submitGenericFormInPlace() { window.location = data.redirect; return; } + if (callback) { + callback(data) + } $('#genericModal').modal('hide').remove(); $('body').append(data);