Merge remote-tracking branch 'upstream/4.5' into 4.5-master-2015-01-19-15-14-40
[civicrm-core.git] / CRM / Core / BAO / EntityTag.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26*/
27
28/**
29 * This class contains functions for managing Tag(tag) for a contact
30 *
31 * @package CRM
06b69b18 32 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
33 * $Id$
34 *
35 */
36class CRM_Core_BAO_EntityTag extends CRM_Core_DAO_EntityTag {
37
38 /**
39 *
40 * Given a contact id, it returns an array of tag id's the
41 * contact belongs to.
42 *
6a0b768e
TO
43 * @param int $entityID
44 * Id of the entity usually the contactID.
45 * @param string $entityTable
46 * Name of the entity table usually 'civicrm_contact'.
6a488035
TO
47 *
48 * @return array(
353ffa53 49 * ) reference $tag array of category id's the contact belongs to.
6a488035 50 *
6a488035 51 */
00be9182 52 public static function &getTag($entityID, $entityTable = 'civicrm_contact') {
6a488035
TO
53 $tags = array();
54
55 $entityTag = new CRM_Core_BAO_EntityTag();
56 $entityTag->entity_id = $entityID;
57 $entityTag->entity_table = $entityTable;
58 $entityTag->find();
59
60 while ($entityTag->fetch()) {
61 $tags[$entityTag->tag_id] = $entityTag->tag_id;
62 }
63 return $tags;
64 }
65
66 /**
100fef9d 67 * Takes an associative array and creates a entityTag object
6a488035
TO
68 *
69 * the function extract all the params it needs to initialize the create a
70 * group object. the params array could contain additional unused name/value
71 * pairs
72 *
6a0b768e
TO
73 * @param array $params
74 * (reference ) an assoc array of name/value pairs.
6a488035 75 *
16b10e64 76 * @return CRM_Core_BAO_EntityTag
6a488035 77 */
00be9182 78 public static function add(&$params) {
6a488035
TO
79 $dataExists = self::dataExists($params);
80 if (!$dataExists) {
81 return NULL;
82 }
83
84 $entityTag = new CRM_Core_BAO_EntityTag();
85 $entityTag->copyValues($params);
86
87 // dont save the object if it already exists, CRM-1276
88 if (!$entityTag->find(TRUE)) {
89 $entityTag->save();
90
91 //invoke post hook on entityTag
92 // we are using this format to keep things consistent between the single and bulk operations
93 // so a bit different from other post hooks
94 $object = array(0 => array(0 => $params['entity_id']), 1 => $params['entity_table']);
95 CRM_Utils_Hook::post('create', 'EntityTag', $params['tag_id'], $object);
96 }
97 return $entityTag;
98 }
99
100 /**
101 * Check if there is data to create the object
102 *
6a0b768e
TO
103 * @param array $params
104 * An assoc array of name/value pairs.
dd244018 105 *
6a488035 106 * @return boolean
6a488035 107 */
00be9182 108 public static function dataExists($params) {
c490a46a 109 return !($params['tag_id'] == 0);
6a488035
TO
110 }
111
112 /**
100fef9d 113 * Delete the tag for a contact
6a488035 114 *
6a0b768e
TO
115 * @param array $params
116 * (reference ) an assoc array of name/value pairs.
6a488035 117 *
16b10e64 118 * @return CRM_Core_BAO_EntityTag
6a488035 119 */
00be9182 120 public static function del(&$params) {
6a488035
TO
121 $entityTag = new CRM_Core_BAO_EntityTag();
122 $entityTag->copyValues($params);
355b20b1 123 $entityTag->delete();
6a488035 124
355b20b1 125 //invoke post hook on entityTag
126 $object = array(0 => array(0 => $params['entity_id']), 1 => $params['entity_table']);
127 CRM_Utils_Hook::post('delete', 'EntityTag', $params['tag_id'], $object);
6a488035
TO
128 }
129
130 /**
131 * Given an array of entity ids and entity table, add all the entity to the tags
132 *
6a0b768e
TO
133 * @param array $entityIds
134 * (reference ) the array of entity ids to be added.
135 * @param int $tagId
136 * The id of the tag.
137 * @param string $entityTable
138 * Name of entity table default:civicrm_contact.
6a488035 139 *
a6c01b45
CW
140 * @return array
141 * (total, added, notAdded) count of enities added to tag
6a488035 142 */
00be9182 143 public static function addEntitiesToTag(&$entityIds, $tagId, $entityTable = 'civicrm_contact') {
353ffa53 144 $numEntitiesAdded = 0;
6a488035 145 $numEntitiesNotAdded = 0;
353ffa53 146 $entityIdsAdded = array();
6a488035
TO
147
148 foreach ($entityIds as $entityId) {
149 $tag = new CRM_Core_DAO_EntityTag();
150
353ffa53
TO
151 $tag->entity_id = $entityId;
152 $tag->tag_id = $tagId;
6a488035
TO
153 $tag->entity_table = $entityTable;
154 if (!$tag->find()) {
155 $tag->save();
156 $entityIdsAdded[] = $entityId;
157 $numEntitiesAdded++;
158 }
159 else {
160 $numEntitiesNotAdded++;
161 }
162 }
163
164 //invoke post hook on entityTag
165 $object = array($entityIdsAdded, $entityTable);
166 CRM_Utils_Hook::post('create', 'EntityTag', $tagId, $object);
167
168 // reset the group contact cache for all groups
169 // if tags are being used in a smart group
170 CRM_Contact_BAO_GroupContactCache::remove();
171
172 return array(count($entityIds), $numEntitiesAdded, $numEntitiesNotAdded);
173 }
174
175 /**
176 * Given an array of entity ids and entity table, remove entity(s) tags
177 *
6a0b768e
TO
178 * @param array $entityIds
179 * (reference ) the array of entity ids to be removed.
180 * @param int $tagId
181 * The id of the tag.
182 * @param string $entityTable
183 * Name of entity table default:civicrm_contact.
6a488035 184 *
a6c01b45
CW
185 * @return array
186 * (total, removed, notRemoved) count of entities removed from tags
6a488035 187 */
00be9182 188 public static function removeEntitiesFromTag(&$entityIds, $tagId, $entityTable = 'civicrm_contact') {
6a488035
TO
189 $numEntitiesRemoved = 0;
190 $numEntitiesNotRemoved = 0;
191 $entityIdsRemoved = array();
192
193 foreach ($entityIds as $entityId) {
194 $tag = new CRM_Core_DAO_EntityTag();
195
353ffa53
TO
196 $tag->entity_id = $entityId;
197 $tag->tag_id = $tagId;
6a488035
TO
198 $tag->entity_table = $entityTable;
199 if ($tag->find()) {
200 $tag->delete();
201 $entityIdsRemoved[] = $entityId;
202 $numEntitiesRemoved++;
203 }
204 else {
205 $numEntitiesNotRemoved++;
206 }
207 }
208
209 //invoke post hook on entityTag
210 $object = array($entityIdsRemoved, $entityTable);
211 CRM_Utils_Hook::post('delete', 'EntityTag', $tagId, $object);
212
213 // reset the group contact cache for all groups
214 // if tags are being used in a smart group
215 CRM_Contact_BAO_GroupContactCache::remove();
216
217 return array(count($entityIds), $numEntitiesRemoved, $numEntitiesNotRemoved);
218 }
219
220 /**
100fef9d 221 * Takes an associative array and creates tag entity record for all tag entities
6a488035 222 *
6a0b768e
TO
223 * @param array $params
224 * (reference) an assoc array of name/value pairs.
dd244018 225 * @param $entityTable
100fef9d 226 * @param int $entityID
dd244018 227 *
6a488035 228 * @return void
6a488035 229 */
00be9182 230 public static function create(&$params, $entityTable, $entityID) {
6a488035
TO
231 // get categories for the entity id
232 $entityTag = CRM_Core_BAO_EntityTag::getTag($entityID, $entityTable);
233
234 // get the list of all the categories
235 $allTag = CRM_Core_BAO_Tag::getTags($entityTable);
236
237 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
238 if (!is_array($params)) {
239 $params = array();
240 }
241
242 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
243 if (!is_array($entityTag)) {
244 $entityTag = array();
245 }
246
247 // check which values has to be inserted/deleted for contact
248 foreach ($allTag as $key => $varValue) {
249 $tagParams['entity_table'] = $entityTable;
250 $tagParams['entity_id'] = $entityID;
251 $tagParams['tag_id'] = $key;
252
253 if (array_key_exists($key, $params) && !array_key_exists($key, $entityTag)) {
254 // insert a new record
255 CRM_Core_BAO_EntityTag::add($tagParams);
256 }
257 elseif (!array_key_exists($key, $params) && array_key_exists($key, $entityTag)) {
258 // delete a record for existing contact
259 CRM_Core_BAO_EntityTag::del($tagParams);
260 }
261 }
262 }
263
264 /**
265 * This function returns all entities assigned to a specific tag
266 *
6a0b768e
TO
267 * @param object $tag
268 * An object of a tag.
6a488035 269 *
a6c01b45
CW
270 * @return array
271 * array of entity ids
6a488035 272 */
00be9182 273 public function getEntitiesByTag($tag) {
07fc2fd3 274 $entityIds = array();
6a488035
TO
275 $entityTagDAO = new CRM_Core_DAO_EntityTag();
276 $entityTagDAO->tag_id = $tag->id;
277 $entityTagDAO->find();
278 while ($entityTagDAO->fetch()) {
07fc2fd3 279 $entityIds[] = $entityTagDAO->entity_id;
6a488035 280 }
07fc2fd3 281 return $entityIds;
6a488035
TO
282 }
283
284 /**
100fef9d 285 * Get contact tags
6a488035 286 */
00be9182 287 public static function getContactTags($contactID, $count = FALSE) {
6a488035
TO
288 $contactTags = array();
289 if (!$count) {
290 $select = "SELECT name ";
291 }
292 else {
293 $select = "SELECT count(*) as cnt";
294 }
295
8ef12e64 296 $query = "{$select}
297 FROM civicrm_tag ct
6a488035
TO
298 INNER JOIN civicrm_entity_tag et ON ( ct.id = et.tag_id AND
299 et.entity_id = {$contactID} AND
300 et.entity_table = 'civicrm_contact' AND
301 ct.is_tagset = 0 )";
302
303 $dao = CRM_Core_DAO::executeQuery($query);
304
305 if ($count) {
306 $dao->fetch();
307 return $dao->cnt;
308 }
309
310 while ($dao->fetch()) {
311 $contactTags[] = $dao->name;
312 }
313
314 return $contactTags;
315 }
316
317 /**
100fef9d 318 * Get child contact tags given parentId
6a488035 319 */
00be9182 320 public static function getChildEntityTags($parentId, $entityId, $entityTable = 'civicrm_contact') {
6a488035
TO
321 $entityTags = array();
322 $query = "SELECT ct.id as tag_id, name FROM civicrm_tag ct
323 INNER JOIN civicrm_entity_tag et ON ( et.entity_id = {$entityId} AND
324 et.entity_table = '{$entityTable}' AND et.tag_id = ct.id)
325 WHERE ct.parent_id = {$parentId}";
326
327 $dao = CRM_Core_DAO::executeQuery($query);
328
329 while ($dao->fetch()) {
330 $entityTags[$dao->tag_id] = array(
331 'id' => $dao->tag_id,
332 'name' => $dao->name,
333 );
334 }
335
336 return $entityTags;
337 }
338
339 /**
100fef9d 340 * Merge two tags: tag B into tag A.
6a488035 341 */
00be9182 342 public function mergeTags($tagAId, $tagBId) {
2aa397bc 343 $queryParams = array(
353ffa53 344 1 => array($tagBId, 'Integer'),
6a488035
TO
345 2 => array($tagAId, 'Integer'),
346 );
347
348 // re-compute used_for field
349 $query = "SELECT id, name, used_for FROM civicrm_tag WHERE id IN (%1, %2)";
353ffa53
TO
350 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
351 $tags = array();
6a488035
TO
352 while ($dao->fetch()) {
353 $label = ($dao->id == $tagAId) ? 'tagA' : 'tagB';
354 $tags[$label] = $dao->name;
355 $tags["{$label}_used_for"] = $dao->used_for ? explode(",", $dao->used_for) : array();
356 }
357 $usedFor = array_merge($tags["tagA_used_for"], $tags["tagB_used_for"]);
358 $usedFor = implode(',', array_unique($usedFor));
359 $tags["tagB_used_for"] = explode(",", $usedFor);
360
361 // get all merge queries together
362 $sqls = array(
363 // 1. update entity tag entries
c2105be3 364 "UPDATE IGNORE civicrm_entity_tag SET tag_id = %1 WHERE tag_id = %2",
6a488035
TO
365 // 2. update used_for info for tag B
366 "UPDATE civicrm_tag SET used_for = '{$usedFor}' WHERE id = %1",
367 // 3. remove tag A, if tag A is getting merged into B
368 "DELETE FROM civicrm_tag WHERE id = %2",
369 // 4. remove duplicate entity tag records
370 "DELETE et2.* from civicrm_entity_tag et1 INNER JOIN civicrm_entity_tag et2 ON et1.entity_table = et2.entity_table AND et1.entity_id = et2.entity_id AND et1.tag_id = et2.tag_id WHERE et1.id < et2.id",
c2105be3
BS
371 // 5. remove orphaned entity_tags
372 "DELETE FROM civicrm_entity_tag WHERE tag_id = %2",
6a488035
TO
373 );
374 $tables = array('civicrm_entity_tag', 'civicrm_tag');
375
376 // Allow hook_civicrm_merge() to add SQL statements for the merge operation AND / OR
377 // perform any other actions like logging
378 CRM_Utils_Hook::merge('sqls', $sqls, $tagAId, $tagBId, $tables);
379
380 // call the SQL queries in one transaction
381 $transaction = new CRM_Core_Transaction();
382 foreach ($sqls as $sql) {
383 CRM_Core_DAO::executeQuery($sql, $queryParams, TRUE, NULL, TRUE);
384 }
385 $transaction->commit();
386
387 $tags['status'] = TRUE;
388 return $tags;
389 }
76773c5a
CW
390
391 /**
392 * Get options for a given field.
393 * @see CRM_Core_DAO::buildOptions
394 *
6a0b768e
TO
395 * @param string $fieldName
396 * @param string $context
16b10e64 397 * @see CRM_Core_DAO::buildOptionsContext.
6a0b768e 398 * @param array $props
16b10e64 399 * whatever is known about this dao object.
76773c5a
CW
400 *
401 * @return Array|bool
402 */
403 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
404 $params = array();
405
406 $options = CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context);
407
408 // Output tag list as nested hierarchy
409 // TODO: This will only work when api.entity is "entity_tag". What about others?
410 if (($fieldName == 'tag' || $fieldName == 'tag_id') && ($context == 'search' || $context == 'create')) {
411 $options = CRM_Core_BAO_Tag::getTags('civicrm_contact', CRM_Core_DAO::$_nullArray, CRM_Utils_Array::value('parent_id', $params), '- ');
412 }
413
414 return $options;
415 }
6a488035 416}