From bbdbd3b1847f4b2e191d39064dabe0e2637bc775 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 9 Jan 2019 14:19:14 +0100 Subject: [PATCH] chg: [tagging] WIP - bulk tagging via generic picker on tag level --- app/Controller/AttributesController.php | 30 ++++++-- app/Controller/EventsController.php | 15 ++-- app/Controller/TagsController.php | 52 +++++++++----- app/View/Elements/generic_picker.ctp | 59 ++++++++++----- app/View/Elements/generic_pre_picker.ctp | 22 ++++-- app/View/Tags/ajax/select_tag.ctp | 92 ++++-------------------- app/View/Tags/ajax/taxonomy_choice.ctp | 1 + app/webroot/css/main.css | 6 ++ app/webroot/js/misp.js | 6 +- 9 files changed, 146 insertions(+), 137 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index ffe2cf674..87ed5f347 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -2850,11 +2850,33 @@ class AttributesController extends AppController $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')); + // try to parse json array + $tag_ids = json_decode($tag_id); + if ($tag_ids !== null) { // can decode json + $tag_id_list = array(); + foreach ($tag_ids as $tag_id) { + 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_id_list[] = $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)) { diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index be0435c5c..7c1f5a360 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3293,16 +3293,17 @@ class EventsController extends AppController } public function genericPicker() { - $this->set('options', array( - 'require_choice' => true, - 'pre_choices' => array( - 'Tag Collections' => "'4/collections/attribute', 'tags', 'selectTag'", - 'All Tags' => "4/all/attribute, 'tags', 'selectTag'", - ) - )); + $this->set('options', array()); $this->set('items', array('item1', 'item2', 'item3')); $this->render('/Elements/generic_picker'); } + public function genericPrePicker() { + $this->set('choices', array( + 'Tag Collections' => "/tags/selectTag/4/collections/attribute", + 'All Tags' => "/tags/selectTag/4/all/attribute" + )); + $this->render('/Elements/generic_pre_picker'); + } public function addTag($id = false, $tag_id = false) { diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index 182fdb17d..cee4e536c 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -638,13 +638,6 @@ class TagsController extends AppController $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); @@ -693,19 +686,40 @@ class TagsController extends AppController 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('scope', $scope); + $this->set('object_id', $id); + $items = array(); + foreach ($options as $k => $option) { + $choice_id = $k; + if ($taxonomy_id === 'collections') { + $choice_id = 'collection_' . $choice_id; + } + + $onClickForm = 'quickSubmitTagForm'; + if ($scope === 'attribute') { + $onClickForm = 'quickSubmitAttributeTagForm'; + } + if ($scope === 'tag_collection') { + $onClickForm = 'quickSubmitTagCollectionTagForm'; + } + + $items[h($option)] = array( + 'value' => h($choice_id), + 'additionalData' => array( + 'id' => h($id) + ) + ); + } + $this->set('items', $items); + $this->set('options', array( // set chosen (select picker) options + 'select_options' => array( + 'multiple' => true, + ), + 'functionName' => $onClickForm, + )); + $this->render('ajax/select_tag'); } public function tagStatistics($percentage = false, $keysort = false) diff --git a/app/View/Elements/generic_picker.ctp b/app/View/Elements/generic_picker.ctp index 47934ecb7..6b07d110d 100644 --- a/app/View/Elements/generic_picker.ctp +++ b/app/View/Elements/generic_picker.ctp @@ -1,24 +1,25 @@ array( // 'multiple' => '', // set to add possibility to pick multiple options in the select //'placeholder' => '' // set to replace the default placeholder text ), 'chosen_options' => array( + 'width' => '400px', //'no_results_text' => '', // set to replace the default no result text after filtering //'max_selected_options' => 'Infinity' // set to replace the max selected options 'disable_search_threshold' => 10, 'allow_single_deselect' => true, ), - 'require_choice' => false, - 'pre_choices' => array() + 'functionName' => '', // function to be called on submit ); - $scope = isset($scope) ? $scope : ''; // merge options with defaults $defaults = array_replace_recursive($defaults_options, $options); @@ -34,13 +35,26 @@ function add_option($name, $param) { $option_html = '
- - - - -
- +
+ +
+ +
; $(".generic_picker select").chosen(chosen_options); }) + + function submitFunction(clicked, callback) { + var $clicked = $(clicked); + var $select = $clicked.parent().find('select'); + var selected = $select.val(); + var additionalData = $select.find(":selected").data('additionaldata'); + callback(selected, additionalData); + } diff --git a/app/View/Elements/generic_pre_picker.ctp b/app/View/Elements/generic_pre_picker.ctp index c776b302e..98546ecb9 100644 --- a/app/View/Elements/generic_pre_picker.ctp +++ b/app/View/Elements/generic_pre_picker.ctp @@ -5,7 +5,7 @@ */ /** Config **/ -$select_threshold = 3; +$select_threshold = 6; /** Global **/ $use_select = count($choices) > $select_threshold; @@ -26,13 +26,14 @@ $use_select = count($choices) > $select_threshold; dataType:"html", async: true, cache: false, + beforeSend: function() { + var loadingHtml = '
'; + var $arrow = $clicked.closest('div.popover').find('div.arrow'); + syncPopoverArrow($arrow, $wrapper, loadingHtml) + }, success:function (data, textStatus) { var $arrow = $clicked.closest('div.popover').find('div.arrow'); - var ar_pos = $arrow.position(); - $wrapper.html(data); - // redraw popover - $arrow.css('top', ar_pos.top + 'px'); - $arrow.css('left', ar_pos.left + 'px'); + syncPopoverArrow($arrow, $wrapper, data) }, error:function() { $wrapper.html('
Something went wrong - the queried function returned an exception. Contact your administrator for further details (the exception has been logged).
'); @@ -41,6 +42,15 @@ $use_select = count($choices) > $select_threshold; }); } + // Used to keep the popover arrow at the correct place regardless of the popover content + function syncPopoverArrow($arrow, $wrapper, content) { + var ar_pos = $arrow.position(); + $wrapper.html(content); + // redraw popover + $arrow.css('top', ar_pos.top + 'px'); + $arrow.css('left', ar_pos.left + 'px'); + } + function setupChosen(id) { var $elem = $('#'+id); diff --git a/app/View/Tags/ajax/select_tag.ctp b/app/View/Tags/ajax/select_tag.ctp index 5de53038b..898962e57 100644 --- a/app/View/Tags/ajax/select_tag.ctp +++ b/app/View/Tags/ajax/select_tag.ctp @@ -1,84 +1,16 @@ -
- - -
- Form->create('Attribute', array('url' => '/attributes/addTag/' . $object_id, 'style' => 'margin:0px;')); - } 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)); - echo $this->Form->end(); - ?> -
- -
- +
&$option): - $choice_id = $k; - if ($taxonomy_id === 'collections') { - $choice_id = 'collection_' . $choice_id; - } + if ($scope === 'attribute') { + echo $this->Form->create('Attribute', array('url' => '/attributes/addTag/' . $object_id, 'style' => 'margin:0px;')); + } 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)); + echo $this->Form->end(); ?> - -
- ' . h($option) . ''; - ?> - - -
-
-
-
- -Html->script('tag-selection-keyboard-navigation.js'); ?> +element('generic_picker'); ?> diff --git a/app/View/Tags/ajax/taxonomy_choice.ctp b/app/View/Tags/ajax/taxonomy_choice.ctp index bb2345e9b..3b03f9fc7 100644 --- a/app/View/Tags/ajax/taxonomy_choice.ctp +++ b/app/View/Tags/ajax/taxonomy_choice.ctp @@ -1,3 +1,4 @@ +