From b733747a3397f585160e3ca0b950308ff28188a6 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Mon, 19 Dec 2016 23:13:14 -0500 Subject: [PATCH] CRM-19769 - Add select2 widget to display tags in color --- CRM/Activity/Form/Activity.php | 12 +++++----- CRM/Case/Form/Case.php | 10 ++++---- CRM/Case/Form/CaseView.php | 18 +++++++------- CRM/Core/BAO/Tag.php | 36 ++++++++++++++++++++++++++++ CRM/Core/Form.php | 10 ++++++++ CRM/Core/Form/Renderer.php | 24 +++++++++++++++++++ CRM/Tag/Form/Edit.php | 9 ++----- CRM/Utils/Array.php | 31 ++++++++++++++++++++++++ css/civicrm.css | 25 ++++++++++++++++--- css/contactSummary.css | 6 ----- js/Common.js | 13 ++++++++-- templates/CRM/Case/Form/CaseView.js | 2 +- templates/CRM/Case/Form/CaseView.tpl | 9 ++++++- 13 files changed, 166 insertions(+), 39 deletions(-) diff --git a/CRM/Activity/Form/Activity.php b/CRM/Activity/Form/Activity.php index 5d2d1fd8a4..1cacf38998 100644 --- a/CRM/Activity/Form/Activity.php +++ b/CRM/Activity/Form/Activity.php @@ -544,7 +544,7 @@ class CRM_Activity_Form_Activity extends CRM_Contact_Form_Task { $defaults['assignee_contact_id'] = CRM_Utils_Array::value('assignee_contact', $defaults); // set default tags if exists - $defaults['tag'] = CRM_Core_BAO_EntityTag::getTag($this->_activityId, 'civicrm_activity'); + $defaults['tag'] = implode(',', CRM_Core_BAO_EntityTag::getTag($this->_activityId, 'civicrm_activity')); } else { // if it's a new activity, we need to set default values for associated contact fields @@ -725,13 +725,10 @@ class CRM_Activity_Form_Activity extends CRM_Contact_Form_Task { $this->assign('customDataSubType', $this->_activityTypeId); $this->assign('entityID', $this->_activityId); - CRM_Core_BAO_Tag::getTags('civicrm_activity', $tags, NULL, - '  ', TRUE); + $tags = CRM_Core_BAO_Tag::getColorTags('civicrm_activity'); if (!empty($tags)) { - $this->add('select', 'tag', ts('Tags'), $tags, FALSE, - array('id' => 'tags', 'multiple' => 'multiple', 'class' => 'crm-select2 huge') - ); + $this->add('select2', 'tag', ts('Tags'), $tags, FALSE, array('class' => 'huge', 'placeholder' => ts('- select -'), 'multiple' => TRUE)); } // we need to hide activity tagset for special activities @@ -1011,6 +1008,9 @@ class CRM_Activity_Form_Activity extends CRM_Contact_Form_Task { // add tags if exists $tagParams = array(); if (!empty($params['tag'])) { + if (!is_array($params['tag'])) { + $params['tag'] = explode(',', $params['tag']); + } foreach ($params['tag'] as $tag) { $tagParams[$tag] = 1; } diff --git a/CRM/Case/Form/Case.php b/CRM/Case/Form/Case.php index 76c61baaf5..f236b23dab 100644 --- a/CRM/Case/Form/Case.php +++ b/CRM/Case/Form/Case.php @@ -244,12 +244,11 @@ class CRM_Case_Form_Case extends CRM_Core_Form { )), TRUE ); - CRM_Core_BAO_Tag::getTags('civicrm_case', $tags, NULL, - '  ', TRUE); + $tags = CRM_Core_BAO_Tag::getColorTags('civicrm_case'); if (!empty($tags)) { - $this->add('select', 'tag', ts('Select Tags'), $tags, FALSE, - array('id' => 'tags', 'multiple' => 'multiple', 'class' => 'crm-select2') + $this->add('select2', 'tag', ts('Tags'), $tags, FALSE, + array('class' => 'huge', 'multiple' => 'multiple') ); } @@ -371,6 +370,9 @@ class CRM_Case_Form_Case extends CRM_Core_Form { $tagParams = array(); if (!empty($params['tag'])) { $tagParams = array(); + if (!is_array($params['tag'])) { + $params['tag'] = explode(',', $params['tag']); + } foreach ($params['tag'] as $tag) { $tagParams[$tag] = 1; } diff --git a/CRM/Case/Form/CaseView.php b/CRM/Case/Form/CaseView.php index 2eabfc6e54..ad0de50ff5 100644 --- a/CRM/Case/Form/CaseView.php +++ b/CRM/Case/Form/CaseView.php @@ -343,28 +343,28 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form { $this->assign('hookCaseSummary', $hookCaseSummary); } - CRM_Core_BAO_Tag::getTags('civicrm_case', $allTags, NULL, - '  ', TRUE); + $allTags = CRM_Core_BAO_Tag::getColorTags('civicrm_case'); if (!empty($allTags)) { - $this->add('select', 'case_tag', ts('Tags'), $allTags, FALSE, - array('id' => 'tags', 'multiple' => 'multiple', 'class' => 'crm-select2') + $this->add('select2', 'case_tag', ts('Tags'), $allTags, FALSE, + array('id' => 'tags', 'multiple' => 'multiple') ); $tags = CRM_Core_BAO_EntityTag::getTag($this->_caseID, 'civicrm_case'); - $this->setDefaults(array('case_tag' => $tags)); - foreach ($tags as $tid) { - if (isset($allTags[$tid])) { - $tags[$tid] = $allTags[$tid]; + $tagInfo = CRM_Utils_Array::findInTree($tid, $allTags); + if ($tagInfo) { + $tags[$tid] = $tagInfo; } else { unset($tags[$tid]); } } - $this->assign('tags', implode(', ', array_filter($tags))); + $this->setDefaults(array('case_tag' => implode(',', array_keys($tags)))); + + $this->assign('tags', $tags); $this->assign('showTags', TRUE); } else { diff --git a/CRM/Core/BAO/Tag.php b/CRM/Core/BAO/Tag.php index 6bc321a8cf..7b158f8e34 100644 --- a/CRM/Core/BAO/Tag.php +++ b/CRM/Core/BAO/Tag.php @@ -314,6 +314,42 @@ class CRM_Core_BAO_Tag extends CRM_Core_DAO_Tag { return $tags; } + /** + * @param string $usedFor + * @param bool $allowSelectingNonSelectable + * @param null $exclude + * @return array + * @throws \CiviCRM_API3_Exception + */ + public static function getColorTags($usedFor = NULL, $allowSelectingNonSelectable = FALSE, $exclude = NULL) { + $params = array( + 'options' => array('limit' => 0), + 'is_tagset' => 0, + 'return' => array('name', 'description', 'parent_id', 'color', 'is_selectable', 'used_for'), + ); + if ($usedFor) { + $params['used_for'] = array('LIKE' => "%$usedFor%"); + } + if ($exclude) { + $params['id'] = array('!=' => $exclude); + } + $allTags = array(); + foreach (CRM_Utils_Array::value('values', civicrm_api3('Tag', 'get', $params)) as $id => $tag) { + $allTags[$id] = array( + 'text' => $tag['name'], + 'id' => $id, + 'description' => CRM_Utils_Array::value('description', $tag), + 'parent_id' => CRM_Utils_Array::value('parent_id', $tag), + 'used_for' => CRM_Utils_Array::value('used_for', $tag), + 'color' => CRM_Utils_Array::value('color', $tag), + ); + if (!$allowSelectingNonSelectable && empty($tag['is_selectable'])) { + $allTags[$id]['disabled'] = TRUE; + } + } + return CRM_Utils_Array::buildTree($allTags); + } + /** * Delete the tag. * diff --git a/CRM/Core/Form.php b/CRM/Core/Form.php index eb230d457d..e01cbb70ed 100644 --- a/CRM/Core/Form.php +++ b/CRM/Core/Form.php @@ -356,6 +356,16 @@ class CRM_Core_Form extends HTML_QuickForm_Page { } $type = $type == 'wysiwyg' ? 'textarea' : 'text'; } + // Like select but accepts rich array data (with nesting, colors, icons, etc) as option list. + if ($inputType == 'select2') { + $type = 'text'; + $options = $attributes; + $attributes = $attributes = ($extra ? $extra : array()) + array('class' => ''); + $attributes['class'] = ltrim($attributes['class'] . " crm-select2 crm-form-select2"); + $attributes['data-select-params'] = json_encode(array('data' => $options, 'multiple' => !empty($attributes['multiple']))); + unset($attributes['multiple']); + $extra = NULL; + } // @see http://wiki.civicrm.org/confluence/display/CRMDOC/crmDatepicker if ($type == 'datepicker') { $attributes = ($attributes ? $attributes : array()); diff --git a/CRM/Core/Form/Renderer.php b/CRM/Core/Form/Renderer.php index 6f235382db..3eeb16091d 100644 --- a/CRM/Core/Form/Renderer.php +++ b/CRM/Core/Form/Renderer.php @@ -120,6 +120,9 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty { if ($element->getAttribute('data-api-entity') && $element->getAttribute('data-entity-value')) { $this->renderFrozenEntityRef($el, $element); } + elseif ($element->getAttribute('type') == 'text' && $element->getAttribute('data-select-params')) { + $this->renderFrozenSelect2($el, $element); + } elseif ($element->getAttribute('type') == 'text' && $element->getAttribute('formatType')) { list($date, $time) = CRM_Utils_Date::setDateDefaults($element->getValue(), $element->getAttribute('formatType'), $element->getAttribute('format'), $element->getAttribute('timeformat')); $date .= ($element->getAttribute('timeformat')) ? " $time" : ''; @@ -253,6 +256,27 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty { $field->setValue(implode(',', $val)); } + /** + * Render select2 as text. + * + * @param array $el + * @param HTML_QuickForm_element $field + */ + public function renderFrozenSelect2(&$el, $field) { + $params = json_decode($field->getAttribute('data-select-params'), TRUE); + $val = $field->getValue(); + if ($val && !empty($params['data'])) { + $display = array(); + foreach (explode(',', $val) as $item) { + $match = CRM_Utils_Array::findInTree($item, $params['data']); + if (isset($match['text']) && strlen($match['text'])) { + $display[] = $match['text']; + } + } + $el['html'] = implode('; ', $display) . ''; + } + } + /** * Render entity references as text. * If user has permission, format as link (for now limited to contacts). diff --git a/CRM/Tag/Form/Edit.php b/CRM/Tag/Form/Edit.php index 8b60e348fd..8d6d94ff67 100644 --- a/CRM/Tag/Form/Edit.php +++ b/CRM/Tag/Form/Edit.php @@ -67,14 +67,9 @@ class CRM_Tag_Form_Edit extends CRM_Admin_Form { $this->_isTagSet = TRUE; } - $allTag = array('' => ts('- select -')) + CRM_Core_BAO_Tag::getTagsNotInTagset(); - - if ($this->_id) { - unset($allTag[$this->_id]); - } - if (!$this->_isTagSet) { - $this->add('select', 'parent_id', ts('Parent Tag'), $allTag, FALSE, array('class' => 'crm-select2')); + $colorTags = CRM_Core_BAO_Tag::getColorTags(NULL, TRUE, $this->_id); + $this->add('select2', 'parent_id', ts('Parent Tag'), $colorTags, FALSE, array('placeholder' => ts('- select -'))); // Tagsets are not selectable by definition so only include the selectable field if NOT a tagset. $selectable = $this->add('checkbox', 'is_selectable', ts('Selectable?')); diff --git a/CRM/Utils/Array.php b/CRM/Utils/Array.php index d82250cc74..c2862e3449 100644 --- a/CRM/Utils/Array.php +++ b/CRM/Utils/Array.php @@ -1098,4 +1098,35 @@ class CRM_Utils_Array { return $array; } + public static function buildTree($elements, $parentId = NULL) { + $branch = array(); + + foreach ($elements as $element) { + if ($element['parent_id'] == $parentId) { + $children = self::buildTree($elements, $element['id']); + if ($children) { + $element['children'] = $children; + } + $branch[] = $element; + } + } + + return $branch; + } + + public static function findInTree($search, $tree, $field = 'id') { + foreach ($tree as $item) { + if ($item[$field] == $search) { + return $item; + } + if (!empty($item['children'])) { + $found = self::findInTree($search, $item['children']); + if ($found) { + return $found; + } + } + } + return NULL; + } + } diff --git a/css/civicrm.css b/css/civicrm.css index 90d6ffb381..716350ba6c 100644 --- a/css/civicrm.css +++ b/css/civicrm.css @@ -3054,14 +3054,14 @@ div.m ul#civicrm-menu, } .crm-container .select2-results li, .crm-container .select2-results .crm-select2-row, -.crm-container .select2-results .crm-select2-row .crm-select2-row-description p { +.crm-container .select2-results .crm-select2-row-description p { padding: 0; margin: 0; } .crm-container .select2-results .crm-select2-row .crm-select2-row-label { font-size: 1.1em; } -.crm-container .select2-results .crm-select2-row .crm-select2-row-description p { +.crm-container .select2-results .crm-select2-row-description p { font-size: 0.8em; line-height: 1.5em; color: #696969; @@ -3069,10 +3069,14 @@ div.m ul#civicrm-menu, white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + font-weight: normal; } -.crm-container .select2-results .select2-highlighted .crm-select2-row .crm-select2-row-description p { +.crm-container .select2-results .select2-highlighted > .select2-result-label .crm-select2-row-description p { color: #f0f0f0; } +.select2-container .crm-select2-row-description { + display: none; +} .crm-container .select2-results .crm-select2-icon { width: 20px; height: 100%; @@ -3104,6 +3108,14 @@ div.m ul#civicrm-menu, box-sizing: border-box; } +span.crm-select-item-color { + display: inline-block; + width: .8em; + height: .7em; + border-radius: 2px; + border: 1px solid grey; +} + /* jQuery UI styles */ .crm-container .ui-progressbar-value { background-image: url("../packages/jquery/css/images/pbar-ani.gif"); @@ -3811,3 +3823,10 @@ span.crm-status-icon { .crm-container .crm-grip { cursor: move; } + +.crm-tag-item { + display: inline-block; + padding: 1px 5px; + border-radius: 3px; + border: 1px solid grey; +} \ No newline at end of file diff --git a/css/contactSummary.css b/css/contactSummary.css index a62b8827cc..5c4f536dc1 100644 --- a/css/contactSummary.css +++ b/css/contactSummary.css @@ -352,9 +352,3 @@ div#crm-contact-thumbnail { color: #000; } -.crm-tag-item { - display: inline-block; - padding: 1px 5px; - border-radius: 3px; - border: 1px solid grey; -} diff --git a/js/Common.js b/js/Common.js index 38a2a20939..c5685fdd16 100644 --- a/js/Common.js +++ b/js/Common.js @@ -373,8 +373,17 @@ if (!CRM.vars) CRM.vars = {}; }; function formatCrmSelect2(row) { - var icon = $(row.element).data('icon'); - return (icon ? ' ' : '') + _.escape(row.text); + var icon = row.icon || $(row.element).data('icon'), + color = row.color || $(row.element).data('color'), + description = row.description || $(row.element).data('description'), + ret = ''; + if (icon) { + ret += ' '; + } + if (color) { + ret += ' '; + } + return ret + _.escape(row.text) + (description ? '

' + _.escape(description) + '

' : ''); } /** diff --git a/templates/CRM/Case/Form/CaseView.js b/templates/CRM/Case/Form/CaseView.js index b09f18bb61..43ae02172a 100644 --- a/templates/CRM/Case/Form/CaseView.js +++ b/templates/CRM/Case/Form/CaseView.js @@ -33,7 +33,7 @@ var miniForms = { '#manageTagsDialog': { post: function(data) { - var tagsChecked = $("#tags", this) ? $("#tags", this).select2('val').join(',') : '', + var tagsChecked = $("#tags", this) ? $("#tags", this).val() : '', tagList = {}, url = CRM.url('civicrm/case/ajax/processtags'); $("input[name^=case_taglist]", this).each(function() { diff --git a/templates/CRM/Case/Form/CaseView.tpl b/templates/CRM/Case/Form/CaseView.tpl index 3de01fc521..61722a8d56 100644 --- a/templates/CRM/Case/Form/CaseView.tpl +++ b/templates/CRM/Case/Form/CaseView.tpl @@ -276,7 +276,14 @@
{if $tags} -

  {$tags}

+

+    + {foreach from=$tags item='tag'} + + {$tag.text} + + {/foreach} +

{/if} {foreach from=$tagSetTags item=displayTagset} -- 2.25.1