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