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