Merge remote-tracking branch 'upstream/4.5' into 4.5-4.6-2015-03-16-17-24-33
[civicrm-core.git] / CRM / Contact / BAO / ContactType.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35 class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType {
36
37 /**
38 * Fetch object based on array of properties.
39 *
40 * @param array $params
41 * (reference ) an assoc array of name/value pairs.
42 * @param array $defaults
43 * (reference ) an assoc array to hold the flattened values.
44 *
45 * @return CRM_Contact_BAO_ContactType|null
46 * object on success, null otherwise
47 */
48 public static function retrieve(&$params, &$defaults) {
49 $contactType = new CRM_Contact_DAO_ContactType();
50 $contactType->copyValues($params);
51 if ($contactType->find(TRUE)) {
52 CRM_Core_DAO::storeValues($contactType, $defaults);
53 return $contactType;
54 }
55 return NULL;
56 }
57
58 /**
59 * @param $contactType
60 *
61 * @return bool
62 */
63 public static function isActive($contactType) {
64 $contact = self::contactTypeInfo(FALSE);
65 $active = array_key_exists($contactType, $contact) ? TRUE : FALSE;
66 return $active;
67 }
68
69 /**
70 * Retrieve basic contact type information.
71 *
72 * @param bool $all
73 *
74 * @return array
75 * Array of basic contact types information.
76 */
77 public static function &basicTypeInfo($all = FALSE) {
78 static $_cache = NULL;
79
80 if ($_cache === NULL) {
81 $_cache = array();
82 }
83
84 $argString = $all ? 'CRM_CT_BTI_1' : 'CRM_CT_BTI_0';
85 if (!array_key_exists($argString, $_cache)) {
86 $cache = CRM_Utils_Cache::singleton();
87 $_cache[$argString] = $cache->get($argString);
88 if (!$_cache[$argString]) {
89 $sql = "
90 SELECT *
91 FROM civicrm_contact_type
92 WHERE parent_id IS NULL
93 ";
94 if ($all === FALSE) {
95 $sql .= " AND is_active = 1";
96 }
97
98 $dao = CRM_Core_DAO::executeQuery($sql,
99 CRM_Core_DAO::$_nullArray,
100 FALSE,
101 'CRM_Contact_DAO_ContactType'
102 );
103 while ($dao->fetch()) {
104 $value = array();
105 CRM_Core_DAO::storeValues($dao, $value);
106 $_cache[$argString][$dao->name] = $value;
107 }
108
109 $cache->set($argString, $_cache[$argString]);
110 }
111 }
112 return $_cache[$argString];
113 }
114
115 /**
116 * Retrieve all basic contact types.
117 *
118 * @param bool $all
119 *
120 * @return array
121 * Array of basic contact types
122 */
123 public static function basicTypes($all = FALSE) {
124 return array_keys(self::basicTypeInfo($all));
125 }
126
127 /**
128 * @param bool $all
129 * @param string $key
130 *
131 * @return array
132 */
133 public static function basicTypePairs($all = FALSE, $key = 'name') {
134 $subtypes = self::basicTypeInfo($all);
135
136 $pairs = array();
137 foreach ($subtypes as $name => $info) {
138 $index = ($key == 'name') ? $name : $info[$key];
139 $pairs[$index] = $info['label'];
140 }
141 return $pairs;
142 }
143
144 /**
145 * Retrieve all subtypes Information.
146 *
147 * @param array $contactType
148 * ..
149 * @param bool $all
150 * @param bool $ignoreCache
151 * @param bool $reset
152 *
153 * @return array
154 * Array of sub type information
155 */
156 public static function &subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCache = FALSE, $reset = FALSE) {
157 static $_cache = NULL;
158
159 if ($reset === TRUE) {
160 $_cache = NULL;
161 }
162
163 if ($_cache === NULL) {
164 $_cache = array();
165 }
166 if ($contactType && !is_array($contactType)) {
167 $contactType = array($contactType);
168 }
169
170 $argString = $all ? 'CRM_CT_STI_1_' : 'CRM_CT_STI_0_';
171 if (!empty($contactType)) {
172 $argString .= implode('_', $contactType);
173 }
174
175 if ((!array_key_exists($argString, $_cache)) || $ignoreCache) {
176 $cache = CRM_Utils_Cache::singleton();
177 $_cache[$argString] = $cache->get($argString);
178 if (!$_cache[$argString] || $ignoreCache) {
179 $_cache[$argString] = array();
180
181 $ctWHERE = '';
182 if (!empty($contactType)) {
183 $ctWHERE = " AND parent.name IN ('" . implode("','", $contactType) . "')";
184 }
185
186 $sql = "
187 SELECT subtype.*, parent.name as parent, parent.label as parent_label
188 FROM civicrm_contact_type subtype
189 INNER JOIN civicrm_contact_type parent ON subtype.parent_id = parent.id
190 WHERE subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
191 ";
192 if ($all === FALSE) {
193 $sql .= " AND subtype.is_active = 1 AND parent.is_active = 1 ORDER BY parent.id";
194 }
195 $dao = CRM_Core_DAO::executeQuery($sql, array(),
196 FALSE, 'CRM_Contact_DAO_ContactType'
197 );
198 while ($dao->fetch()) {
199 $value = array();
200 CRM_Core_DAO::storeValues($dao, $value);
201 $value['parent'] = $dao->parent;
202 $value['parent_label'] = $dao->parent_label;
203 $_cache[$argString][$dao->name] = $value;
204 }
205
206 $cache->set($argString, $_cache[$argString]);
207 }
208 }
209 return $_cache[$argString];
210 }
211
212 /**
213 *
214 * retrieve all subtypes
215 *
216 * @param array $contactType
217 * ..
218 * @param bool $all
219 * @param string $columnName
220 * @param bool $ignoreCache
221 *
222 * @return array
223 * all subtypes OR list of subtypes associated to
224 * a given basic contact type
225 */
226 public static function subTypes($contactType = NULL, $all = FALSE, $columnName = 'name', $ignoreCache = FALSE) {
227 if ($columnName == 'name') {
228 return array_keys(self::subTypeInfo($contactType, $all, $ignoreCache));
229 }
230 else {
231 return array_values(self::subTypePairs($contactType, FALSE, NULL, $ignoreCache));
232 }
233 }
234
235 /**
236 *
237 * retrieve subtype pairs with name as 'subtype-name' and 'label' as value
238 *
239 * @param array $contactType
240 * @param bool $all
241 * @param string $labelPrefix
242 * @param bool $ignoreCache
243 *
244 * @return array
245 * list of subtypes with name as 'subtype-name' and 'label' as value
246 */
247 public static function subTypePairs($contactType = NULL, $all = FALSE, $labelPrefix = '- ', $ignoreCache = FALSE) {
248 $subtypes = self::subTypeInfo($contactType, $all, $ignoreCache);
249
250 $pairs = array();
251 foreach ($subtypes as $name => $info) {
252 $pairs[$name] = $labelPrefix . $info['label'];
253 }
254 return $pairs;
255 }
256
257 /**
258 *
259 * retrieve list of all types i.e basic + subtypes.
260 *
261 * @param bool $all
262 *
263 * @return array
264 * Array of basic types + all subtypes.
265 */
266 public static function contactTypes($all = FALSE) {
267 return array_keys(self::contactTypeInfo($all));
268 }
269
270 /**
271 *
272 * retrieve info array about all types i.e basic + subtypes.
273 *
274 * @param bool $all
275 * @param bool $reset
276 *
277 * @return array
278 * Array of basic types + all subtypes.
279 */
280 public static function contactTypeInfo($all = FALSE, $reset = FALSE) {
281 static $_cache = NULL;
282
283 if ($reset === TRUE) {
284 $_cache = NULL;
285 }
286
287 if ($_cache === NULL) {
288 $_cache = array();
289 }
290
291 $argString = $all ? 'CRM_CT_CTI_1' : 'CRM_CT_CTI_0';
292 if (!array_key_exists($argString, $_cache)) {
293 $cache = CRM_Utils_Cache::singleton();
294 $_cache[$argString] = $cache->get($argString);
295 if (!$_cache[$argString]) {
296 $_cache[$argString] = array();
297
298 $sql = "
299 SELECT type.*, parent.name as parent, parent.label as parent_label
300 FROM civicrm_contact_type type
301 LEFT JOIN civicrm_contact_type parent ON type.parent_id = parent.id
302 WHERE type.name IS NOT NULL
303 ";
304 if ($all === FALSE) {
305 $sql .= " AND type.is_active = 1";
306 }
307
308 $dao = CRM_Core_DAO::executeQuery($sql,
309 array(),
310 FALSE,
311 'CRM_Contact_DAO_ContactType'
312 );
313 while ($dao->fetch()) {
314 $value = array();
315 CRM_Core_DAO::storeValues($dao, $value);
316 if (array_key_exists('parent_id', $value)) {
317 $value['parent'] = $dao->parent;
318 $value['parent_label'] = $dao->parent_label;
319 }
320 $_cache[$argString][$dao->name] = $value;
321 }
322
323 $cache->set($argString, $_cache[$argString]);
324 }
325 }
326
327 return $_cache[$argString];
328 }
329
330 /**
331 * Retrieve basic type pairs with name as 'built-in name' and 'label' as value
332 *
333 * @param bool $all
334 * @param null $typeName
335 * @param null $delimiter
336 *
337 * @return array
338 * Array of basictypes with name as 'built-in name' and 'label' as value
339 */
340 public static function contactTypePairs($all = FALSE, $typeName = NULL, $delimiter = NULL) {
341 $types = self::contactTypeInfo($all);
342
343 if ($typeName && !is_array($typeName)) {
344 $typeName = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($typeName, CRM_Core_DAO::VALUE_SEPARATOR));
345 }
346
347 $pairs = array();
348 if ($typeName) {
349 foreach ($typeName as $type) {
350 if (array_key_exists($type, $types)) {
351 $pairs[$type] = $types[$type]['label'];
352 }
353 }
354 }
355 else {
356 foreach ($types as $name => $info) {
357 $pairs[$name] = $info['label'];
358 }
359 }
360
361 return !$delimiter ? $pairs : implode($delimiter, $pairs);
362 }
363
364 /**
365 * Get a list of elements for select box.
366 * Note that this used to default to using the hex(01) character - which results in an invalid character being used in form fields
367 * which was not handled well be anything that loaded & resaved the html (outside core)
368 * The use of this separator is now explicit in the calling functions as a step towards it's removal
369 *
370 * @param bool $all
371 * @param bool $isSeparator
372 * @param string $separator
373 *
374 * @return mixed
375 */
376 public static function getSelectElements(
377 $all = FALSE,
378 $isSeparator = TRUE,
379 $separator = '__'
380 ) {
381 static $_cache = NULL;
382
383 if ($_cache === NULL) {
384 $_cache = array();
385 }
386
387 $argString = $all ? 'CRM_CT_GSE_1' : 'CRM_CT_GSE_0';
388 $argString .= $isSeparator ? '_1' : '_0';
389 $argString .= $separator;
390 if (!array_key_exists($argString, $_cache)) {
391 $cache = CRM_Utils_Cache::singleton();
392 $_cache[$argString] = $cache->get($argString);
393
394 if (!$_cache[$argString]) {
395 $_cache[$argString] = array();
396
397 $sql = "
398 SELECT c.name as child_name , c.label as child_label , c.id as child_id,
399 p.name as parent_name, p.label as parent_label, p.id as parent_id
400 FROM civicrm_contact_type c
401 LEFT JOIN civicrm_contact_type p ON ( c.parent_id = p.id )
402 WHERE ( c.name IS NOT NULL )
403 ";
404
405 if ($all === FALSE) {
406 $sql .= "
407 AND c.is_active = 1
408 AND ( p.is_active = 1 OR p.id IS NULL )
409 ";
410 }
411 $sql .= " ORDER BY c.id";
412
413 $values = array();
414 $dao = CRM_Core_DAO::executeQuery($sql);
415 while ($dao->fetch()) {
416 if (!empty($dao->parent_id)) {
417 $key = $isSeparator ? $dao->parent_name . $separator . $dao->child_name : $dao->child_name;
418 $label = "- {$dao->child_label}";
419 $pName = $dao->parent_name;
420 }
421 else {
422 $key = $dao->child_name;
423 $label = $dao->child_label;
424 $pName = $dao->child_name;
425 }
426
427 if (!isset($values[$pName])) {
428 $values[$pName] = array();
429 }
430 $values[$pName][] = array('key' => $key, 'label' => $label);
431 }
432
433 $selectElements = array();
434 foreach ($values as $pName => $elements) {
435 foreach ($elements as $element) {
436 $selectElements[$element['key']] = $element['label'];
437 }
438 }
439 $_cache[$argString] = $selectElements;
440
441 $cache->set($argString, $_cache[$argString]);
442 }
443 }
444 return $_cache[$argString];
445 }
446
447 /**
448 * Check if a given type is a subtype.
449 *
450 * @param string $subType
451 * Contact subType.
452 * @param bool $ignoreCache
453 *
454 * @return bool
455 * true if subType, false otherwise.
456 */
457 public static function isaSubType($subType, $ignoreCache = FALSE) {
458 return in_array($subType, self::subTypes(NULL, TRUE, 'name', $ignoreCache));
459 }
460
461 /**
462 * Retrieve the basic contact type associated with given subType.
463 *
464 * @param array /string $subType contact subType.
465 * @return array/string of basicTypes.
466 */
467 public static function getBasicType($subType) {
468 static $_cache = NULL;
469 if ($_cache === NULL) {
470 $_cache = array();
471 }
472
473 $isArray = TRUE;
474 if ($subType && !is_array($subType)) {
475 $subType = array($subType);
476 $isArray = FALSE;
477 }
478 $argString = implode('_', $subType);
479
480 if (!array_key_exists($argString, $_cache)) {
481 $_cache[$argString] = array();
482
483 $sql = "
484 SELECT subtype.name as contact_subtype, type.name as contact_type
485 FROM civicrm_contact_type subtype
486 INNER JOIN civicrm_contact_type type ON ( subtype.parent_id = type.id )
487 WHERE subtype.name IN ('" . implode("','", $subType) . "' )";
488 $dao = CRM_Core_DAO::executeQuery($sql);
489 while ($dao->fetch()) {
490 if (!$isArray) {
491 $_cache[$argString] = $dao->contact_type;
492 break;
493 }
494 $_cache[$argString][$dao->contact_subtype] = $dao->contact_type;
495 }
496 }
497 return $_cache[$argString];
498 }
499
500 /**
501 * Suppress all subtypes present in given array.
502 *
503 * @param array $subTypes
504 * Contact subTypes.
505 * @param bool $ignoreCache
506 *
507 * @return array
508 * Array of suppressed subTypes.
509 */
510 public static function suppressSubTypes(&$subTypes, $ignoreCache = FALSE) {
511 $subTypes = array_diff($subTypes, self::subTypes(NULL, TRUE, 'name', $ignoreCache));
512 return $subTypes;
513 }
514
515 /**
516 * Verify if a given subtype is associated with a given basic contact type.
517 *
518 * @param string $subType
519 * Contact subType.
520 * @param string $contactType
521 * Contact Type.
522 * @param bool $ignoreCache
523 * @param string $columnName
524 *
525 * @return bool
526 * true if contact extends, false otherwise.
527 */
528 public static function isExtendsContactType($subType, $contactType, $ignoreCache = FALSE, $columnName = 'name') {
529 if (!is_array($subType)) {
530 $subType = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($subType, CRM_Core_DAO::VALUE_SEPARATOR));
531 }
532 $subtypeList = self::subTypes($contactType, TRUE, $columnName, $ignoreCache);
533 $intersection = array_intersect($subType, $subtypeList);
534 return $subType == $intersection;
535 }
536
537 /**
538 * Create shortcuts menu for contactTypes.
539 *
540 * @return array
541 * of contactTypes
542 */
543 public static function getCreateNewList() {
544 $shortCuts = array();
545 //@todo FIXME - using the CRM_Core_DAO::VALUE_SEPARATOR creates invalid html - if you can find the form
546 // this is loaded onto then replace with something like '__' & test
547 $separator = CRM_Core_DAO::VALUE_SEPARATOR;
548 $contactTypes = self::getSelectElements(FALSE, TRUE, $separator);
549 foreach ($contactTypes as $key => $value) {
550 if ($key) {
551 $typeValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $key);
552 $cType = CRM_Utils_Array::value('0', $typeValue);
553 $typeUrl = 'ct=' . $cType;
554 if ($csType = CRM_Utils_Array::value('1', $typeValue)) {
555 $typeUrl .= "&cst=$csType";
556 }
557 $shortCut = array(
558 'path' => 'civicrm/contact/add',
559 'query' => "$typeUrl&reset=1",
560 'ref' => "new-$value",
561 'title' => $value,
562 );
563 if ($csType = CRM_Utils_Array::value('1', $typeValue)) {
564 $shortCuts[$cType]['shortCuts'][] = $shortCut;
565 }
566 else {
567 $shortCuts[$cType] = $shortCut;
568 }
569 }
570 }
571 return $shortCuts;
572 }
573
574 /**
575 * Delete Contact SubTypes.
576 *
577 * @param int $contactTypeId
578 * ID of the Contact Subtype to be deleted.
579 *
580 * @return bool
581 */
582 public static function del($contactTypeId) {
583
584 if (!$contactTypeId) {
585 return FALSE;
586 }
587
588 $params = array('id' => $contactTypeId);
589 self::retrieve($params, $typeInfo);
590 $name = $typeInfo['name'];
591 // check if any custom group
592 $custom = new CRM_Core_DAO_CustomGroup();
593 $custom->whereAdd("extends_entity_column_value LIKE '%" .
594 CRM_Core_DAO::VALUE_SEPARATOR .
595 $name .
596 CRM_Core_DAO::VALUE_SEPARATOR . "%'"
597 );
598 if ($custom->find()) {
599 return FALSE;
600 }
601
602 // remove subtype for existing contacts
603 $sql = "
604 UPDATE civicrm_contact SET contact_sub_type = NULL
605 WHERE contact_sub_type = '$name'";
606 CRM_Core_DAO::executeQuery($sql);
607
608 // remove subtype from contact type table
609 $contactType = new CRM_Contact_DAO_ContactType();
610 $contactType->id = $contactTypeId;
611 $contactType->delete();
612
613 // remove navigation entry if any
614 if ($name) {
615 $sql = "
616 DELETE
617 FROM civicrm_navigation
618 WHERE name = %1";
619 $params = array(1 => array("New $name", 'String'));
620 $dao = CRM_Core_DAO::executeQuery($sql, $params);
621 CRM_Core_BAO_Navigation::resetNavigation();
622 }
623 return TRUE;
624 }
625
626 /**
627 * Add or update Contact SubTypes.
628 *
629 * @param array $params
630 * An assoc array of name/value pairs.
631 *
632 * @return object|void
633 */
634 public static function add(&$params) {
635
636 // label or name
637 if (empty($params['id']) && empty($params['label'])) {
638 return NULL;
639 }
640 if (!empty($params['parent_id']) &&
641 !CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_ContactType', $params['parent_id'])
642 ) {
643 return NULL;
644 }
645
646 $contactType = new CRM_Contact_DAO_ContactType();
647 $contactType->copyValues($params);
648 $contactType->id = CRM_Utils_Array::value('id', $params);
649 $contactType->is_active = CRM_Utils_Array::value('is_active', $params, 0);
650
651 $contactType->save();
652 if ($contactType->find(TRUE)) {
653 $contactName = $contactType->name;
654 $contact = ucfirst($contactType->label);
655 $active = $contactType->is_active;
656 }
657
658 if (!empty($params['id'])) {
659 $params = array('name' => "New $contactName");
660 $newParams = array(
661 'label' => "New $contact",
662 'is_active' => $active,
663 );
664 CRM_Core_BAO_Navigation::processUpdate($params, $newParams);
665 }
666 else {
667 $name = self::getBasicType($contactName);
668 if (!$name) {
669 return;
670 }
671 $value = array('name' => "New $name");
672 CRM_Core_BAO_Navigation::retrieve($value, $navinfo);
673 $navigation = array(
674 'label' => "New $contact",
675 'name' => "New $contactName",
676 'url' => "civicrm/contact/add?ct=$name&cst=$contactName&reset=1",
677 'permission' => 'add contacts',
678 'parent_id' => $navinfo['id'],
679 'is_active' => $active,
680 );
681 CRM_Core_BAO_Navigation::add($navigation);
682 }
683 CRM_Core_BAO_Navigation::resetNavigation();
684
685 // reset the cache after adding
686 self::subTypeInfo(NULL, FALSE, FALSE, TRUE);
687
688 return $contactType;
689 }
690
691 /**
692 * Update the is_active flag in the db.
693 *
694 * @param int $id
695 * Id of the database record.
696 * @param bool $is_active
697 * Value we want to set the is_active field.
698 *
699 * @return Object
700 * DAO object on success, null otherwise
701 */
702 public static function setIsActive($id, $is_active) {
703 $params = array('id' => $id);
704 self::retrieve($params, $contactinfo);
705 $params = array('name' => "New $contactinfo[name]");
706 $newParams = array('is_active' => $is_active);
707 CRM_Core_BAO_Navigation::processUpdate($params, $newParams);
708 CRM_Core_BAO_Navigation::resetNavigation();
709 return CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_ContactType', $id,
710 'is_active', $is_active
711 );
712 }
713
714 /**
715 * @param string $typeName
716 *
717 * @return mixed
718 */
719 public static function getLabel($typeName) {
720 $types = self::contactTypeInfo(TRUE);
721
722 if (array_key_exists($typeName, $types)) {
723 return $types[$typeName]['label'];
724 }
725 return $typeName;
726 }
727
728 /**
729 * Check whether allow to change any contact's subtype
730 * on the basis of custom data and relationship of specific subtype
731 * currently used in contact/edit form amd in import validation
732 *
733 * @param int $contactId
734 * Contact id.
735 * @param string $subType
736 * Subtype.
737 *
738 * @return bool
739 */
740 public static function isAllowEdit($contactId, $subType = NULL) {
741
742 if (!$contactId) {
743 return TRUE;
744 }
745
746 if (empty($subType)) {
747 $subType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
748 $contactId,
749 'contact_sub_type'
750 );
751 }
752
753 if (self::hasCustomData($subType, $contactId) || self::hasRelationships($contactId, $subType)) {
754 return FALSE;
755 }
756
757 return TRUE;
758 }
759
760 /**
761 * @param $contactType
762 * @param int $contactId
763 *
764 * @return bool
765 */
766 public static function hasCustomData($contactType, $contactId = NULL) {
767 $subTypeClause = '';
768
769 if (self::isaSubType($contactType)) {
770 $subType = $contactType;
771 $contactType = self::getBasicType($subType);
772
773 // check for empty custom data which extends subtype
774 $subTypeValue = CRM_Core_DAO::VALUE_SEPARATOR . $subType . CRM_Core_DAO::VALUE_SEPARATOR;
775 $subTypeClause = " AND extends_entity_column_value LIKE '%{$subTypeValue}%' ";
776 }
777 $query = "SELECT table_name FROM civicrm_custom_group WHERE extends = '{$contactType}' {$subTypeClause}";
778
779 $dao = CRM_Core_DAO::executeQuery($query);
780 while ($dao->fetch()) {
781 $sql = "SELECT count(id) FROM {$dao->table_name}";
782 if ($contactId) {
783 $sql .= " WHERE entity_id = {$contactId}";
784 }
785 $sql .= " LIMIT 1";
786
787 $customDataCount = CRM_Core_DAO::singleValueQuery($sql);
788 if (!empty($customDataCount)) {
789 $dao->free();
790 return TRUE;
791 }
792 }
793 return FALSE;
794 }
795
796 /**
797 * @todo what does this function do?
798 * @param int $contactId
799 * @param $contactType
800 *
801 * @return bool
802 */
803 public static function hasRelationships($contactId, $contactType) {
804 $subTypeClause = NULL;
805 if (self::isaSubType($contactType)) {
806 $subType = $contactType;
807 $contactType = self::getBasicType($subType);
808 $subTypeClause = " AND ( ( crt.contact_type_a = '{$contactType}' AND crt.contact_sub_type_a = '{$subType}') OR
809 ( crt.contact_type_b = '{$contactType}' AND crt.contact_sub_type_b = '{$subType}') ) ";
810 }
811 else {
812 $subTypeClause = " AND ( crt.contact_type_a = '{$contactType}' OR crt.contact_type_b = '{$contactType}' ) ";
813 }
814
815 // check relationships for
816 $relationshipQuery = "
817 SELECT count(cr.id) FROM civicrm_relationship cr
818 INNER JOIN civicrm_relationship_type crt ON
819 ( cr.relationship_type_id = crt.id {$subTypeClause} )
820 WHERE ( cr.contact_id_a = {$contactId} OR cr.contact_id_b = {$contactId} )
821 LIMIT 1";
822
823 $relationshipCount = CRM_Core_DAO::singleValueQuery($relationshipQuery);
824
825 if (!empty($relationshipCount)) {
826 return TRUE;
827 }
828
829 return FALSE;
830 }
831
832 /**
833 * @todo what does this function do?
834 * @param $contactType
835 * @param array $subtypeSet
836 *
837 * @return array
838 */
839 public static function getSubtypeCustomPair($contactType, $subtypeSet = array()) {
840 if (empty($subtypeSet)) {
841 return $subtypeSet;
842 }
843
844 $customSet = $subTypeClause = array();
845 foreach ($subtypeSet as $subtype) {
846 $subtype = CRM_Utils_Type::escape($subtype, 'String');
847 $subType = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR;
848 $subTypeClause[] = "extends_entity_column_value LIKE '%{$subtype}%' ";
849 }
850 $query = "SELECT table_name
851 FROM civicrm_custom_group
852 WHERE extends = %1 AND " . implode(" OR ", $subTypeClause);
853 $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($contactType, 'String')));
854 while ($dao->fetch()) {
855 $customSet[] = $dao->table_name;
856 }
857 return array_unique($customSet);
858 }
859
860 /**
861 * Function that does something.
862 * @todo what does this function do?
863 *
864 * @param int $contactID
865 * @param $contactType
866 * @param array $oldSubtypeSet
867 * @param array $newSubtypeSet
868 *
869 * @return bool
870 */
871 public static function deleteCustomSetForSubtypeMigration(
872 $contactID,
873 $contactType,
874 $oldSubtypeSet = array(),
875 $newSubtypeSet = array()
876 ) {
877 $oldCustomSet = self::getSubtypeCustomPair($contactType, $oldSubtypeSet);
878 $newCustomSet = self::getSubtypeCustomPair($contactType, $newSubtypeSet);
879
880 $customToBeRemoved = array_diff($oldCustomSet, $newCustomSet);
881 foreach ($customToBeRemoved as $customTable) {
882 self::deleteCustomRowsForEntityID($customTable, $contactID);
883 }
884 return TRUE;
885 }
886
887 /**
888 * Delete content / rows of a custom table specific to a subtype for a given custom-group.
889 * This function currently works for contact subtypes only and could be later improved / genralized
890 * to work for other subtypes as well.
891 *
892 * @param int $gID
893 * Custom group id.
894 * @param array $subtypes
895 * List of subtypes related to which entry is to be removed.
896 *
897 * @return void
898 */
899 public static function deleteCustomRowsOfSubtype($gID, $subtypes = array()) {
900 if (!$gID or empty($subtypes)) {
901 return FALSE;
902 }
903
904 $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $gID, 'table_name');
905
906 // drop triggers CRM-13587
907 CRM_Core_DAO::dropTriggers($tableName);
908
909 $subtypeClause = array();
910 foreach ($subtypes as $subtype) {
911 $subtype = CRM_Utils_Type::escape($subtype, 'String');
912 $subtypeClause[] = "civicrm_contact.contact_sub_type LIKE '%" . CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR . "%'";
913 }
914 $subtypeClause = implode(' OR ', $subtypeClause);
915
916 $query = "DELETE custom.*
917 FROM {$tableName} custom
918 INNER JOIN civicrm_contact ON civicrm_contact.id = custom.entity_id
919 WHERE ($subtypeClause)";
920
921 CRM_Core_DAO::singleValueQuery($query);
922
923 // rebuild triggers CRM-13587
924 CRM_Core_DAO::triggerRebuild($tableName);
925 }
926
927 /**
928 * Delete content / rows of a custom table specific entity-id for a given custom-group table.
929 *
930 * @param int $customTable
931 * Custom table name.
932 * @param int $entityID
933 * Entity id.
934 *
935 * @return void
936 */
937 public static function deleteCustomRowsForEntityID($customTable, $entityID) {
938 $customTable = CRM_Utils_Type::escape($customTable, 'String');
939 $query = "DELETE FROM {$customTable} WHERE entity_id = %1";
940 return CRM_Core_DAO::singleValueQuery($query, array(1 => array($entityID, 'Integer')));
941 }
942
943 }