diff --git a/CRM/Admin/Page/AJAX.php b/CRM/Admin/Page/AJAX.php index 35f5f2aabd56..1aa2d5c0ad23 100644 --- a/CRM/Admin/Page/AJAX.php +++ b/CRM/Admin/Page/AJAX.php @@ -304,20 +304,24 @@ public static function getTagTree() { $parent = CRM_Utils_Type::escape(CRM_Utils_Array::value('parent_id', $_GET, 0), 'Integer'); $result = array(); - $parentClause = $parent ? "AND tag.parent_id = $parent" : 'AND tag.parent_id IS NULL'; - $sql = "SELECT tag.*, child.id AS child, COUNT(et.id) as usages - FROM civicrm_tag tag - LEFT JOIN civicrm_entity_tag et ON et.tag_id = tag.id - LEFT JOIN civicrm_tag child ON child.parent_id = tag.id - WHERE tag.is_tagset <> 1 $parentClause - GROUP BY tag.id - ORDER BY tag.name"; + $parentClause = $parent ? "AND parent_id = $parent" : 'AND parent_id IS NULL'; + $sql = "SELECT * + FROM civicrm_tag + WHERE is_tagset <> 1 $parentClause + GROUP BY id + ORDER BY name"; + + // fetch all child tags in Array('parent_tag' => array('child_tag_1', 'child_tag_2', ...)) format + $childTagIDs = CRM_Core_BAO_Tag::getChildTags(); + $dao = CRM_Core_DAO::executeQuery($sql); while ($dao->fetch()) { $style = ''; if ($dao->color) { $style = "background-color: {$dao->color}; color: " . CRM_Utils_Color::getContrast($dao->color); } + $hasChildTags = empty($childTagIDs[$dao->id]) ? FALSE : TRUE; + $usedFor = (array) explode(',', $dao->used_for); $result[] = array( 'id' => $dao->id, 'text' => $dao->name, @@ -330,18 +334,25 @@ public static function getTagTree() { 'style' => $style, 'class' => 'crm-tag-item', ), - 'children' => (bool) $dao->child, + 'children' => $hasChildTags, 'data' => array( 'description' => (string) $dao->description, 'is_selectable' => (bool) $dao->is_selectable, 'is_reserved' => (bool) $dao->is_reserved, - 'used_for' => $dao->used_for ? explode(',', $dao->used_for) : array(), + 'used_for' => $usedFor, 'color' => $dao->color ? $dao->color : '#ffffff', - 'usages' => (int) $dao->usages, + 'usages' => civicrm_api3('EntityTag', 'getcount', array( + 'entity_table' => array('IN' => $usedFor), + 'tag_id' => $dao->id, + )), ), ); } + if (!empty($_REQUEST['is_unit_test'])) { + return $result; + } + CRM_Utils_JSON::output($result); } diff --git a/CRM/Core/BAO/Tag.php b/CRM/Core/BAO/Tag.php index 31722e897d7f..a8316323b760 100644 --- a/CRM/Core/BAO/Tag.php +++ b/CRM/Core/BAO/Tag.php @@ -531,4 +531,40 @@ public static function getTagsNotInTagset() { return $tags; } + /** + * Get child tags IDs + * + * @return array $childTagIDs + * associated array of child tags in Array('Parent Tag ID' => Array('Child Tag 1', ...)) format + */ + public static function getChildTags() { + $childTagIDs = array(); + + // only fetch those tags which has child tags + $getChildGroupSQL = "SELECT parent.id as parent_id, GROUP_CONCAT(child.id) as child_id + FROM civicrm_tag parent, + civicrm_tag child + WHERE parent.is_tagset <> 1 AND child.parent_id = parent.id + GROUP BY parent.id + "; + $dao = CRM_Core_DAO::executeQuery($getChildGroupSQL); + while ($dao->fetch()) { + $childTagIDs[$dao->parent_id] = (array) explode(',', $dao->child_id); + } + + // check if child tag has any childs, if found then include those child tags inside parent tag + // i.e. format Array('parent_tag' => array('child_tag_1', ...), 'child_tag_1' => array(child_tag_1_1, ..), ..) + // to Array('parent_tag' => array('child_tag_1', 'child_tag_1_1'...), ..) + foreach ($childTagIDs as $parentTagID => $childTags) { + foreach ($childTags as $childTag) { + // if $childTag has any child tag of its own + if (array_key_exists($childTag, $childTagIDs)) { + $childTagIDs[$parentTagID] = array_merge($childTagIDs[$parentTagID], $childTagIDs[$childTag]); + } + } + } + + return $childTagIDs; + } + } diff --git a/tests/phpunit/CRM/Contact/Page/AjaxTest.php b/tests/phpunit/CRM/Contact/Page/AjaxTest.php index cbc02de1b601..acc0eb8cfee4 100644 --- a/tests/phpunit/CRM/Contact/Page/AjaxTest.php +++ b/tests/phpunit/CRM/Contact/Page/AjaxTest.php @@ -210,6 +210,89 @@ public function testGetDedupesPostCode() { $this->assertEquals(array('data' => array(), 'recordsTotal' => 0, 'recordsFiltered' => 0), $result); } + /** + * CRM-20621 : Test to check usage count of Tag tree + */ + public function testGetTagTree() { + $contacts = array(); + // create three contacts + for ($i = 0; $i < 3; $i++) { + $contacts[] = $this->individualCreate(); + } + + // Create Tag called as 'Parent Tag' + $parentTag = $this->tagCreate(array( + 'name' => 'Parent Tag', + 'used_for' => 'civicrm_contact', + )); + //assign first contact to parent tag + $params = array( + 'entity_id' => $contacts[0], + 'entity_table' => 'civicrm_contact', + 'tag_id' => $parentTag['id'], + ); + // TODO: EntityTag.create API is not working + CRM_Core_BAO_EntityTag::add($params); + + // Create child Tag of $parentTag + $childTag1 = $this->tagCreate(array( + 'name' => 'Child Tag Level 1', + 'parent_id' => $parentTag['id'], + 'used_for' => 'civicrm_contact', + )); + //assign contact to this level 1 child tag + $params = array( + 'entity_id' => $contacts[1], + 'entity_table' => 'civicrm_contact', + 'tag_id' => $childTag1['id'], + ); + CRM_Core_BAO_EntityTag::add($params); + + // Create child Tag of $childTag1 + $childTag2 = $this->tagCreate(array( + 'name' => 'Child Tag Level 2', + 'parent_id' => $childTag1['id'], + 'used_for' => 'civicrm_contact', + )); + //assign contact to this level 2 child tag + $params = array( + 'entity_id' => $contacts[2], + 'entity_table' => 'civicrm_contact', + 'tag_id' => $childTag2['id'], + ); + CRM_Core_BAO_EntityTag::add($params); + + // CASE I : check the usage count of parent tag which need to be 1 + // as the one contact added + $_REQUEST['is_unit_test'] = TRUE; + $parentTagTreeResult = CRM_Admin_Page_AJAX::getTagTree(); + foreach ($parentTagTreeResult as $result) { + if ($result['id'] == $parentTag['id']) { + $this->assertEquals(1, $result['data']['usages']); + } + } + + // CASE 2 : check the usage count of level 1 child tag, which needs to be 1 + // as it should include the count of added one contact + $_GET['parent_id'] = $parentTag['id']; + $childTagTree = CRM_Admin_Page_AJAX::getTagTree(); + $this->assertEquals(1, $childTagTree[0]['data']['usages']); + + // CASE 2 : check the usage count of child tag at level 2 + //which needs to be 1 as it has no child tag + $_GET['parent_id'] = $childTag1['id']; + $childTagTree = CRM_Admin_Page_AJAX::getTagTree(); + $this->assertEquals(1, $childTagTree[0]['data']['usages']); + + //cleanup + foreach ($contacts as $id) { + $this->callAPISuccess('Contact', 'delete', array('id' => $id)); + } + $this->callAPISuccess('Tag', 'delete', array('id' => $childTag2['id'])); + $this->callAPISuccess('Tag', 'delete', array('id' => $childTag1['id'])); + $this->callAPISuccess('Tag', 'delete', array('id' => $parentTag['id'])); + } + /** * Test to check contact reference field */