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