diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 3ab640863..ad1adfd6a 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -904,7 +904,7 @@ CREATE TABLE IF NOT EXISTS `tags` ( `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `colour` varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `exportable` tinyint(1) NOT NULL, - `org_id` tinyint(1) NOT NULL DEFAULT 0, + `org_id` int(11) NOT NULL DEFAULT 0, `user_id` int(11) NOT NULL DEFAULT 0, `hide_tag` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 439b90b16..9fe02627d 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -46,7 +46,7 @@ class AppController extends Controller public $helpers = array('Utility', 'OrgImg'); - private $__queryVersion = '50'; + private $__queryVersion = '51'; public $pyMispVersion = '2.4.99'; public $phpmin = '5.6.5'; public $phprec = '7.0.16'; diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index dce7db014..ffe2cf674 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -2838,15 +2838,31 @@ class AttributesController extends AppController $tag_id = $this->request->data['tag']; } if (!is_numeric($tag_id)) { - $tag = $this->Attribute->AttributeTag->Tag->find('first', array('recursive' => -1, 'conditions' => array('LOWER(Tag.name) LIKE' => strtolower(trim($tag_id))))); - if (empty($tag)) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status' => 200, 'type' => 'json')); + if (preg_match('/^collection_[0-9]+$/i', $tag_id)) { + $tagChoice = explode('_', $tag_id)[1]; + $this->loadModel('TagCollection'); + $tagCollection = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $tagChoice))); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag Collection.')), 'status'=>200, 'type' => 'json')); + } + $tag_id_list = array(); + foreach ($tagCollection[0]['TagCollectionTag'] as $tagCollectionTag) { + $tag_id_list[] = $tagCollectionTag['tag_id']; + } + } else { + $tag = $this->Event->EventTag->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); + if (empty($tag)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag_id = $tag['Tag']['id']; } - $tag_id = $tag['Tag']['id']; } if (!isset($idList)) { $idList = array($id); } + if (empty($tag_id_list)) { + $tag_id_list = array($tag_id); + } $success = 0; $fails = 0; foreach ($idList as $id) { @@ -2874,40 +2890,44 @@ class AttributesController extends AppController $this->Attribute->Event->insertLock($this->Auth->user(), $eventId); } $this->Attribute->recursive = -1; - $this->Attribute->AttributeTag->Tag->id = $tag_id; - if (!$this->Attribute->AttributeTag->Tag->exists()) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status' => 200, 'type' => 'json')); - } - $tag = $this->Attribute->AttributeTag->Tag->find('first', array( - 'conditions' => array('Tag.id' => $tag_id), - 'recursive' => -1, - 'fields' => array('Tag.name') - )); - $found = $this->Attribute->AttributeTag->find('first', array( - 'conditions' => array( - 'attribute_id' => $id, - 'tag_id' => $tag_id - ), - 'recursive' => -1, - )); - $this->autoRender = false; - if (!empty($found)) { - $fails++; - continue; - } - $this->Attribute->AttributeTag->create(); - if ($this->Attribute->AttributeTag->save(array('attribute_id' => $id, 'tag_id' => $tag_id, 'event_id' => $eventId))) { - $event['Event']['published'] = 0; - $date = new DateTime(); - $event['Event']['timestamp'] = $date->getTimestamp(); - $this->Attribute->Event->save($event); - $this->Attribute->data['Attribute']['timestamp'] = $date->getTimestamp(); - $this->Attribute->save($this->Attribute->data); - $log = ClassRegistry::init('Log'); - $log->createLogEntry($this->Auth->user(), 'tag', 'Attribute', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to attribute (' . $id . ')', 'Attribute (' . $id . ') tagged as Tag (' . $tag_id . ')'); - $success++; - } else { - $fails++; + + foreach ($tag_id_list as $tag_id) { + $this->Attribute->AttributeTag->Tag->id = $tag_id; + if (!$this->Attribute->AttributeTag->Tag->exists()) { + $fails++; + continue; + } + $tag = $this->Attribute->AttributeTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + $found = $this->Attribute->AttributeTag->find('first', array( + 'conditions' => array( + 'attribute_id' => $id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + $this->autoRender = false; + if (!empty($found)) { + $fails++; + continue; + } + $this->Attribute->AttributeTag->create(); + if ($this->Attribute->AttributeTag->save(array('attribute_id' => $id, 'tag_id' => $tag_id, 'event_id' => $eventId))) { + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + $this->Attribute->Event->save($event); + $this->Attribute->data['Attribute']['timestamp'] = $date->getTimestamp(); + $this->Attribute->save($this->Attribute->data); + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'Attribute', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to attribute (' . $id . ')', 'Attribute (' . $id . ') tagged as Tag (' . $tag_id . ')'); + $success++; + } else { + $fails++; + } } } if ($fails == 0) { diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 0ab2a6d9e..f321bab8e 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -181,6 +181,7 @@ class ACLComponent extends Component 'selectGalaxy' => array('perm_tagger'), 'selectGalaxyNamespace' => array('perm_tagger'), 'selectCluster' => array('perm_tagger'), + 'showGalaxies' => array('*'), 'update' => array(), 'view' => array('*'), 'viewGraph' => array('*') @@ -382,6 +383,16 @@ class ACLComponent extends Component 'viewSightings' => array('perm_sighting'), 'quickAdd' => array('perm_sighting') ), + 'tagCollections' => array( + 'add' => array('perm_tag_editor'), + 'addTag' => array('perm_tag_editor'), + 'delete' => array('perm_tag_editor'), + 'edit' => array('perm_tag_editor'), + 'getRow' => array('perm_tag_editor'), + 'index' => array('*'), + 'removeTag' => array('perm_tag_editor'), + 'view' => array('*') + ), 'tags' => array( 'add' => array('perm_tag_editor'), 'attachTagToObject' => array('perm_tagger'), @@ -394,6 +405,7 @@ class ACLComponent extends Component 'selectTaxonomy' => array('perm_tagger'), 'showEventTag' => array('*'), 'showAttributeTag' => array('*'), + 'showTagControllerTag' => array('*'), 'tagStatistics' => array('*'), 'view' => array('*'), 'viewGraph' => array('*'), diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 118829ef8..a70e6c109 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -1848,7 +1848,7 @@ class EventsController extends AppController $this->Event->read(null, $target_id); // check if private and user not authorised to edit if (!$this->_isSiteAdmin() && ($this->Event->data['Event']['orgc_id'] != $this->_checkOrg() || !($this->userRole['perm_modify']))) { - $this->Flash->error(__('You are not authorised to do that. Please consider using the \'propose attribute\' feature.')); + $this->Flash->error(__("You are not authorised to do that. Please consider using the 'propose attribute' feature.")); $this->redirect(array('action' => 'view', $target_id)); } $this->Event->insertLock($this->Auth->user(), $target_id); @@ -3318,11 +3318,24 @@ class EventsController extends AppController $conditions['Tag.user_id'] = array('0', $this->Auth->user('id')); } if (!is_numeric($tag_id)) { - $tag = $this->Event->EventTag->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); - if (empty($tag)) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + if (preg_match('/^collection_[0-9]+$/i', $tag_id)) { + $tagChoice = explode('_', $tag_id)[1]; + $this->loadModel('TagCollection'); + $tagCollection = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $tagChoice))); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag Collection.')), 'status'=>200, 'type' => 'json')); + } + $tag_id_list = array(); + foreach ($tagCollection[0]['TagCollectionTag'] as $tagCollectionTag) { + $tag_id_list[] = $tagCollectionTag['tag_id']; + } + } else { + $tag = $this->Event->EventTag->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); + if (empty($tag)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag_id = $tag['Tag']['id']; } - $tag_id = $tag['Tag']['id']; } $this->Event->recursive = -1; $event = $this->Event->read(array(), $id); @@ -3334,37 +3347,53 @@ class EventsController extends AppController return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'You don\'t have permission to do that.')), 'status'=>200, 'type' => 'json')); } } - $this->Event->EventTag->Tag->id = $tag_id; - if (!$this->Event->EventTag->Tag->exists()) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); - } - $tag = $this->Event->EventTag->Tag->find('first', array( - 'conditions' => array('Tag.id' => $tag_id), - 'recursive' => -1, - 'fields' => array('Tag.name') - )); - $found = $this->Event->EventTag->find('first', array( - 'conditions' => array( - 'event_id' => $id, - 'tag_id' => $tag_id - ), - 'recursive' => -1, - )); $this->autoRender = false; - if (!empty($found)) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag is already attached to this event.')), 'status'=>200, 'type' => 'json')); + $error = false; + $success = false; + if (empty($tag_id_list)) { + $tag_id_list = array($tag_id); } - $this->Event->EventTag->create(); - if ($this->Event->EventTag->save(array('event_id' => $id, 'tag_id' => $tag_id))) { - $event['Event']['published'] = 0; - $date = new DateTime(); - $event['Event']['timestamp'] = $date->getTimestamp(); - $this->Event->save($event); - $log = ClassRegistry::init('Log'); - $log->createLogEntry($this->Auth->user(), 'tag', 'Event', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to event (' . $id . ')', 'Event (' . $id . ') tagged as Tag (' . $tag_id . ')'); - return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Tag added.', 'check_publish' => true)), 'status'=>200, 'type' => 'json')); + foreach ($tag_id_list as $tag_id) { + $this->Event->EventTag->Tag->id = $tag_id; + if (!$this->Event->EventTag->Tag->exists()) { + $error = __('Invalid Tag.'); + continue; + } + $tag = $this->Event->EventTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + $found = $this->Event->EventTag->find('first', array( + 'conditions' => array( + 'event_id' => $id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + if (!empty($found)) { + $error = __('Tag is already attached to this event.'); + continue; + } + $this->Event->EventTag->create(); + if ($this->Event->EventTag->save(array('event_id' => $id, 'tag_id' => $tag_id))) { + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + $this->Event->save($event); + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'Event', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to event (' . $id . ')', 'Event (' . $id . ') tagged as Tag (' . $tag_id . ')'); + $success = __('Tag(s) added.'); + } else { + $fail = __('Tag could not be added.'); + } + } + if ($success) { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => __('Tag(s) added.'), 'check_publish' => true)), 'status'=>200, 'type' => 'json')); + } elseif (empty($fail)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => __('All tags are already present, nothing to add.'), 'check_publish' => true)), 'status'=>200, 'type' => 'json')); } else { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag could not be added.')), 'status'=>200, 'type' => 'json')); + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $fail)), 'status'=>200, 'type' => 'json')); } } diff --git a/app/Controller/GalaxiesController.php b/app/Controller/GalaxiesController.php index a84bde6c2..f06baa52a 100644 --- a/app/Controller/GalaxiesController.php +++ b/app/Controller/GalaxiesController.php @@ -173,8 +173,7 @@ class GalaxiesController extends AppController { $cluster_id = $this->request->data['Galaxy']['target_id']; $result = $this->Galaxy->attachCluster($this->Auth->user(), $target_type, $target_id, $cluster_id); - $this->Flash->info($result); - $this->redirect($this->referer()); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $result, 'check_publish' => true)), 'status'=>200, 'type' => 'json')); } public function attachMultipleClusters($target_id, $target_type = 'event') @@ -204,4 +203,28 @@ class GalaxiesController extends AppController $this->set('galaxy_id', $cluster['Galaxy']['id']); $this->render('/Events/view_graph'); } + + public function showGalaxies($id, $scope = 'event') + { + $this->layout = 'ajax'; + $this->set('scope', $scope); + if ($scope == 'event') { + $this->loadModel('Event'); + $object = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id, 'metadata' => 1)); + if (empty($object)) { + throw new MethodNotAllowedException('Invalid event.'); + } + $this->set('object', $object[0]); + + } elseif ($scope == 'attribute') { + $this->loadModel('Attribute'); + $object = $this->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id))); + if (empty($object)) { + throw new MethodNotAllowedException('Invalid attribute.'); + } + $object[0] = $this->Attribute->Event->massageTags($object[0], 'Attribute'); + } + $this->set('object', $object[0]); + $this->render('/Events/ajax/ajaxGalaxies'); + } } diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php index b71cb84aa..9bc216500 100644 --- a/app/Controller/ServersController.php +++ b/app/Controller/ServersController.php @@ -821,6 +821,11 @@ class ServersController extends AppController return $this->Server->loadAvailableLanguages(); } + private function __loadTagCollections() + { + return $this->Server->loadTagCollections($this->Auth->user()); + } + private function __loadLocalOrgs() { $this->loadModel('Organisation'); diff --git a/app/Controller/TagCollectionsController.php b/app/Controller/TagCollectionsController.php new file mode 100644 index 000000000..24bbefe30 --- /dev/null +++ b/app/Controller/TagCollectionsController.php @@ -0,0 +1,343 @@ + 60, + 'order' => array( + 'TagCollection.name' => 'ASC' + ), + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag'), 'User', 'Organisation') + ); + + public function add() + { + if ($this->request->is('post')) { + $this->TagCollection->create(); + if (!isset($this->request->data['TagCollection'])) { + $this->request->data = array('TagCollection' => $this->request->data); + } + $this->request->data['TagCollection']['org_id'] = $this->Auth->user('org_id'); + $this->request->data['TagCollection']['user_id'] = $this->Auth->user('id'); + if ($this->TagCollection->save($this->request->data)) { + if ($this->_isRest()) { + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array('TagCollection.id' => $this->TagCollection->id) + )); + return $this->RestResponse->viewData($tagCollection, $this->response->type()); + } else { + $this->Flash->success(__('The tag collection has been saved')); + $this->redirect(array('action' => 'index')); + } + } else { + $message = json_encode($this->TagCollection->validationErrors); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('TagCollection', 'add', false, $message, $this->response->type()); + } else { + $this->Flash->error(__('The tag collection could not be added. Reason: ') . $message); + } + } + } elseif ($this->_isRest()) { + return $this->RestResponse->describe('TagCollection', 'add', false, $this->response->type()); + } + $this->set('action', 'add'); + } + + public function view() + { + + } + + public function edit($id) + { + $this->TagCollection->id = $id; + if (!$this->TagCollection->exists()) { + throw new NotFoundException(__('Invalid Tag Collection')); + } + $tagCollection = $this->TagCollection->find('first', array( + 'conditions' => array('TagCollection.id' => $id), + 'recursive' => -1 + )); + if (!$this->_isSiteAdmin() && $tagCollection['TagCollection']['org_id'] !== $this->Auth->user('org_id')) { + throw new MethodNotAllowedException(__('You don\'t have editing rights on this Tag Collection.')); + } + if ($this->request->is('post') || $this->request->is('put')) { + if (!isset($this->request->data['TagCollection'])) { + $this->request->data = array('TagCollection' => $this->request->data); + } + $this->request->data['TagCollection']['id'] = $tagCollection['TagCollection']['id']; + $this->request->data['TagCollection']['uuid'] = $tagCollection['TagCollection']['uuid']; + if ($this->TagCollection->save($this->request->data)) { + if ($this->_isRest()) { + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array('TagCollection.id' => $this->TagCollection->id) + )); + return $this->RestResponse->viewData($tagCollection, $this->response->type()); + } else { + $this->Flash->success(__('The tag collection has been saved')); + $this->redirect(array('action' => 'index')); + } + } else { + $message = json_encode($this->TagCollection->validationErrors); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('TagCollection', 'add', false, $message, $this->response->type()); + } else { + $this->Flash->error(__('The tag collection could not be added. Reason: ') . $message); + } + } + } elseif ($this->_isRest()) { + return $this->RestResponse->describe('TagCollection', 'add', false, $this->response->type()); + } else { + $this->request->data = $tagCollection; + } + $this->set('action', 'edit'); + $this->render('add'); + } + + public function delete($id) + { + $tagCollection = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $id))); + if (empty($tagCollection)) { + throw new NotFoundException(__('Invalid tag collection.')); + } + $tagCollection = $tagCollection[0]; + if ($this->TagCollection->checkAccess($this->Auth->user(), $tagCollection, 'write')) { + $result = $this->TagCollection->delete($id); + if ($result) { + $message = __('Tag collection deleted.'); + if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse('TagCollections', 'delete', false, $this->response->type(), $message); + } else { + $this->Flash->success($message); + $this->redirect('index'); + } + } else { + $message = __('Tag collection could not be deleted.'); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('TagCollections', 'delete', false, $message, $this->response->type()); + } else { + $this->Flash->error($message); + $this->redirect('index'); + } + } + } else { + throw new NotFoundException(__('You are not allowed to delete that.')); + } + } + + public function addTag($id = false, $tag_id = false) + { + if (!$this->request->is('post')) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'You don\'t have permission to do that.')), 'status'=>200, 'type' => 'json')); + } + $rearrangeRules = array( + 'request' => false, + 'TagCollection' => false, + 'tag_id' => 'tag', + 'tag_collection_id' => 'tag_collection', + 'id' => 'tag_collection' + ); + $RearrangeTool = new RequestRearrangeTool(); + $this->request->data = $RearrangeTool->rearrangeArray($this->request->data, $rearrangeRules); + if ($id === false) { + $id = $this->request->data['tag_collection']; + } + if ($tag_id === false) { + $tag_id = $this->request->data['tag']; + } + $conditions = array('LOWER(Tag.name) LIKE' => strtolower(trim($tag_id))); + if (!$this->_isSiteAdmin()) { + $conditions['Tag.org_id'] = array('0', $this->Auth->user('org_id')); + $conditions['Tag.user_id'] = array('0', $this->Auth->user('id')); + } + if (!is_numeric($tag_id)) { + $tag = $this->TagCollection->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); + if (empty($tag)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag_id = $tag['Tag']['id']; + } + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array('TagCollection.id' => $id) + )); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid tag collection.')), 'status'=>200, 'type' => 'json')); + } + if (!$this->_isSiteAdmin()) { + if (!$this->userRole['perm_tagger'] || ($this->Auth->user('org_id') !== $tag_collection['TagCollection']['org_id'])) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'You don\'t have permission to do that.')), 'status'=>200, 'type' => 'json')); + } + } + $this->TagCollection->TagCollectionTag->Tag->id = $tag_id; + if (!$this->TagCollection->TagCollectionTag->Tag->exists()) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag = $this->TagCollection->TagCollectionTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + $found = $this->TagCollection->TagCollectionTag->find('first', array( + 'conditions' => array( + 'tag_collection_id' => $id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + $this->autoRender = false; + if (!empty($found)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag is already attached to this collection.')), 'status'=>200, 'type' => 'json')); + } + $this->TagCollection->TagCollectionTag->create(); + if ($this->TagCollection->TagCollectionTag->save(array('tag_collection_id' => $id, 'tag_id' => $tag_id))) { + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'TagCollection', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to collection (' . $id . ')', 'Event (' . $id . ') tagged as Tag (' . $tag_id . ')'); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Tag added.')), 'status'=>200, 'type' => 'json')); + } else { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag could not be added.')), 'status'=>200, 'type' => 'json')); + } + } + + public function removeTag($id = false, $tag_id = false) + { + if (!$this->request->is('post')) { + $this->set('id', $id); + $this->set('tag_id', $tag_id); + $this->set('model', 'tag_collection'); + $this->layout = 'ajax'; + $this->render('/Attributes/ajax/tagRemoveConfirmation'); + } else { + $rearrangeRules = array( + 'request' => false, + 'TagCollection' => false, + 'tag_id' => 'tag', + 'tag_collection_id' => 'tag_collection', + 'id' => 'tag_collection' + ); + $RearrangeTool = new RequestRearrangeTool(); + $this->request->data = $RearrangeTool->rearrangeArray($this->request->data, $rearrangeRules); + if ($id === false) { + $id = $this->request->data['tag_collection']; + } + if ($tag_id === false) { + $tag_id = $this->request->data['tag']; + } + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array( + 'TagCollection.id' => $id + ), + 'contain' => array( + 'TagCollectionTag' => array( + 'Tag' + ) + ) + )); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Invalid tag collection.'))), 'status' => 200, 'type' => 'json')); + } + $found = false; + if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $tagCollection['TagCollection']['org_id']) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Insufficient privileges to remove the tag from the collection.'))), 'status' => 200, 'type' => 'json')); + } + foreach ($tagCollection['TagCollectionTag'] as $TagCollectionTag) { + if ((is_numeric($tag_id) && $TagCollectionTag['Tag']['id'] == $tag_id) || $TagCollectionTag['Tag']['name'] === $tag_id) { + $found = true; + $tag = $TagCollectionTag; + $result = $this->TagCollection->TagCollectionTag->delete($TagCollectionTag['id']); + break; + } + } + if (!$found) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Invalid tag or tag not associated with the collection.'))), 'status' => 200, 'type' => 'json')); + } + + if (!$result) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Failed to remove tag from the collection.'))), 'status' => 200, 'type' => 'json')); + } + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'TagCollection', $id, 'Removed tag (' . $tag['Tag']['id'] . ') "' . $tag['Tag']['name'] . '" from tag collection (' . $id . ')', 'Tag collection (' . $id . ') - untagged Tag (' . $tag_id . ')'); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Tag removed.')), 'status' => 200)); + } + } + + public function index() + { + //$this->Auth->user('Role')['perm_site_admin']); + $conditions = array(); + if (!$this->_isSiteAdmin()) { + $conditions = array( + 'OR' => array( + 'TagCollection.all_orgs' => 1, + 'TagCollection.org_id' => $this->Auth->user('org_id') + ) + ); + $this->paginate['conditions'] = $conditions; + } + if ($this->_isRest()) { + $params = array( + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag'), 'Organisation', 'User') + ); + if (!empty($conditions)) { + $params['conditions'] = $conditions; + } + $namedParams = array('limit', 'page'); + foreach ($namedParams as $namedParam) { + if (!empty($this->params['named'][$namedParam])) { + $params['limit'] = $this->params['named'][$namedParam]; + } + } + $list = $this->TagCollection->find('all', $params); + } else { + $list = $this->paginate(); + } + $this->loadModel('Event'); + foreach ($list as $k => $tag_collection) { + $list[$k] = $this->TagCollection->cullBlockedTags($this->Auth->user(), $tag_collection); + $list[$k] = $this->Event->massageTags($list[$k], 'TagCollection', false, true); + if (!$this->_isSiteAdmin() && $list[$k]['TagCollection']['org_id'] !== $this->Auth->user('org_id')) { + unset($list[$k]['User']); + unset($list[$k]['TagCollection']['user_id']); + } + } + if ($this->_isRest()) { + return $this->RestResponse->viewData($list, $this->response->type()); + } else { + $this->set('list', $list); + } + } + + public function getRow($id) + { + $params = array( + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag'), 'User', 'Organisation'), + 'conditions' => array('TagCollection.id' => $id) + ); + $item = $this->TagCollection->find('first', $params); + if (empty($item)) { + throw new NotFoundException('Invalid tag collection.'); + } + if (!$this->_isSiteAdmin() && $item['TagCollection']['org_id'] !== $this->Auth->user('org_id')) { + unset($item['User']); + unset($item['TagCollection']['user_id']); + } + $this->loadModel('Event'); + $item = $this->Event->massageTags($item, 'TagCollection', false, true); + $this->layout = false; + $this->set('item', $item); + } +} diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index 5508a9e13..ba5424a8f 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -519,6 +519,39 @@ class TagsController extends AppController $this->render('/Attributes/ajax/ajaxAttributeTags'); } + public function showTagControllerTag($id) + { + $this->loadModel('TagCollection'); + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'contain' => array('TagCollection'), + 'conditions' => array('TagCollection.id' => $id) + )); + if (empty($tagCollection) || (!$this->_isSiteAdmin() && $tagCollection['org_id'] !== $this->Auth->user('org_id'))) { + throw new MethodNotAllowedException('Invalid tag_collection.'); + } + $this->loadModel('GalaxyCluster'); + $cluster_names = $this->GalaxyCluster->find('list', array('fields' => array('GalaxyCluster.tag_name'), 'group' => array('GalaxyCluster.id', 'GalaxyCluster.tag_name'))); + $this->helpers[] = 'TextColour'; + $tags = $this->TagCollection->TagCollectionTag->find('all', array( + 'conditions' => array( + 'tag_collection_id' => $id, + 'Tag.name !=' => $cluster_names + ), + 'contain' => array('Tag'), + 'fields' => array('Tag.id', 'Tag.colour', 'Tag.name'), + )); + $this->set('tags', $tags); + $event = $this->Tag->EventTag->Event->find('first', array( + 'recursive' => -1, + 'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.user_id'), + 'conditions' => array('Event.id' => $id) + )); + $this->set('event', $event); + $this->layout = 'ajax'; + $this->render('/Events/ajax/ajaxTags'); + } + public function viewTag($id) { $tag = $this->Tag->find('first', array( @@ -534,7 +567,7 @@ class TagsController extends AppController } - public function selectTaxonomy($id, $attributeTag = false) + public function selectTaxonomy($id, $scope = 'event') { if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) { throw new NotFoundException('You don\'t have permission to do that.'); @@ -548,96 +581,126 @@ class TagsController extends AppController unset($options[$k]); } } - if ($attributeTag !== false) { - $this->set('attributeTag', true); - } + $this->set('scope', $scope); $this->set('object_id', $id); $this->set('options', $options); $this->set('favourites', $favourites); $this->render('ajax/taxonomy_choice'); } - public function selectTag($id, $taxonomy_id, $attributeTag = false, $filterData = '') + public function selectTag($id, $taxonomy_id, $scope = 'event', $filterData = '') { if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) { throw new NotFoundException('You don\'t have permission to do that.'); } $this->loadModel('Taxonomy'); $expanded = array(); - if ($taxonomy_id === '0') { - $options = $this->Taxonomy->getAllTaxonomyTags(true); - $expanded = $options; - } elseif ($taxonomy_id === 'favourites') { - $conditions = array('FavouriteTag.user_id' => $this->Auth->user('id')); - $tags = $this->Tag->FavouriteTag->find('all', array( - 'conditions' => $conditions, - 'recursive' => -1, - 'contain' => array('Tag.name') - )); - foreach ($tags as $tag) { - $options[$tag['FavouriteTag']['tag_id']] = $tag['Tag']['name']; - $expanded = $options; - } - } elseif ($taxonomy_id === 'all') { - $conditions = array('Tag.org_id' => array(0, $this->Auth->user('org_id'))); - $conditions = array('Tag.user_id' => array(0, $this->Auth->user('id'))); - $conditions['Tag.hide_tag'] = 0; - $options = $this->Tag->find('list', array('fields' => array('Tag.name'), 'conditions' => $conditions)); - $expanded = $options; - } else { - $taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id); - $options = array(); - foreach ($taxonomies['entries'] as $entry) { - if (!empty($entry['existing_tag']['Tag'])) { - $options[$entry['existing_tag']['Tag']['id']] = $entry['existing_tag']['Tag']['name']; - $expanded[$entry['existing_tag']['Tag']['id']] = $entry['expanded']; - } - } - } - // Unset all tags that this user cannot use for tagging, determined by the org restriction on tags - if (!$this->_isSiteAdmin()) { - $banned_tags = $this->Tag->find('list', array( - 'conditions' => array( - 'NOT' => array( - 'Tag.org_id' => array( - 0, - $this->Auth->user('org_id') - ), - 'Tag.user_id' => array( - 0, - $this->Auth->user('id') - ) - ) - ), - 'fields' => array('Tag.id') - )); - foreach ($banned_tags as $banned_tag) { - unset($options[$banned_tag]); - unset($expanded[$banned_tag]); - } - } - $hidden_tags = $this->Tag->find('list', array( - 'conditions' => array('Tag.hide_tag' => 1), + $banned_tags = $this->Tag->find('list', array( + 'conditions' => array( + 'NOT' => array( + 'Tag.org_id' => array( + 0, + $this->Auth->user('org_id') + ), + 'Tag.user_id' => array( + 0, + $this->Auth->user('id') + ) + ) + ), 'fields' => array('Tag.id') )); - foreach ($hidden_tags as $hidden_tag) { - unset($options[$hidden_tag]); - unset($expanded[$hidden_tag]); - } - if ($attributeTag !== false && $attributeTag !== "false") { - $this->set('attributeTag', true); - } - $this->set('object_id', $id); - foreach ($options as $k => $v) { - if (substr($v, 0, strlen('misp-galaxy:')) === 'misp-galaxy:') { - unset($options[$k]); + $this->set('taxonomy_id', $taxonomy_id); + if ($taxonomy_id === 'collections') { + $this->loadModel('TagCollection'); + $tagCollections = $this->TagCollection->fetchTagCollection($this->Auth->user()); + $options = array(); + $expanded = array(); + foreach ($tagCollections as &$tagCollection) { + $options[$tagCollection['TagCollection']['id']] = $tagCollection['TagCollection']['name']; + $expanded[$tagCollection['TagCollection']['id']] = empty($tagCollection['TagCollection']['description']) ? $tagCollection['TagCollection']['name'] : $tagCollection['TagCollection']['description']; + if (!empty($tagCollection['TagCollectionTag'])) { + $tagList = array(); + foreach ($tagCollection['TagCollectionTag'] as $k => $tce) { + if (in_array($tce['tag_id'], $banned_tags)) { + unset($tagCollection['TagCollectionTag'][$k]); + } else { + $tagList[] = $tce['Tag']['name']; + } + $tagCollection['TagCollectionTag'] = array_values($tagCollection['TagCollectionTag']); + } + $tagList = implode(', ', $tagList); + $expanded[$tagCollection['TagCollection']['id']] .= sprintf(' (%s)', $tagList); + } } + $this->set('scope', $scope); + $this->set('object_id', $id); + $this->set('options', $options); + $this->set('expanded', $expanded); + $this->set('custom', $taxonomy_id == 0 ? true : false); + $this->set('filterData', $filterData); + $this->render('ajax/select_tag'); + } else { + if ($taxonomy_id === '0') { + $options = $this->Taxonomy->getAllTaxonomyTags(true); + $expanded = $options; + } elseif ($taxonomy_id === 'favourites') { + $conditions = array('FavouriteTag.user_id' => $this->Auth->user('id')); + $tags = $this->Tag->FavouriteTag->find('all', array( + 'conditions' => $conditions, + 'recursive' => -1, + 'contain' => array('Tag.name') + )); + foreach ($tags as $tag) { + $options[$tag['FavouriteTag']['tag_id']] = $tag['Tag']['name']; + $expanded = $options; + } + } elseif ($taxonomy_id === 'all') { + $conditions = array('Tag.org_id' => array(0, $this->Auth->user('org_id'))); + $conditions = array('Tag.user_id' => array(0, $this->Auth->user('id'))); + $conditions['Tag.hide_tag'] = 0; + $options = $this->Tag->find('list', array('fields' => array('Tag.name'), 'conditions' => $conditions)); + $expanded = $options; + } else { + $taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id); + $options = array(); + if (!empty($taxonomies['entries'])) { + foreach ($taxonomies['entries'] as $entry) { + if (!empty($entry['existing_tag']['Tag'])) { + $options[$entry['existing_tag']['Tag']['id']] = $entry['existing_tag']['Tag']['name']; + $expanded[$entry['existing_tag']['Tag']['id']] = $entry['expanded']; + } + } + } + } + // Unset all tags that this user cannot use for tagging, determined by the org restriction on tags + if (!$this->_isSiteAdmin()) { + foreach ($banned_tags as $banned_tag) { + unset($options[$banned_tag]); + unset($expanded[$banned_tag]); + } + } + $hidden_tags = $this->Tag->find('list', array( + 'conditions' => array('Tag.hide_tag' => 1), + 'fields' => array('Tag.id') + )); + foreach ($hidden_tags as $hidden_tag) { + unset($options[$hidden_tag]); + unset($expanded[$hidden_tag]); + } + $this->set('scope', $scope); + $this->set('object_id', $id); + foreach ($options as $k => $v) { + if (substr($v, 0, strlen('misp-galaxy:')) === 'misp-galaxy:') { + unset($options[$k]); + } + } + $this->set('options', $options); + $this->set('expanded', $expanded); + $this->set('custom', $taxonomy_id == 0 ? true : false); + $this->set('filterData', $filterData); + $this->render('ajax/select_tag'); } - $this->set('options', $options); - $this->set('expanded', $expanded); - $this->set('custom', $taxonomy_id == 0 ? true : false); - $this->set('filterData', $filterData); - $this->render('ajax/select_tag'); } public function tagStatistics($percentage = false, $keysort = false) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 9ab912f74..9aeb3cf23 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -71,7 +71,8 @@ class AppModel extends Model 1 => false, 2 => false, 3 => false, 4 => true, 5 => false, 6 => false, 7 => false, 8 => false, 9 => false, 10 => false, 11 => false, 12 => false, 13 => false, 14 => false, 15 => false, 18 => false, 19 => false, 20 => false, - 21 => false, 22 => false, 23 => false, 24 => false, 25 => false + 21 => false, 22 => false, 23 => false, 24 => false, 25 => false, 26 => false, + 27 => false ); public function afterSave($created, $options = array()) @@ -1059,6 +1060,32 @@ class AppModel extends Model $this->__addIndex('galaxy_clusters', 'uuid'); $this->__addIndex('galaxy_clusters', 'collection_uuid'); break; + case 26: + $sqlArray[] = "CREATE TABLE IF NOT EXISTS tag_collections ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `user_id` int(11) NOT NULL, + `org_id` int(11) NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `all_orgs` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (id), + INDEX `uuid` (`uuid`), + INDEX `user_id` (`user_id`), + INDEX `org_id` (`org_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + $sqlArray[] = "CREATE TABLE IF NOT EXISTS tag_collection_tags ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `tag_collection_id` int(11) NOT NULL, + `tag_id` int(11) NOT NULL, + PRIMARY KEY (id), + INDEX `uuid` (`tag_collection_id`), + INDEX `user_id` (`tag_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + break; + case 27: + $sqlArray[] = 'ALTER TABLE `tags` CHANGE `org_id` `org_id` int(11) NOT NULL DEFAULT 0;'; + break; case 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; diff --git a/app/Model/Event.php b/app/Model/Event.php index 1a90abfd4..5730b70f6 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3288,6 +3288,39 @@ class Event extends AppModel } $this->publish($this->getID(), $passAlong); } + if (empty($data['Event']['locked']) && !empty(Configure::read('MISP.default_event_tag_collection'))) { + $this->TagCollection = ClassRegistry::init('TagCollection'); + $tagCollection = $this->TagCollection->fetchTagCollection($user, array('conditions' => array('TagCollection.id' => Configure::read('MISP.default_event_tag_collection')))); + if (!empty($tagCollection)) { + $tag_id_list = array(); + foreach ($tagCollection[0]['TagCollectionTag'] as $tagCollectionTag) { + $tag_id_list[] = $tagCollectionTag['tag_id']; + } + foreach ($tag_id_list as $tag_id) { + $tag = $this->EventTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + if (!empty($tag)) { + $found = $this->EventTag->find('first', array( + 'conditions' => array( + 'event_id' => $this->id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + if (empty($found)) { + $this->EventTag->create(); + if ($this->EventTag->save(array('event_id' => $this->id, 'tag_id' => $tag_id))) { + $log = ClassRegistry::init('Log'); + $log->createLogEntry($user, 'tag', 'Event', $this->id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to event (' . $this->id . ')', 'Event (' . $this->id . ') tagged as Tag (' . $tag_id . ')'); + } + } + } + } + } + } return true; } else { $validationErrors['Event'] = $this->validationErrors; @@ -5244,7 +5277,7 @@ class Event extends AppModel return $attributes_added; } - public function massageTags($data, $dataType = 'Event', $excludeGalaxy = false) + public function massageTags($data, $dataType = 'Event', $excludeGalaxy = false, $cullGalaxyTags = false) { $data['Galaxy'] = array(); if (empty($this->GalaxyCluster)) { @@ -5277,6 +5310,7 @@ class Event extends AppModel unset($temp['GalaxyCluster']['Galaxy']); $data['Galaxy'][count($data['Galaxy']) - 1]['GalaxyCluster'][] = $temp['GalaxyCluster']; } + unset($data[$dataType . 'Tag'][$k]); } } } diff --git a/app/Model/Galaxy.php b/app/Model/Galaxy.php index 82adc6be3..39d763a79 100644 --- a/app/Model/Galaxy.php +++ b/app/Model/Galaxy.php @@ -179,55 +179,54 @@ class Galaxy extends AppModel return true; } + private function __attachClusterToEvent($user, $target_id, $cluster_id) { + + } + public function attachCluster($user, $target_type, $target_id, $cluster_id) { + $connectorModel = Inflector::camelize($target_type) . 'Tag'; $cluster = $this->GalaxyCluster->find('first', array('recursive' => -1, 'conditions' => array('id' => $cluster_id), 'fields' => array('tag_name', 'id', 'value'))); $this->Tag = ClassRegistry::init('Tag'); - if ($target_type == 'event') { - $event = $this->Tag->EventTag->Event->fetchEvent($user, array('eventid' => $target_id, 'metadata' => 1)); - if (empty($event)) { - throw new NotFoundException('Invalid event.'); - } - $event = $event[0]; - $tag_id = $this->Tag->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user); - if ($tag_id === false) { - throw new MethodNotAllowedException('Could not attach cluster.'); - } - $this->Tag->EventTag->create(); - $existingTag = $this->Tag->EventTag->find('first', array('conditions' => array('event_id' => $target_id, 'tag_id' => $tag_id))); - if (!empty($existingTag)) { - return 'Cluster already attached.'; - } - $result = $this->Tag->EventTag->save(array('event_id' => $target_id, 'tag_id' => $tag_id)); - } elseif ($target_type == 'attribute') { - $attribute = $this->Tag->AttributeTag->Attribute->fetchAttributes($user, array('conditions' => array('Attribute.id' => $target_id), 'flatten' => 1)); - if (empty($attribute)) { - throw new NotFoundException('Invalid attribute.'); - } - $attribute = $attribute[0]; - $tag_id = $this->Tag->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user); - if ($tag_id === false) { - throw new MethodNotAllowedException('Could not attach cluster.'); - } - $this->Tag->AttributeTag->create(); - $existingTag = $this->Tag->AttributeTag->find('first', array('conditions' => array('attribute_id' => $target_id, 'tag_id' => $tag_id))); - if (!empty($existingTag)) { - return 'Cluster already attached.'; - } + if ($target_type === 'event') { + $target = $this->Tag->EventTag->Event->fetchEvent($user, array('eventid' => $target_id, 'metadata' => 1)); + } elseif ($target_type === 'attribute') { + $target = $this->Tag->AttributeTag->Attribute->fetchAttributes($user, array('conditions' => array('Attribute.id' => $target_id), 'flatten' => 1)); + } elseif ($target_type === 'tag_collection') { + $target = $this->Tag->TagCollectionTag->TagCollection->fetchTagCollection($user, array('conditions' => array('TagCollection.id' => $target_id))); + } + if (empty($target)) { + throw new NotFoundException(__('Invalid %s.', $target_type)); + } + $target = $target[0]; + $tag_id = $this->Tag->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user); + $existingTag = $this->Tag->$connectorModel->find('first', array('conditions' => array($target_type . '_id' => $target_id, 'tag_id' => $tag_id))); + if (!empty($existingTag)) { + return 'Cluster already attached.'; + } + $this->Tag->$connectorModel->create(); + $toSave = array($target_type . '_id' => $target_id, 'tag_id' => $tag_id); + if ($target_type === 'attribute') { $event = $this->Tag->EventTag->Event->find('first', array( 'conditions' => array( - 'Event.id' => $attribute['Attribute']['event_id'] + 'Event.id' => $target['Attribute']['event_id'] ), 'recursive' => -1 )); - $result = $this->Tag->AttributeTag->save(array('attribute_id' => $target_id, 'tag_id' => $tag_id, 'event_id' => $attribute['Attribute']['event_id'])); + $toSave['event_id'] = $target['Attribute']['event_id']; } + $result = $this->Tag->$connectorModel->save($toSave); if ($result) { - $this->Tag->EventTag->Event->insertLock($user, $event['Event']['id']); - $event['Event']['published'] = 0; - $date = new DateTime(); - $event['Event']['timestamp'] = $date->getTimestamp(); - $this->Tag->EventTag->Event->save($event); + if ($target_type !== 'tag_collection') { + if ($target_type === 'event') { + $event = $target; + } + $this->Tag->EventTag->Event->insertLock($user, $event['Event']['id']); + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + $this->Tag->EventTag->Event->save($event); + } $this->Log = ClassRegistry::init('Log'); $this->Log->create(); $this->Log->save(array( diff --git a/app/Model/Server.php b/app/Model/Server.php index 38866b66f..82ed522c9 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -522,6 +522,15 @@ class Server extends AppModel 'type' => 'string', 'options' => array('1' => 'High', '2' => 'Medium', '3' => 'Low', '4' => 'undefined'), ), + 'default_event_tag_collection' => array( + 'level' => 0, + 'description' => __('The tag collection to be applied to all events created manually.'), + 'value' => 0, + 'errorMessage' => '', + 'test' => 'testTagCollections', + 'type' => 'numeric', + 'optionsSource' => 'TagCollections', + ), 'tagging' => array( 'level' => 1, 'description' => __('Enable the tagging feature of MISP. This is highly recommended.'), @@ -2643,6 +2652,27 @@ class Server extends AppModel return true; } + public function loadTagCollections() + { + $this->TagCollection = ClassRegistry::init('TagCollection'); + $user = array('Role' => array('perm_site_admin' => 1)); + $tagCollections = $this->TagCollection->fetchTagCollection($user); + $options = array(0 => 'None'); + foreach ($tagCollections as $tagCollection) { + $options[intval($tagCollection['TagCollection']['id'])] = $tagCollection['TagCollection']['name']; + } + return $options; + } + + public function testTagCollections($value) + { + $tag_collections = $this->loadTagCollections(); + if (!isset($tag_collections[intval($value)])) { + return 'Invalid tag_collection.'; + } + return true; + } + public function testForNumeric($value) { if (!is_numeric($value)) { @@ -3161,6 +3191,23 @@ class Server extends AppModel public function serverSettingsSaveValue($setting, $value) { + // validate if current config.php is intact: + $current = file_get_contents(APP . 'Config' . DS . 'config.php'); + $current = trim($current); + if (strlen($current) < 20) { + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => 'SYSTEM', + 'model' => 'Server', + 'model_id' => $id, + 'email' => 'SYSTEM', + 'action' => 'error', + 'user_id' => 0, + 'title' => 'Error: Tried to modify server settings but current config is broken.', + )); + return false; + } copy(APP . 'Config' . DS . 'config.php', APP . 'Config' . DS . 'config.php.bk'); $settingObject = $this->getCurrentServerSettings(); foreach ($settingObject as $branchName => $branch) { @@ -3215,6 +3262,17 @@ class Server extends AppModel // if the saved config file is empty, restore the backup. if (strlen($config_saved) < 20) { copy(APP . 'Config' . DS . 'config.php.bk', APP . 'Config' . DS . 'config.php'); + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => 'SYSTEM', + 'model' => 'Server', + 'model_id' => $id, + 'email' => 'SYSTEM', + 'action' => 'error', + 'user_id' => 0, + 'title' => 'Error: Something went wrong saving the config file, reverted to backup file.', + )); return false; } return true; diff --git a/app/Model/Tag.php b/app/Model/Tag.php index dc9fbac0b..5b63e0f76 100644 --- a/app/Model/Tag.php +++ b/app/Model/Tag.php @@ -52,6 +52,9 @@ class Tag extends AppModel ), 'AttributeTag' => array( 'dependent' => true + ), + 'TagCollectionTag' => array( + 'dependent' => true ) ); diff --git a/app/Model/TagCollection.php b/app/Model/TagCollection.php new file mode 100644 index 000000000..56fdb5890 --- /dev/null +++ b/app/Model/TagCollection.php @@ -0,0 +1,122 @@ + array( + 'roleModel' => 'Role', + 'roleKey' => 'role_id', + 'change' => 'full' + ), + 'Containable' + ); + + public $hasMany = array( + 'TagCollectionTag' => array( + 'dependent' => true + ) + ); + + public $belongsTo = array( + 'User', + 'Organisation' => array( + 'foreignKey' => 'org_id' + ) + ); + + public $whitelistedItems = false; + + public $validate = array( + 'name' => array( + 'valueNotEmpty' => array( + 'rule' => array('valueNotEmpty'), + ), + 'unique' => array( + 'rule' => 'isUnique', + 'message' => 'A similar name already exists.', + ), + ) + ); + + public function beforeValidate($options = array()) + { + parent::beforeValidate(); + // generate UUID if it doesn't exist + if (empty($this->data['TagCollection']['uuid'])) { + $this->data['TagCollection']['uuid'] = CakeText::uuid(); + } + return true; + } + + public function fetchTagCollection($user, $params = array()) + { + if (empty($user['Role']['perm_site_admin'])) { + $params['conditions']['AND'][] = array( + 'OR' => array( + 'TagCollection.org_id' => $user['org_id'], + 'TagCollection.all_orgs' => 1 + ) + ); + } + if (empty($params['contain'])) { + $params['contain'] = array( + 'Organisation', + 'User', + 'TagCollectionTag' => array('Tag') + ); + } + $tagCollections = $this->find('all', $params); + $tagCollections = $this->cullBlockedTags($user, $tagCollections); + return $tagCollections; + } + + public function checkAccess($user, $tagCollection, $accessLevel = 'read') + { + if (isset($tagCollection['TagCollection'])) { + $tagCollection = $tagCollection['TagCollection']; + } + if (!empty($user['Role']['admin'])) { + return true; + } + if (!$tagCollection['all_orgs'] && $user['org_id'] != $tagCollection['org_id']) { + return false; + } + if ($accessLevel === 'write') { + if ($tagCollection['org_id'] !== $user['org_id']) { + return false; + } + } + return true; + } + + public function cullBlockedTags($user, $tagCollections) + { + $single = false; + if (!isset($tagCollections[0])) { + $tagCollections = array(0 => $tagCollections); + $single = true; + } + if (empty($user['Role']['perm_site_admin'])) { + foreach ($tagCollections as $k => $tagCollection) { + foreach ($tagCollection['TagCollectionTag'] as $k2 => $tagCollectionTag) { + if ( + (!empty($tagCollectionTag['Tag']['org_id']) && $tagCollectionTag['Tag']['org_id'] != $user['org_id']) || + (!empty($tagCollectionTag['Tag']['user_id']) && $tagCollectionTag['Tag']['user_id'] != $user['id']) || + $tagCollectionTag['Tag']['hide_tag'] + ) { + unset($tagCollections[$k]['TagCollectionTag'][$k2]); + } + } + $tagCollections[$k]['TagCollectionTag'] = array_values($tagCollections[$k]['TagCollectionTag']); + } + } + return $single ? $tagCollections[0] : $tagCollections; + } +} diff --git a/app/Model/TagCollectionTag.php b/app/Model/TagCollectionTag.php new file mode 100644 index 000000000..fde336c09 --- /dev/null +++ b/app/Model/TagCollectionTag.php @@ -0,0 +1,31 @@ + array( + 'roleModel' => 'Role', + 'roleKey' => 'role_id', + 'change' => 'full' + ), + 'Containable' + ); + + public $belongsTo = array( + 'TagCollection' => array( + 'className' => 'TagCollection', + ), + 'Tag' => array( + 'className' => 'Tag', + ) + ); + + public $validate = array( + + ); +} diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index cae6a8f23..8fe7426e7 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -163,7 +163,7 @@ ); } ?> - + element('galaxyQuickViewMini', array( 'mayModify' => $mayModify, diff --git a/app/View/Elements/Events/eventIndexTable.ctp b/app/View/Elements/Events/eventIndexTable.ctp index 67aa76e07..b4a239289 100644 --- a/app/View/Elements/Events/eventIndexTable.ctp +++ b/app/View/Elements/Events/eventIndexTable.ctp @@ -92,7 +92,6 @@ OrgImg->getOrgImg(array('name' => $event['Orgc']['name'], 'id' => $event['Orgc']['id'], 'size' => 24)); ?> -   diff --git a/app/View/Elements/TagCollections/index_row.ctp b/app/View/Elements/TagCollections/index_row.ctp new file mode 100644 index 000000000..855f07b2c --- /dev/null +++ b/app/View/Elements/TagCollections/index_row.ctp @@ -0,0 +1,48 @@ + +   +   +   + +
+ element( + 'ajaxTagCollectionTags', + array( + 'attributeId' => $item['TagCollection']['id'], + 'attributeTags' => $item['TagCollectionTag'], + 'tagAccess' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), + 'context' => 'tagCollection', + 'tagCollection' => $item + ) + ); + ?> +
+ + + element('galaxyQuickViewMini', array( + 'mayModify' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), + 'isAclTagger' => true, + 'data' => $item['Galaxy'], + 'target_id' => h($item['TagCollection']['id']), + 'target_type' => 'tag_collection' + )); + ?> + +   + '"> + OrgImg->getOrgImg(array('name' => $item['Organisation']['name'], 'id' => $item['Organisation']['id'], 'size' => 24)); + ?> + +   +   + + Html->link('', array('action' => 'edit', $item['TagCollection']['id']), array('class' => 'icon-edit', 'title' => 'Edit')); + echo $this->Form->postLink('', array('action' => 'delete', $item['TagCollection']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete "%s"?', $item['TagCollection']['name'])); + } + ?> + + diff --git a/app/View/Elements/ajaxAttributeTags.ctp b/app/View/Elements/ajaxAttributeTags.ctp index 3de5afebc..7dd278fa7 100644 --- a/app/View/Elements/ajaxAttributeTags.ctp +++ b/app/View/Elements/ajaxAttributeTags.ctp @@ -39,7 +39,7 @@ if ($full) { $addTagButton = sprintf( '', - sprintf("'%s/true', 'tags', 'selectTaxonomy'", h($attributeId)) + sprintf("'%s/attributes', 'tags', 'selectTaxonomy'", h($attributeId)) ); } echo $addTagButton; diff --git a/app/View/Elements/ajaxTagCollectionTags.ctp b/app/View/Elements/ajaxTagCollectionTags.ctp new file mode 100644 index 000000000..de3a1d21d --- /dev/null +++ b/app/View/Elements/ajaxTagCollectionTags.ctp @@ -0,0 +1,36 @@ +
+ $tag); + $tagClass = $full ? 'tagFirstHalf' : 'tag'; + ?> +
+
+ +
x
+ +
+ +
+ +', + sprintf("'%s/tag_collection', 'tags', 'selectTaxonomy'", h($tagCollection['TagCollection']['id'])) + ); + } + echo $addTagButton; + ?> +
+
diff --git a/app/View/Elements/generic_table.ctp b/app/View/Elements/generic_table.ctp new file mode 100644 index 000000000..c2bfd2fd9 --- /dev/null +++ b/app/View/Elements/generic_table.ctp @@ -0,0 +1,66 @@ +Paginator->options(array( + 'update' => '.span12', + 'evalScripts' => true, + 'before' => '$(".progress").show()', + 'complete' => '$(".progress").hide()', + )); + $title = sprintf('

%s

', Inflector::humanize($controller)); + if (!empty($description)) { + $description = sprintf('

%s

', Inflector::humanize($description)); + } else { + $description = ''; + } + $pagination = sprintf( + '', + $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span')), + $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span')), + $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span')) + ); + $table_headers = ''; + foreach ($headers as $header => $data) { + if (!empty($data['sort'])) { + if (!empty($data['alias'])) { + $header_data = $this->Paginator->sort($header, $data['alias']); + } else { + $header_data = $this->Paginator->sort($header); + } + } else { + if (!empty($data['alias'])) { + $header_data = $data['alias']; + } else { + $header_data = Inflector::humanize(h($header)); + } + } + $action = ($header === 'Actions') ? ' class="actions"' : ''; + $table_headers .= '' . $header_data . ''; + } + $table_contents = ''; + foreach ($items as $item) { + $table_contents .= $this->element($row_path, array( + 'item' => $item + )); + } + $table = sprintf( + '%s%s
', + $table_headers, + $table_contents + ); + $pagination_details = sprintf( + '

%s

', + $this->Paginator->counter( + array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + ) + ) + ); + echo sprintf( + '
%s%s%s%s%s%s
', + $controller, + $title, + $description, + $pagination, + $table, + $pagination_details, + $pagination + ); diff --git a/app/View/Elements/generic_table_row.ctp b/app/View/Elements/generic_table_row.ctp new file mode 100644 index 000000000..3c8d74b9c --- /dev/null +++ b/app/View/Elements/generic_table_row.ctp @@ -0,0 +1,59 @@ +Paginator->options(array( + 'update' => '.span12', + 'evalScripts' => true, + 'before' => '$(".progress").show()', + 'complete' => '$(".progress").hide()', + )); + $title = sprintf('

%s index

', Inflector::humanize($controller)); + if (!empty($description)) { + $description = sprintf('

%s

', Inflector::humanize($description)); + } else { + $description = ''; + } + $pagination = sprintf( + '', + $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span')), + $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span')), + $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span')) + ); + $table_headers = ''; + foreach ($headers as $header => $data) { + if (!empty($data['sort'])) { + if (!empty($data['alias'])) { + $header_data = $this->Paginator->sort($header, $data['alias']); + } else { + $header_data = $this->Paginator->sort($header); + } + } else { + $header_data = '' . h($header) . ''; + } + $table_headers .= $header_data; + } + $table_contents = $this->element($row_path, array( + 'items' => $items + )); + $table = sprintf( + '%s%s
', + $table_header, + $table_contents + ); + $pagination_details = sprintf( + '

%s

', + $this->Paginator->counter( + array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + ) + ) + ); + echo sprintf( + '
%s%s%s%s%s%s
', + $controller, + $title, + $description, + $pagination, + $table, + $pagination_details, + $pagination + ); + echo $this->element('side_menu', array('menuList' => 'whitelist', 'menuItem' => 'index')); diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 638521bba..eb8e7cdf1 100644 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -33,6 +33,7 @@
  • +
  • diff --git a/app/View/Elements/side_menu.ctp b/app/View/Elements/side_menu.ctp index 8d4f61dea..ea207f929 100644 --- a/app/View/Elements/side_menu.ctp +++ b/app/View/Elements/side_menu.ctp @@ -181,6 +181,25 @@ } break; + case 'tag-collections': + echo $this->element('/side_menu_link', array( + 'url' => '/tag_collections/index', + 'text' => __('List Tag Collections') + )); + if ($isAclTagEditor) { + echo $this->element('/side_menu_link', array( + 'url' => '/tag_collections/add', + 'text' => __('Add Tag Collection') + )); + if (($menuItem === 'edit')) { + echo $this->element('/side_menu_link', array( + 'url' => '/tag_collections/edit/' . $id, + 'text' => __('Add Tag Collection') + )); + } + } + break; + case 'event-collection': echo $this->element('/side_menu_link', array( 'element_id' => 'index', diff --git a/app/View/Events/ajax/ajaxGalaxies.ctp b/app/View/Events/ajax/ajaxGalaxies.ctp new file mode 100644 index 000000000..8a0fac72d --- /dev/null +++ b/app/View/Events/ajax/ajaxGalaxies.ctp @@ -0,0 +1,26 @@ +%s', __('Galaxies')); + $element = 'galaxyQuickView'; + } else if ($scope == 'attribute') { + $element = 'galaxyQuickViewMini'; + } + echo $this->element($element, array( + 'mayModify' => $mayModify, + 'isAclTagger' => $isAclTagger, + 'data' => $object['Galaxy'], + 'target_id' => $object['Event']['id'], + 'target_type' => $scope + )); +?> + diff --git a/app/View/Events/ajax/ajaxTags.ctp b/app/View/Events/ajax/ajaxTags.ctp index c7d26fdb7..6001997d0 100644 --- a/app/View/Events/ajax/ajaxTags.ctp +++ b/app/View/Events/ajax/ajaxTags.ctp @@ -1,4 +1,11 @@ element('ajaxTags', array('event' => $event, 'tags' => $tags, 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']))); + $mayModify = ( + ($isAclModify && $event['Event']['user_id'] == $me['id'] && $event['Event']['orgc_id'] == $me['org_id']) || + ($isAclModifyOrg && $event['Event']['orgc_id'] == $me['org_id']) + ); + echo $this->element('ajaxTags', array( + 'event' => $event, + 'tags' => $tags, + 'tagAccess' => ($isSiteAdmin || $mayModify) + )); ?> diff --git a/app/View/Events/json/index.ctp b/app/View/Events/json/index.ctp index 10fee8d9b..05e62a625 100644 --- a/app/View/Events/json/index.ctp +++ b/app/View/Events/json/index.ctp @@ -9,14 +9,9 @@ foreach ($events as $key => $event) { $events[$key]['GalaxyCluster'] = $event['GalaxyCluster']; } if (isset($event['EventTag'])) $events[$key]['EventTag'] = $event['EventTag']; - if (!empty($events[$key]['SharingGroup'])) $events[$key]['SharingGroup'] = $event['SharingGroup']; - - // cleanup the array from things we do not want to expose - unset($events[$key]['user_id']); - // hide the org field if we are not in showorg mode - if (!Configure::read('MISP.showorg')) { - unset($events[$key]['Org']); - unset($events[$key]['Orgc']); + if (!empty($event['SharingGroup'])) { + $events[$key]['SharingGroup'] = $event['SharingGroup']; } + unset($events[$key]['user_id']); } echo json_encode($events); diff --git a/app/View/Galaxies/ajax/cluster_choice.ctp b/app/View/Galaxies/ajax/cluster_choice.ctp index 309f9c18f..d59400fa8 100644 --- a/app/View/Galaxies/ajax/cluster_choice.ctp +++ b/app/View/Galaxies/ajax/cluster_choice.ctp @@ -43,7 +43,7 @@ }); $('.clusterSelectChoice').click(function() { - quickSubmitGalaxyForm($(this).data('target-type') + '/' + $(this).data('target-id'), $(this).data('cluster-id')); + quickSubmitGalaxyForm($(this).data('target-id'), $(this).data('cluster-id'), $(this).data('target-type')); }); $('#clusterFilterField').keyup(function() { var filterString = $("#clusterFilterField").val().toLowerCase(); diff --git a/app/View/TagCollections/add.ctp b/app/View/TagCollections/add.ctp new file mode 100644 index 000000000..c43e2d07e --- /dev/null +++ b/app/View/TagCollections/add.ctp @@ -0,0 +1,27 @@ +
    +Form->create('TagCollection'); ?> +
    + %s', __('Add Tag Collection')); + } else { + echo sprintf('%s', __('Edit Tag Collection')); + } + echo $this->Form->input('name', array('class' => 'span6')); + echo '
    '; + echo $this->Form->input('description', array('class' => 'span6')); + echo '
    '; + echo $this->Form->input('all_orgs', array( + 'type' => 'checkbox', + 'label' => __('Visible to all orgs') + )); + ?> +
    +Form->button(__('Submit'), array('class' => 'btn btn-primary')); +echo $this->Form->end(); +?> +
    +element('side_menu', array('menuList' => 'tag-collections', 'menuItem' => 'add')); +?> diff --git a/app/View/TagCollections/get_row.ctp b/app/View/TagCollections/get_row.ctp new file mode 100644 index 000000000..a52ff938e --- /dev/null +++ b/app/View/TagCollections/get_row.ctp @@ -0,0 +1,11 @@ +element('TagCollections/index_row'); +?> + + diff --git a/app/View/TagCollections/index.ctp b/app/View/TagCollections/index.ctp new file mode 100644 index 000000000..3fab6668e --- /dev/null +++ b/app/View/TagCollections/index.ctp @@ -0,0 +1,28 @@ +element('generic_table', array( + 'items' => $list, + 'controller' => 'tag_collections', + 'headers' => array( + 'id' => array('sort' => 1), + 'uuid' => array('sort' => 1), + 'name' => array('sort' => 1), + 'tags' => array(), + 'galaxies' => array(), + 'all_orgs' => array(), + 'org_id' => array('alias' => 'Organisation'), + 'user_id' => array('alias' => 'User'), + 'description' => array(), + 'Actions' => array() + ), + 'row_path' => 'TagCollections/index_row' + )); + + echo $this->element('side_menu', array('menuList' => 'tag-collections', 'menuItem' => 'index')); +?> + diff --git a/app/View/Tags/ajax/select_tag.ctp b/app/View/Tags/ajax/select_tag.ctp index 8e5f5f51b..11fb2e05a 100644 --- a/app/View/Tags/ajax/select_tag.ctp +++ b/app/View/Tags/ajax/select_tag.ctp @@ -1,11 +1,14 @@
    +
    Form->create('Attribute', array('url' => '/attributes/addTag/' . $object_id, 'style' => 'margin:0px;')); - } else { + } elseif ($scope === 'event') { echo $this->Form->create('Event', array('url' => '/events/addTag/' . $object_id, 'style' => 'margin:0px;')); + } elseif ($scope === 'tag_collection') { + echo $this->Form->create('TagCollection', array('url' => '/tag_collections/addTag/' . $object_id, 'style' => 'margin:0px;')); } echo $this->Form->input('attribute_ids', array('style' => 'display:none;', 'label' => false)); echo $this->Form->input('tag', array('value' => 0)); @@ -17,18 +20,32 @@
    - &$option): ?> - - - - - - + &$option): + $choice_id = $k; + if ($taxonomy_id === 'collections') { + $choice_id = 'collection_' . $choice_id; + } + ?> + + + ' . h($option) . ''; + ?>
    -
    +
    -Html->script('tag-selection-keyboard-navigation.js'); ?> \ No newline at end of file +Html->script('tag-selection-keyboard-navigation.js'); ?> diff --git a/app/View/Tags/ajax/taxonomy_choice.ctp b/app/View/Tags/ajax/taxonomy_choice.ctp index 746ce2ac5..bb2345e9b 100644 --- a/app/View/Tags/ajax/taxonomy_choice.ctp +++ b/app/View/Tags/ajax/taxonomy_choice.ctp @@ -5,22 +5,61 @@
    - - - - - - - - - - - - &$option): ?> - - - - + %s', + sprintf( + '', + h($object_id), + h($scope), + __('Favourite Tags') + ) + ); + } + if ($scope !== 'tag_collection') { + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('Tag Collections') + ) + ); + } + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('Custom Tags') + ) + ); + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('All Tags') + ) + ); + foreach ($options as $k => &$option) { + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($k), + h($scope), + __('Taxonomy Library'), + h($option) + ) + ); + } + ?>
    :
    %s
    %s
    %s
    %s
    %s: %s
    @@ -50,7 +89,7 @@ } } }, 500); - + } $('#filterField').keyup(onKeyUp); diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 88dc57805..239668a9d 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -567,11 +567,13 @@ function quickSubmitTagForm(event_id, tag_id) { }, success:function (data, textStatus) { loadEventTags(event_id); + loadGalaxies(event_id, 'event'); handleGenericAjaxResponse(data); }, error:function() { showMessage('fail', 'Could not add tag.'); loadEventTags(event_id); + loadGalaxies(event_id, 'event'); }, complete:function() { $("#popover_form").fadeOut(); @@ -599,12 +601,14 @@ function quickSubmitAttributeTagForm(attribute_id, tag_id) { updateIndex(0, 'event'); } else { loadAttributeTags(attribute_id); + loadGalaxies(attribute_id, 'attribute'); } handleGenericAjaxResponse(data); }, error:function() { showMessage('fail', 'Could not add tag.'); loadAttributeTags(attribute_id); + loadGalaxies(attribute_id, 'attribute'); }, complete:function() { $("#popover_form").fadeOut(); @@ -617,6 +621,46 @@ function quickSubmitAttributeTagForm(attribute_id, tag_id) { return false; } +function quickSubmitTagCollectionTagForm(tag_collection_id, tag_id) { + $('#TagCollectionTag').val(tag_id); + $.ajax({ + data: $('#TagCollectionSelectTagForm').closest("form").serialize(), + beforeSend: function (XMLHttpRequest) { + $(".loading").show(); + }, + success:function (data, textStatus) { + handleGenericAjaxResponse(data); + refreshTagCollectionRow(tag_collection_id); + }, + error:function() { + showMessage('fail', 'Could not add tag.'); + loadTagCollectionTags(tag_collection_id); + }, + complete:function() { + $("#popover_form").fadeOut(); + $("#gray_out").fadeOut(); + $(".loading").hide(); + }, + type:"post", + url:"/tag_collections/addTag/" + tag_collection_id + }); + return false; +} + +function refreshTagCollectionRow(tag_collection_id) { + $.ajax({ + type:"get", + url:"/tag_collections/getRow/" + tag_collection_id, + error:function() { + showMessage('fail', 'Could not fetch updates to the modified row.'); + }, + success: function (data, textStatus) { + $('[data-row-id="' + tag_collection_id + '"]').replaceWith(data); + } + }); + +} + function handleAjaxEditResponse(data, name, type, id, field, event) { responseArray = data; if (type == 'Attribute') { @@ -848,6 +892,32 @@ function loadEventTags(id) { }); } +function loadGalaxies(id, scope) { + $.ajax({ + dataType:"html", + cache: false, + success:function (data, textStatus) { + if (scope == 'event') { + $("#galaxies_div").html(data); + } else if (scope == 'attribute') { + $("#attribute_" + id + "_galaxy").html(data); + } + }, + url:"/galaxies/showGalaxies/" + id + "/" + scope, + }); +} + +function loadTagCollectionTags(id) { + $.ajax({ + dataType:"html", + cache: false, + success:function (data, textStatus) { + $(".tagCollectionTagContainer").html(data); + }, + url:"/tags/showEventTag/" + id, + }); +} + function removeEventTag(event, tag) { var answer = confirm("Are you sure you want to remove this tag from the event?"); if (answer) { @@ -905,6 +975,8 @@ function removeObjectTag(context, object, tag) { $("#gray_out").fadeOut(); if (context == 'Attribute') { loadAttributeTags(object); + } else if (context == 'tag_collection') { + refreshTagCollectionRow(object); } else { loadEventTags(object); } @@ -3223,9 +3295,29 @@ function addGalaxyListener(id) { getPopup(target_type + '/' + target_id, 'galaxies', 'selectGalaxyNamespace'); } -function quickSubmitGalaxyForm(event_id, cluster_id) { +function quickSubmitGalaxyForm(scope, cluster_id, event_id) { $('#GalaxyTargetId').val(cluster_id); - $('#GalaxySelectClusterForm').submit(); + $.ajax({ + data: $('#GalaxySelectClusterForm').closest("form").serialize(), + beforeSend: function (XMLHttpRequest) { + $(".loading").show(); + }, + success:function (data, textStatus) { + loadGalaxies(event_id, scope); + handleGenericAjaxResponse(data); + }, + error:function() { + showMessage('fail', 'Could not add cluster.'); + loadGalaxies(event_id, scope); + }, + complete:function() { + $("#popover_form").fadeOut(); + $("#gray_out").fadeOut(); + $(".loading").hide(); + }, + type:"post", + url: "/galaxies/attachCluster/" + event_id + "/" + scope + }); return false; }