CRM-19769 - Add select2 widget to display tags in color
authorColeman Watts <coleman@civicrm.org>
Tue, 20 Dec 2016 04:13:14 +0000 (23:13 -0500)
committerColeman Watts <coleman@civicrm.org>
Fri, 23 Dec 2016 22:09:00 +0000 (17:09 -0500)
13 files changed:
CRM/Activity/Form/Activity.php
CRM/Case/Form/Case.php
CRM/Case/Form/CaseView.php
CRM/Core/BAO/Tag.php
CRM/Core/Form.php
CRM/Core/Form/Renderer.php
CRM/Tag/Form/Edit.php
CRM/Utils/Array.php
css/civicrm.css
css/contactSummary.css
js/Common.js
templates/CRM/Case/Form/CaseView.js
templates/CRM/Case/Form/CaseView.tpl

index 5d2d1fd8a4de165d6161f91e6742f25f61c9ce21..1cacf389983b6e71b9d1d622cd61468ea97db7e6 100644 (file)
@@ -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,
-      '&nbsp;&nbsp;', 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;
       }
index 76c61baaf54b87f48c0ee5313170fb7d7825f806..f236b23dab0df199dbafb1fe69dda338812ccbb6 100644 (file)
@@ -244,12 +244,11 @@ class CRM_Case_Form_Case extends CRM_Core_Form {
       )), TRUE
     );
 
-    CRM_Core_BAO_Tag::getTags('civicrm_case', $tags, NULL,
-      '&nbsp;&nbsp;', 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;
       }
index 2eabfc6e5499ffd386bc840adc6c9372a0851fbf..ad0de50ff575a5de457dfb286392b2af84a22f72 100644 (file)
@@ -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,
-      '&nbsp;&nbsp;', 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 {
index 6bc321a8cfcef3bb7e94f0d0c3179d0fa30c3d61..7b158f8e34a2d235cd2759d17094df07f3e28966 100644 (file)
@@ -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.
    *
index eb230d457d4065520ebaf28f3d7425255e7c70df..e01cbb70edb3d71ed39ea3c9fd297667d36ae090 100644 (file)
@@ -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());
index 6f235382dbf9cf1ea3091d215189f88c24893603..3eeb16091dffbfa974d94348c2b2ab54e43f4469 100644 (file)
@@ -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) . '<input type="hidden" value="' . $field->getValue() . '" name="' . $field->getAttribute('name') . '">';
+    }
+  }
+
   /**
    * Render entity references as text.
    * If user has permission, format as link (for now limited to contacts).
index 8b60e348fd3437d70c456bddc5ebfb3bfad1d675..8d6d94ff678f0811108b831e3a9cd32314563fb0 100644 (file)
@@ -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?'));
index d82250cc7460e65a837befe2296fd7560f6f48d0..c2862e3449a7ec4f56da040db5ac08ec4d040e05 100644 (file)
@@ -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;
+  }
+
 }
index 90d6ffb3812de05b61f2bdd47973fc141050bd0d..716350ba6c0a0eed2b396d45391a207fc9f09c33 100644 (file)
@@ -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
index a62b8827ccbbbbcce3bbe47ce5c0696e0a510b15..5c4f536dc17e934f3f4d5cabc4f8e08ffee3f532 100644 (file)
@@ -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;
-}
index 38a2a20939d58cb978086703937bc3da6a982b77..c5685fdd16fce50390fbd288af3c35eeedd8f76e 100644 (file)
@@ -373,8 +373,17 @@ if (!CRM.vars) CRM.vars = {};
   };
 
   function formatCrmSelect2(row) {
-    var icon = $(row.element).data('icon');
-    return (icon ? '<i class="crm-i ' + icon + '"></i> ' : '') + _.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 += '<i class="crm-i ' + icon + '"></i> ';
+    }
+    if (color) {
+      ret += '<span class="crm-select-item-color" style="background-color: ' + color + '"></span> ';
+    }
+    return ret + _.escape(row.text) + (description ? '<div class="crm-select2-row-description"><p>' + _.escape(description) + '</p></div>' : '');
   }
 
   /**
index b09f18bb619ce96685eb28488e52a7f7270caabc..43ae02172aefc243687a19711ea62c0bb780944d 100644 (file)
@@ -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() {
index 3de01fc521c527be9a9da1424ba4aca5716c2efd..61722a8d561f6cab3ba3b9575b7cf07b5423c71b 100644 (file)
  </div><!-- /.crm-accordion-header -->
  <div class="crm-accordion-body">
   {if $tags}
-    <p class="crm-block crm-content-block crm-case-caseview-display-tags">&nbsp;&nbsp;{$tags}</p>
+    <p class="crm-block crm-content-block crm-case-caseview-display-tags">
+      &nbsp;&nbsp;
+      {foreach from=$tags item='tag'}
+        <span class="crm-tag-item" {if !empty($tag.color)}style="background-color: {$tag.color}; color: {$tag.color|colorContrast};"{/if}>
+          {$tag.text}
+        </span>
+      {/foreach}
+    </p>
   {/if}
 
    {foreach from=$tagSetTags item=displayTagset}