diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index 247fff530176..48893a169fb0 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -2498,7 +2498,7 @@ public static function getContactActivitySelector(&$params) { $activity = array(); $activity['DT_RowId'] = $activityId; // Add class to this row if overdue. - $activity['DT_RowClass'] = 'crm-entity'; + $activity['DT_RowClass'] = "crm-entity status-id-{$values['status_id']}"; if (CRM_Utils_Date::overdue(CRM_Utils_Array::value('activity_date_time', $values)) && CRM_Utils_Array::value('status_id', $values) == 1 ) { diff --git a/CRM/Activity/Form/Activity.php b/CRM/Activity/Form/Activity.php index 5d2d1fd8a4de..1cacf389983b 100644 --- a/CRM/Activity/Form/Activity.php +++ b/CRM/Activity/Form/Activity.php @@ -544,7 +544,7 @@ public function setDefaultValues() { $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 @@ public function buildQuickForm() { $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 @@ protected function processActivity(&$params) { // 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/Admin/Form/Options.php b/CRM/Admin/Form/Options.php index 6c8dad5c5c97..c1f040535676 100644 --- a/CRM/Admin/Form/Options.php +++ b/CRM/Admin/Form/Options.php @@ -134,6 +134,9 @@ public function setDefaultValues() { if ($this->_gName == 'payment_instrument' && $this->_id) { $defaults['financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($this->_id, 'civicrm_option_value', 'financial_account_id'); } + if (empty($this->_id) || !CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'color')) { + $defaults['color'] = '#ffffff'; + } return $defaults; } @@ -174,6 +177,10 @@ public function buildQuickForm() { $this->add('text', 'icon', ts('Icon'), array('class' => 'crm-icon-picker', 'title' => ts('Choose Icon'), 'allowClear' => TRUE)); } + if ($this->_gName == 'activity_status') { + $this->add('color', 'color', ts('Color')); + } + if (!in_array($this->_gName, array( 'email_greeting', 'postal_greeting', @@ -428,7 +435,7 @@ public function postProcess() { } } else { - $params = $ids = array(); + $ids = array(); $params = $this->exportValues(); // allow multiple defaults within group. @@ -458,6 +465,10 @@ public function postProcess() { } } + if (isset($params['color']) && strtolower($params['color']) == '#ffffff') { + $params['color'] = 'null'; + } + $groupParams = array('name' => ($this->_gName)); $optionValue = CRM_Core_OptionValue::addOptionValue($params, $groupParams, $this->_action, $this->_id); diff --git a/CRM/Admin/Page/AJAX.php b/CRM/Admin/Page/AJAX.php index 139321abe4f5..4374d62403c4 100644 --- a/CRM/Admin/Page/AJAX.php +++ b/CRM/Admin/Page/AJAX.php @@ -266,7 +266,7 @@ public static function mergeTagList() { // query to list mergable tags $query = " -SELECT t1.name, t1.id, t1.used_for, t2.name as parent +SELECT t1.name, t1.id, t1.used_for, t1.color, t2.name as parent FROM civicrm_tag t1 LEFT JOIN civicrm_tag t2 ON t1.parent_id = t2.id WHERE t1.id <> {$fromId} AND @@ -280,6 +280,7 @@ public static function mergeTagList() { $row = array( 'id' => $dao->id, 'text' => ($dao->parent ? "{$dao->parent} :: " : '') . $dao->name, + 'color' => isset($dao->color) ? $dao->color : NULL, ); // Add warning about used_for types if (!empty($dao->used_for)) { diff --git a/CRM/Case/BAO/Case.php b/CRM/Case/BAO/Case.php index 02db237d8bc4..d887a323ba6f 100644 --- a/CRM/Case/BAO/Case.php +++ b/CRM/Case/BAO/Case.php @@ -1091,7 +1091,7 @@ public static function getCaseActivity($caseID, &$params, $contactID, $context = $caseActivity['DT_RowId'] = $caseActivityId; //Add classes to the row, via DataTables syntax - $caseActivity['DT_RowClass'] = "crm-entity"; + $caseActivity['DT_RowClass'] = "crm-entity status-id-$dao->status"; if (CRM_Utils_Array::crmInArray($dao->status, $compStatusValues)) { $caseActivity['DT_RowClass'] .= " status-completed"; diff --git a/CRM/Case/Form/Case.php b/CRM/Case/Form/Case.php index 76c61baaf54b..f236b23dab0d 100644 --- a/CRM/Case/Form/Case.php +++ b/CRM/Case/Form/Case.php @@ -244,12 +244,11 @@ public function buildQuickForm() { )), 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 @@ public function postProcess() { $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 2eabfc6e5499..ad0de50ff575 100644 --- a/CRM/Case/Form/CaseView.php +++ b/CRM/Case/Form/CaseView.php @@ -343,28 +343,28 @@ public function buildQuickForm() { $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/Contact/Page/View/Summary.php b/CRM/Contact/Page/View/Summary.php index 02fe9db092c2..ae7c9be46458 100644 --- a/CRM/Contact/Page/View/Summary.php +++ b/CRM/Contact/Page/View/Summary.php @@ -220,7 +220,8 @@ public function view() { $contactTags = CRM_Core_BAO_EntityTag::getContactTags($this->_contactId); if (!empty($contactTags)) { - $defaults['contactTag'] = implode(', ', $contactTags); + $defaults['contactTag'] = $contactTags; + $defaults['allTags'] = CRM_Core_BAO_Tag::getTagsUsedFor('civicrm_contact', FALSE); } $defaults['privacy_values'] = CRM_Core_SelectValues::privacy(); diff --git a/CRM/Core/BAO/Tag.php b/CRM/Core/BAO/Tag.php index e22af2bfce8c..7b158f8e34a2 100644 --- a/CRM/Core/BAO/Tag.php +++ b/CRM/Core/BAO/Tag.php @@ -172,6 +172,7 @@ public static function getTagsUsedFor( $tags[$tag->id]['parent_id'] = $tag->parent_id; $tags[$tag->id]['is_tagset'] = $tag->is_tagset; $tags[$tag->id]['used_for'] = $tag->used_for; + $tags[$tag->id]['color'] = !empty($tag->color) ? $tag->color : NULL; } } $tag->free(); @@ -313,6 +314,42 @@ public static function getTags( 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. * @@ -345,7 +382,7 @@ public static function del($id) { } /** - * Takes an associative array and creates a contact object. + * Takes an associative array and creates a tag object. * * The function extract all the params it needs to initialize the create a * contact object. the params array could contain additional unused name/value diff --git a/CRM/Core/DAO/OptionValue.php b/CRM/Core/DAO/OptionValue.php index 00fdf022bd24..8a27e6203a17 100644 --- a/CRM/Core/DAO/OptionValue.php +++ b/CRM/Core/DAO/OptionValue.php @@ -30,7 +30,7 @@ * * Generated from xml/schema/CRM/Core/OptionValue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7581126bad606520292d883da2f620ca) + * (GenCodeChecksum:fdf2f62ba69b54da0567362493827448) */ require_once 'CRM/Core/DAO.php'; require_once 'CRM/Utils/Type.php'; @@ -149,6 +149,12 @@ class CRM_Core_DAO_OptionValue extends CRM_Core_DAO { * @var string */ public $icon; + /** + * Hex color value e.g. #ffffff + * + * @var string + */ + public $color; /** * class constructor * @@ -335,6 +341,15 @@ static function &fields() { 'size' => CRM_Utils_Type::HUGE, 'default' => 'NULL', ) , + 'color' => array( + 'name' => 'color', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Color') , + 'description' => 'Hex color value e.g. #ffffff', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'default' => 'NULL', + ) , ); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } diff --git a/CRM/Core/DAO/Tag.php b/CRM/Core/DAO/Tag.php index 75835d6dabf5..f412f0cbcf26 100644 --- a/CRM/Core/DAO/Tag.php +++ b/CRM/Core/DAO/Tag.php @@ -30,7 +30,7 @@ * * Generated from xml/schema/CRM/Core/Tag.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e62b7ed2ffb1714200328dd2b08cdc9c) + * (GenCodeChecksum:1e6ad1d5a7c05b1a7d300d2a7722ba4d) */ require_once 'CRM/Core/DAO.php'; require_once 'CRM/Utils/Type.php'; @@ -99,6 +99,12 @@ class CRM_Core_DAO_Tag extends CRM_Core_DAO { * @var int unsigned */ public $created_id; + /** + * Hex color value e.g. #ffffff + * + * @var string + */ + public $color; /** * Date and time that tag was created. * @@ -208,6 +214,15 @@ static function &fields() { 'description' => 'FK to civicrm_contact, who created this tag', 'FKClassName' => 'CRM_Contact_DAO_Contact', ) , + 'color' => array( + 'name' => 'color', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Color') , + 'description' => 'Hex color value e.g. #ffffff', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'default' => 'NULL', + ) , 'created_date' => array( 'name' => 'created_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, diff --git a/CRM/Core/Form.php b/CRM/Core/Form.php index c694a8525234..e01cbb70edb3 100644 --- a/CRM/Core/Form.php +++ b/CRM/Core/Form.php @@ -217,6 +217,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page { 'number', 'url', 'email', + 'color', ); /** @@ -345,6 +346,7 @@ public function &add( $attributes = '', $required = FALSE, $extra = NULL ) { // Fudge some extra types that quickform doesn't support + $inputType = $type; if ($type == 'wysiwyg' || in_array($type, self::$html5Types)) { $attributes = ($attributes ? $attributes : array()) + array('class' => ''); $attributes['class'] = ltrim($attributes['class'] . " crm-form-$type"); @@ -354,6 +356,16 @@ public function &add( } $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()); @@ -384,6 +396,10 @@ public function &add( CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element)); } + if ($inputType == 'color') { + $this->addRule($name, ts('%1 must contain a color value e.g. #ffffff.', array(1 => $label)), 'regex', '/#[0-9a-fA-F]{6}/'); + } + if ($required) { if ($type == 'file') { $error = $this->addRule($name, ts('%1 is a required field.', array(1 => $label)), 'uploadedfile'); diff --git a/CRM/Core/Form/Renderer.php b/CRM/Core/Form/Renderer.php index 6f235382dbf9..3eeb16091dff 100644 --- a/CRM/Core/Form/Renderer.php +++ b/CRM/Core/Form/Renderer.php @@ -120,6 +120,9 @@ public function _elementToArray(&$element, $required, $error) { 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 @@ public static function preProcessEntityRef($field) { $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/Core/Smarty/plugins/modifier.colorContrast.php b/CRM/Core/Smarty/plugins/modifier.colorContrast.php new file mode 100644 index 000000000000..b1145e193de0 --- /dev/null +++ b/CRM/Core/Smarty/plugins/modifier.colorContrast.php @@ -0,0 +1,45 @@ +setPageTitle($this->_isTagSet ? ts('Tag Set') : ts('Tag')); - if ($this->_action == CRM_Core_Action::DELETE) { if ($this->_id && $tag = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'name', 'parent_id')) { $url = CRM_Utils_System::url('civicrm/tag', "reset=1"); @@ -60,6 +58,9 @@ public function buildQuickForm() { } } else { + $parentId = NULL; + $isTagsetChild = FALSE; + $this->_isTagSet = CRM_Utils_Request::retrieve('tagset', 'Positive', $this); if (!$this->_isTagSet && @@ -69,14 +70,16 @@ public function buildQuickForm() { $this->_isTagSet = TRUE; } - $allTag = array('' => ts('- select -')) + CRM_Core_BAO_Tag::getTagsNotInTagset(); - if ($this->_id) { - unset($allTag[$this->_id]); + $parentId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'parent_id'); + $isTagSetChild = $parentId ? CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $parentId, 'is_tagset') : FALSE; } if (!$this->_isTagSet) { - $this->add('select', 'parent_id', ts('Parent Tag'), $allTag, FALSE, array('class' => 'crm-select2')); + if (!$isTagSetChild) { + $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?')); @@ -85,6 +88,7 @@ public function buildQuickForm() { $selectable->setValue(1); } + $this->add('color', 'color', ts('Color')); } $this->assign('isTagSet', $this->_isTagSet); @@ -105,13 +109,7 @@ public function buildQuickForm() { $isReserved = $this->add('checkbox', 'is_reserved', ts('Reserved?')); - $usedFor = $this->addSelect('used_for', array('multiple' => TRUE, 'option_url' => NULL)); - - if ($this->_id && - CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'parent_id') - ) { - $usedFor->freeze(); - } + $this->addSelect('used_for', array('multiple' => TRUE, 'option_url' => NULL)); $adminTagset = TRUE; if (!CRM_Core_Permission::check('administer Tagsets')) { @@ -125,21 +123,32 @@ public function buildQuickForm() { $adminReservedTags = FALSE; } $this->assign('adminReservedTags', $adminReservedTags); - } + $this->setPageTitle($this->_isTagSet ? ts('Tag Set') : ts('Tag')); parent::buildQuickForm(); } + public function setDefaultValues() { + $defaults = parent::setDefaultValues(); + if (empty($this->_id) || !CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'color')) { + $defaults['color'] = '#ffffff'; + } + if (empty($this->_id)) { + $defaults['used_for'] = 'civicrm_contact'; + } + return $defaults; + } + /** * Process the form submission. */ public function postProcess() { - $params = $ids = array(); - // store the submitted values in an array $params = $this->exportValues(); + if ($this->_id) { + $params['id'] = $this->_id; + } - $ids['tag'] = $this->_id; if ($this->_action == CRM_Core_Action::ADD || $this->_action == CRM_Core_Action::UPDATE ) { @@ -159,16 +168,20 @@ public function postProcess() { $params['is_selectable'] = 0; } + if (strtolower($params['color']) == '#ffffff') { + $params['color'] = 'null'; + } + if ($this->_action == CRM_Core_Action::DELETE) { if ($this->_id > 0) { $tag = civicrm_api3('tag', 'getsingle', array('id' => $this->_id)); CRM_Core_BAO_Tag::del($this->_id); - CRM_Core_Session::setStatus(ts('The tag \'%1\' has been deleted.', array(1 => $tag['name'])), ts('Deleted'), 'success'); + CRM_Core_Session::setStatus(ts("The tag '%1' has been deleted.", array(1 => $tag['name'])), ts('Deleted'), 'success'); } } else { - $tag = CRM_Core_BAO_Tag::add($params, $ids); - CRM_Core_Session::setStatus(ts('The tag \'%1\' has been saved.', array(1 => $tag->name)), ts('Saved'), 'success'); + $tag = CRM_Core_BAO_Tag::add($params); + CRM_Core_Session::setStatus(ts("The tag '%1' has been saved.", array(1 => $tag->name)), ts('Saved'), 'success'); } } diff --git a/CRM/Tag/Form/Tag.php b/CRM/Tag/Form/Tag.php index a786ebe19ff9..7809b1f871eb 100644 --- a/CRM/Tag/Form/Tag.php +++ b/CRM/Tag/Form/Tag.php @@ -76,10 +76,10 @@ public function buildQuickForm() { $this->assign('tagged', $entityTag); // get the list of all the categories - $allTag = CRM_Core_BAO_Tag::getTagsUsedFor($this->_entityTable); + $allTags = CRM_Core_BAO_Tag::getTagsUsedFor($this->_entityTable, FALSE); // need to append the array with the " checked " if contact is tagged with the tag - foreach ($allTag as $tagID => $varValue) { + foreach ($allTags as $tagID => $varValue) { if (in_array($tagID, $entityTag)) { $tagAttribute = array( 'checked' => 'checked', @@ -112,7 +112,7 @@ public function buildQuickForm() { } $this->assign('tree', $tree); - $this->assign('tag', $allTag); + $this->assign('allTags', $allTags); //build tag widget $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_contact'); diff --git a/CRM/Tag/Page/Tag.php b/CRM/Tag/Page/Tag.php index 20ed8e097e95..42327a960bcd 100644 --- a/CRM/Tag/Page/Tag.php +++ b/CRM/Tag/Page/Tag.php @@ -164,10 +164,12 @@ public function browse($action = NULL, $sort = NULL) { $usedFor = CRM_Core_OptionGroup::values('tag_used_for'); - $query = "SELECT t1.name, t1.id, t2.name as parent, t1.description, t1.used_for, t1.is_tagset, - t1.is_reserved, t1.parent_id, t1.used_for - FROM civicrm_tag t1 LEFT JOIN civicrm_tag t2 ON t1.parent_id = t2.id - GROUP BY t1.parent_id, t1.id"; + $query = " SELECT t1.name, t1.id, t2.name as parent, t1.description, t1.used_for, t1.is_tagset as is_tagset, + t1.is_reserved, t1.parent_id, t1.used_for, t1.color, t2.is_tagset as is_tagset_child, t2.parent_id as grandparent_id + FROM civicrm_tag t1 + LEFT JOIN civicrm_tag t2 ON t1.parent_id = t2.id + LEFT JOIN civicrm_tag t3 ON t2.parent_id = t3.id + ORDER BY CONCAT(IFNULL(t3.name, ''), IFNULL(t2.name, ''), t1.name)"; $tag = CRM_Core_DAO::executeQuery($query); $values = array(); @@ -186,9 +188,7 @@ public function browse($action = NULL, $sort = NULL) { } } - if (!empty($used)) { - $values[$tag->id]['used_for'] = implode(", ", $used); - } + $values[$tag->id]['used_for'] = implode(", ", $used); $newAction = $action; if ($values[$tag->id]['is_reserved']) { diff --git a/CRM/Upgrade/Incremental/php/FourSeven.php b/CRM/Upgrade/Incremental/php/FourSeven.php index e51dee5950a5..a1e54cfd3d9b 100644 --- a/CRM/Upgrade/Incremental/php/FourSeven.php +++ b/CRM/Upgrade/Incremental/php/FourSeven.php @@ -284,6 +284,10 @@ public function upgrade_4_7_15($rev) { 'civicrm_option_value', 'icon', "varchar(255) COMMENT 'crm-i icon class' DEFAULT NULL"); $this->addTask('CRM-19626 - Add min_amount column to civicrm_price_set', 'addColumn', 'civicrm_price_set', 'min_amount', "INT(10) UNSIGNED DEFAULT '0' COMMENT 'Minimum Amount required for this set.'"); + $this->addTask('CRM-19769 - Add color column to civicrm_tag', 'addColumn', + 'civicrm_tag', 'color', "varchar(255) COMMENT 'Hex color value e.g. #ffffff' DEFAULT NULL"); + $this->addTask('CRM-19779 - Add color column to civicrm_option_value', 'addColumn', + 'civicrm_option_value', 'color', "varchar(255) COMMENT 'Hex color value e.g. #ffffff' DEFAULT NULL"); $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev); } diff --git a/CRM/Utils/Array.php b/CRM/Utils/Array.php index d82250cc7460..c2862e3449a7 100644 --- a/CRM/Utils/Array.php +++ b/CRM/Utils/Array.php @@ -1098,4 +1098,35 @@ public static function encode_items($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/CRM/Utils/Color.php b/CRM/Utils/Color.php new file mode 100644 index 000000000000..6349f8b7a551 --- /dev/null +++ b/CRM/Utils/Color.php @@ -0,0 +1,56 @@ += 128) ? 'black' : 'white'; + } + +} diff --git a/api/v3/Generic/Getlist.php b/api/v3/Generic/Getlist.php index b9334989ce69..1d4cac1dc0cd 100644 --- a/api/v3/Generic/Getlist.php +++ b/api/v3/Generic/Getlist.php @@ -88,6 +88,7 @@ function _civicrm_api3_generic_getList_defaults($entity, &$request, $apiDefaults 'page_num' => 1, 'input' => '', 'image_field' => NULL, + 'color_field' => isset($fields['color']) ? 'color' : NULL, 'id_field' => $entity == 'option_value' ? 'value' : 'id', 'description_field' => array(), 'params' => array(), @@ -149,6 +150,9 @@ function _civicrm_api3_generic_getlist_params(&$request) { if (!empty($request['image_field'])) { $fieldsToReturn[] = $request['image_field']; } + if (!empty($request['color_field'])) { + $fieldsToReturn[] = $request['color_field']; + } if (!empty($request['description_field'])) { $fieldsToReturn = array_merge($fieldsToReturn, (array) $request['description_field']); } @@ -193,6 +197,9 @@ function _civicrm_api3_generic_getlist_output($result, $request, $entity, $field if (!empty($request['image_field'])) { $data['image'] = isset($row[$request['image_field']]) ? $row[$request['image_field']] : ''; } + if (isset($row[$request['color_field']])) { + $data['color'] = $row[$request['color_field']]; + } $output[] = $data; } } diff --git a/css/civicrm.css b/css/civicrm.css index 90d6ffb3812d..716350ba6c0a 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 0c248d78bfde..a0b14924a98e 100644 --- a/css/contactSummary.css +++ b/css/contactSummary.css @@ -350,4 +350,4 @@ div#crm-contact-thumbnail { } #tagGroup table { color: #000; -} \ No newline at end of file +} diff --git a/js/Common.js b/js/Common.js index 38a2a20939d5..346e926f262b 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) + '

' : ''); } /** @@ -483,9 +492,7 @@ if (!CRM.vars) CRM.vars = {}; }, minimumInputLength: 1, formatResult: CRM.utils.formatSelect2Result, - formatSelection: function(row) { - return _.escape((row.prefix !== undefined ? row.prefix + ' ' : '') + row.label + (row.suffix !== undefined ? ' ' + row.suffix : '')); - }, + formatSelection: formatEntityRefSelection, escapeMarkup: _.identity, initSelection: function($el, callback) { var @@ -819,6 +826,7 @@ if (!CRM.vars) CRM.vars = {}; markup += '
'; } markup += '
' + + (row.color ? ' ' : '') + _.escape((row.prefix !== undefined ? row.prefix + ' ' : '') + row.label + (row.suffix !== undefined ? ' ' + row.suffix : '')) + '
' + '
'; @@ -829,6 +837,11 @@ if (!CRM.vars) CRM.vars = {}; return markup; }; + function formatEntityRefSelection(row) { + return (row.color ? ' ' : '') + + _.escape((row.prefix !== undefined ? row.prefix + ' ' : '') + row.label + (row.suffix !== undefined ? ' ' + row.suffix : '')); + } + function renderEntityRefCreateLinks($el) { var createLinks = $el.data('create-links'), diff --git a/templates/CRM/Activity/Selector/Selector.tpl b/templates/CRM/Activity/Selector/Selector.tpl index e49ec1c4e78c..e528963393b8 100644 --- a/templates/CRM/Activity/Selector/Selector.tpl +++ b/templates/CRM/Activity/Selector/Selector.tpl @@ -75,5 +75,15 @@ })(CRM.$); {/literal} +
{include file="CRM/Case/Form/ActivityToCase.tpl" contactID=$contactId} diff --git a/templates/CRM/Admin/Form/Options.tpl b/templates/CRM/Admin/Form/Options.tpl index a127ed006aea..92a22a3e785e 100644 --- a/templates/CRM/Admin/Form/Options.tpl +++ b/templates/CRM/Admin/Form/Options.tpl @@ -139,6 +139,12 @@ {$form.icon.html} {/if} + {if $form.color.html} + + {$form.color.label} + {$form.color.html} + + {/if} {if $form.component_id.html} {* Component ID is exposed for activity types if CiviCase is enabled. *} {$form.component_id.label} diff --git a/templates/CRM/Admin/Page/Options.tpl b/templates/CRM/Admin/Page/Options.tpl index 455baee40c8c..028007453211 100644 --- a/templates/CRM/Admin/Page/Options.tpl +++ b/templates/CRM/Admin/Page/Options.tpl @@ -132,7 +132,9 @@ {if $showComponent} {$row.component_name} {/if} - {$row.label} + + {$row.label} + {if $gName eq "case_status"} {$row.grouping} {/if} diff --git a/templates/CRM/Case/Form/ActivityTab.tpl b/templates/CRM/Case/Form/ActivityTab.tpl index a4b52e1e4112..fe76243ceb03 100644 --- a/templates/CRM/Case/Form/ActivityTab.tpl +++ b/templates/CRM/Case/Form/ActivityTab.tpl @@ -82,7 +82,7 @@ {ts}With{/ts} {ts}Reporter{/ts} {ts}Assignee{/ts} - {ts}Status{/ts} + {ts}Status{/ts}   @@ -111,6 +111,16 @@ })(CRM.$); {/literal} + {if isset($isForm) and $isForm}
diff --git a/templates/CRM/Case/Form/CaseView.js b/templates/CRM/Case/Form/CaseView.js index b09f18bb619c..43ae02172aef 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 3de01fc521c5..61722a8d561f 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} diff --git a/templates/CRM/Contact/Page/View/Summary.tpl b/templates/CRM/Contact/Page/View/Summary.tpl index d585acb1cf76..2cd574ad5b00 100644 --- a/templates/CRM/Contact/Page/View/Summary.tpl +++ b/templates/CRM/Contact/Page/View/Summary.tpl @@ -164,7 +164,13 @@ {ts}Tags{/ts}
-
{$contactTag}
+
+ {foreach from=$contactTag item=tagName key=tagId} + + {$tagName} + + {/foreach} +
{ts}Contact Type{/ts}
diff --git a/templates/CRM/Tag/Form/Edit.tpl b/templates/CRM/Tag/Form/Edit.tpl index fa60d2265ac4..46dc9725cdca 100644 --- a/templates/CRM/Tag/Form/Edit.tpl +++ b/templates/CRM/Tag/Form/Edit.tpl @@ -52,6 +52,12 @@ + {if $form.color.html} + + {$form.color.label} + {$form.color.html} + + {/if} {$form.is_reserved.label} {$form.is_reserved.html}
{ts}Reserved tags can not be deleted. Users with 'administer reserved tags' permission can set or unset the reserved flag. You must uncheck 'Reserved' (and delete any child tags) before you can delete a tag.{/ts} @@ -79,3 +85,17 @@ {/if}
{include file="CRM/common/formButtons.tpl" location="bottom"}
+{literal} + +{/literal} \ No newline at end of file diff --git a/templates/CRM/Tag/Form/Tag.tpl b/templates/CRM/Tag/Form/Tag.tpl index 1cf656f93199..d0e929d842f1 100644 --- a/templates/CRM/Tag/Form/Tag.tpl +++ b/templates/CRM/Tag/Form/Tag.tpl @@ -26,7 +26,7 @@ {* this template is used for adding/editing tags *} {literal} {/literal} {/if} diff --git a/tests/phpunit/CRM/Utils/ColorTest.php b/tests/phpunit/CRM/Utils/ColorTest.php new file mode 100644 index 000000000000..77b894f2a98b --- /dev/null +++ b/tests/phpunit/CRM/Utils/ColorTest.php @@ -0,0 +1,29 @@ +assertEquals($text, CRM_Utils_Color::getContrast($background)); + } + + public function contrastExamples() { + return array( + array('ef4444', 'white'), + array('FAA31B', 'black'), + array('FFF000', 'black'), + array(' 82c341', 'black'), + array('#009F75', 'white'), + array('#88C6eD', 'black'), + array('# 394ba0', 'white'), + array(' #D54799', 'white'), + ); + } + +} diff --git a/xml/schema/Core/OptionValue.xml b/xml/schema/Core/OptionValue.xml index 041d2855ae5e..ac469d7cdcec 100644 --- a/xml/schema/Core/OptionValue.xml +++ b/xml/schema/Core/OptionValue.xml @@ -192,6 +192,15 @@ crm-i icon class 4.7 + + color + Color + varchar + 255 + Hex color value e.g. #ffffff + NULL + 4.7 + index_option_group_id_value value diff --git a/xml/schema/Core/Tag.xml b/xml/schema/Core/Tag.xml index 13fd3869df34..2b7b047f1816 100644 --- a/xml/schema/Core/Tag.xml +++ b/xml/schema/Core/Tag.xml @@ -99,6 +99,15 @@ FK to civicrm_contact, who created this tag 3.4 + + color + Color + varchar + 255 + Hex color value e.g. #ffffff + NULL + 4.7 + created_id civicrm_contact