3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2013
35 class CRM_Contact_BAO_ContactType
extends CRM_Contact_DAO_ContactType
{
38 * Takes a bunch of params that are needed to match certain criteria and
39 * retrieves the relevant objects. Typically the valid params are only
40 * contact_id. We'll tweak this function to be more full featured over a period
41 * of time. This is the inverse function of create. It also stores all the retrieved
42 * values in the default array
44 * @param array $params (reference ) an assoc array of name/value pairs
45 * @param array $defaults (reference ) an assoc array to hold the flattened values
47 * @return object CRM_Contact_BAO_ContactType object on success, null otherwise
51 static function retrieve(&$params, &$defaults) {
52 $contactType = new CRM_Contact_DAO_ContactType();
53 $contactType->copyValues($params);
54 if ($contactType->find(TRUE)) {
55 CRM_Core_DAO
::storeValues($contactType, $defaults);
61 static function isActive($contactType) {
62 $contact = self
::contactTypeInfo(FALSE);
63 $active = array_key_exists($contactType, $contact) ?
TRUE : FALSE;
69 *function to retrieve basic contact type information.
71 *@return array of basic contact types information.
75 static function &basicTypeInfo($all = FALSE) {
76 static $_cache = NULL;
78 if ($_cache === NULL) {
82 $argString = $all ?
'CRM_CT_BTI_1' : 'CRM_CT_BTI_0';
83 if (!array_key_exists($argString, $_cache)) {
84 $cache = CRM_Utils_Cache
::singleton();
85 $_cache[$argString] = $cache->get($argString);
86 if (!$_cache[$argString]) {
89 FROM civicrm_contact_type
90 WHERE parent_id IS NULL
93 $sql .= " AND is_active = 1";
96 $dao = CRM_Core_DAO
::executeQuery($sql,
97 CRM_Core_DAO
::$_nullArray,
99 'CRM_Contact_DAO_ContactType'
101 while ($dao->fetch()) {
103 CRM_Core_DAO
::storeValues($dao, $value);
104 $_cache[$argString][$dao->name
] = $value;
107 $cache->set($argString, $_cache[$argString]);
110 return $_cache[$argString];
115 *function to retrieve all basic contact types.
117 *@return array of basic contact types
121 static function basicTypes($all = FALSE) {
122 return array_keys(self
::basicTypeInfo($all));
125 static function basicTypePairs($all = FALSE, $key = 'name') {
126 $subtypes = self
::basicTypeInfo($all);
129 foreach ($subtypes as $name => $info) {
130 $index = ($key == 'name') ?
$name : $info[$key];
131 $pairs[$index] = $info['label'];
138 *function to retrieve all subtypes Information.
140 *@param array $contactType.
141 *@return array of sub type information
145 static function &subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCache = FALSE, $reset = FALSE) {
146 static $_cache = NULL;
148 if ($reset === TRUE) {
152 if ($_cache === NULL) {
155 if ($contactType && !is_array($contactType)) {
156 $contactType = array($contactType);
159 $argString = $all ?
'CRM_CT_STI_1_' : 'CRM_CT_STI_0_';
160 if (!empty($contactType)) {
161 $argString .= implode('_', $contactType);
164 if ((!array_key_exists($argString, $_cache)) ||
$ignoreCache) {
165 $cache = CRM_Utils_Cache
::singleton();
166 $_cache[$argString] = $cache->get($argString);
167 if (!$_cache[$argString] ||
$ignoreCache) {
168 $_cache[$argString] = array();
171 if (!empty($contactType)) {
172 $ctWHERE = " AND parent.name IN ('" . implode("','", $contactType) . "')";
176 SELECT subtype.*, parent.name as parent, parent.label as parent_label
177 FROM civicrm_contact_type subtype
178 INNER JOIN civicrm_contact_type parent ON subtype.parent_id = parent.id
179 WHERE subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
181 if ($all === FALSE) {
182 $sql .= " AND subtype.is_active = 1 AND parent.is_active = 1 ORDER BY parent.id";
184 $dao = CRM_Core_DAO
::executeQuery($sql, array(),
185 FALSE, 'CRM_Contact_DAO_ContactType'
187 while ($dao->fetch()) {
189 CRM_Core_DAO
::storeValues($dao, $value);
190 $value['parent'] = $dao->parent
;
191 $value['parent_label'] = $dao->parent_label
;
192 $_cache[$argString][$dao->name
] = $value;
195 $cache->set($argString, $_cache[$argString]);
198 return $_cache[$argString];
203 *function to retrieve all subtypes
205 *@param array $contactType.
206 *@return list of all subtypes OR list of subtypes associated to
207 *a given basic contact type
211 static function subTypes($contactType = NULL, $all = FALSE, $columnName = 'name', $ignoreCache = FALSE) {
212 if ($columnName == 'name') {
213 return array_keys(self
::subTypeInfo($contactType, $all, $ignoreCache));
216 return array_values(self
::subTypePairs($contactType, FALSE, NULL, $ignoreCache));
222 *function to retrieve subtype pairs with name as 'subtype-name' and 'label' as value
224 *@param array $contactType.
225 *@return list of subtypes with name as 'subtype-name' and 'label' as value
229 static function subTypePairs($contactType = NULL, $all = FALSE, $labelPrefix = '- ', $ignoreCache = FALSE) {
230 $subtypes = self
::subTypeInfo($contactType, $all, $ignoreCache);
233 foreach ($subtypes as $name => $info) {
234 $pairs[$name] = $labelPrefix . $info['label'];
241 *function to retrieve list of all types i.e basic + subtypes.
243 *@return array of basic types + all subtypes.
247 static function contactTypes($all = FALSE) {
248 return array_keys(self
::contactTypeInfo($all));
253 *function to retrieve info array about all types i.e basic + subtypes.
255 *@return array of basic types + all subtypes.
259 static function contactTypeInfo($all = FALSE, $reset = FALSE) {
260 static $_cache = NULL;
262 if ($reset === TRUE) {
266 if ($_cache === NULL) {
270 $argString = $all ?
'CRM_CT_CTI_1' : 'CRM_CT_CTI_0';
271 if (!array_key_exists($argString, $_cache)) {
272 $cache = CRM_Utils_Cache
::singleton();
273 $_cache[$argString] = $cache->get($argString);
274 if (!$_cache[$argString]) {
275 $_cache[$argString] = array();
278 SELECT type.*, parent.name as parent, parent.label as parent_label
279 FROM civicrm_contact_type type
280 LEFT JOIN civicrm_contact_type parent ON type.parent_id = parent.id
281 WHERE type.name IS NOT NULL
283 if ($all === FALSE) {
284 $sql .= " AND type.is_active = 1";
287 $dao = CRM_Core_DAO
::executeQuery($sql,
288 CRM_Core_DAO
::$_nullArray,
290 'CRM_Contact_DAO_ContactType'
292 while ($dao->fetch()) {
294 CRM_Core_DAO
::storeValues($dao, $value);
295 if (array_key_exists('parent_id', $value)) {
296 $value['parent'] = $dao->parent
;
297 $value['parent_label'] = $dao->parent_label
;
299 $_cache[$argString][$dao->name
] = $value;
302 $cache->set($argString, $_cache[$argString]);
306 return $_cache[$argString];
311 *function to retrieve basic type pairs with name as 'built-in name' and 'label' as value
313 *@param array $contactType.
314 *@return list of basictypes with name as 'built-in name' and 'label' as value
318 static function contactTypePairs($all = FALSE, $typeName = NULL, $delimiter = NULL) {
319 $types = self
::contactTypeInfo($all);
321 if ($typeName && !is_array($typeName)) {
322 $typeName = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($typeName, CRM_Core_DAO
::VALUE_SEPARATOR
));
327 foreach ($typeName as $type) {
328 if (array_key_exists($type, $types)) {
329 $pairs[$type] = $types[$type]['label'];
334 foreach ($types as $name => $info) {
335 $pairs[$name] = $info['label'];
339 return !$delimiter ?
$pairs : implode($delimiter, $pairs);
342 static function &getSelectElements($all = FALSE,
344 $seperator = CRM_Core_DAO
::VALUE_SEPARATOR
346 static $_cache = NULL;
348 if ($_cache === NULL) {
352 $argString = $all ?
'CRM_CT_GSE_1' : 'CRM_CT_GSE_0';
353 $argString .= $isSeperator ?
'_1' : '_0';
354 if (!array_key_exists($argString, $_cache)) {
355 $cache = CRM_Utils_Cache
::singleton();
356 $_cache[$argString] = $cache->get($argString);
358 if (!$_cache[$argString]) {
359 $_cache[$argString] = array();
362 SELECT c.name as child_name , c.label as child_label , c.id as child_id,
363 p.name as parent_name, p.label as parent_label, p.id as parent_id
364 FROM civicrm_contact_type c
365 LEFT JOIN civicrm_contact_type p ON ( c.parent_id = p.id )
366 WHERE ( c.name IS NOT NULL )
369 if ($all === FALSE) {
372 AND ( p.is_active = 1 OR p.id IS NULL )
375 $sql .= " ORDER BY c.id";
378 $dao = CRM_Core_DAO
::executeQuery($sql);
379 while ($dao->fetch()) {
380 if (!empty($dao->parent_id
)) {
381 $key = $isSeperator ?
$dao->parent_name
. $seperator . $dao->child_name
: $dao->child_name
;
382 $label = "- {$dao->child_label}";
383 $pName = $dao->parent_name
;
386 $key = $dao->child_name
;
387 $label = $dao->child_label
;
388 $pName = $dao->child_name
;
391 if (!isset($values[$pName])) {
392 $values[$pName] = array();
394 $values[$pName][] = array('key' => $key, 'label' => $label);
397 $selectElements = array();
398 foreach ($values as $pName => $elements) {
399 foreach ($elements as $element) {
400 $selectElements[$element['key']] = $element['label'];
403 $_cache[$argString] = $selectElements;
405 $cache->set($argString, $_cache[$argString]);
408 return $_cache[$argString];
412 * function to check if a given type is a subtype
414 *@param string $subType contact subType.
415 *@return boolean true if subType, false otherwise.
419 static function isaSubType($subType, $ignoreCache = FALSE) {
420 return in_array($subType, self
::subTypes(NULL, TRUE, 'name', $ignoreCache));
424 *function to retrieve the basic contact type associated with
427 *@param array/string $subType contact subType.
428 *@return array/string of basicTypes.
432 static function getBasicType($subType) {
433 static $_cache = NULL;
434 if ($_cache === NULL) {
439 if ($subType && !is_array($subType)) {
440 $subType = array($subType);
443 $argString = implode('_', $subType);
445 if (!array_key_exists($argString, $_cache)) {
446 $_cache[$argString] = array();
449 SELECT subtype.name as contact_subtype, type.name as contact_type
450 FROM civicrm_contact_type subtype
451 INNER JOIN civicrm_contact_type type ON ( subtype.parent_id = type.id )
452 WHERE subtype.name IN ('" . implode("','", $subType) . "' )";
453 $dao = CRM_Core_DAO
::executeQuery($sql);
454 while ($dao->fetch()) {
456 $_cache[$argString] = $dao->contact_type
;
459 $_cache[$argString][$dao->contact_subtype
] = $dao->contact_type
;
462 return $_cache[$argString];
467 *function to suppress all subtypes present in given array.
469 *@param array $subType contact subType.
470 *@return array of suppresssubTypes .
474 static function suppressSubTypes(&$subTypes, $ignoreCache = FALSE) {
475 $subTypes = array_diff($subTypes, self
::subTypes(NULL, TRUE, 'name', $ignoreCache));
481 *function to verify if a given subtype is associated with a given basic contact type.
483 *@param string $subType contact subType
484 *@param string $contactType contact Type
485 *@return boolean true if contact extends, false otherwise.
489 static function isExtendsContactType($subType, $contactType, $ignoreCache = FALSE, $columnName = 'name') {
490 if (!is_array($subType)) {
491 $subType = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($subType, CRM_Core_DAO
::VALUE_SEPARATOR
));
493 $subtypeList = self
::subTypes($contactType, TRUE, $columnName, $ignoreCache);
494 $intersection = array_intersect($subType, $subtypeList);
495 return $subType == $intersection;
500 *function to create shortcuts menu for contactTypes
502 *@return array of contactTypes
506 static function getCreateNewList() {
507 $shortCuts = array();
508 $contactTypes = self
::getSelectElements();
509 foreach ($contactTypes as $key => $value) {
511 $typeValue = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $key);
512 $typeUrl = 'ct=' . CRM_Utils_Array
::value('0', $typeValue);
513 if ($csType = CRM_Utils_Array
::value('1', $typeValue)) {
514 $typeUrl .= "&cst=$csType";
516 $shortCuts[] = array(
517 'path' => 'civicrm/contact/add',
518 'query' => "$typeUrl&reset=1",
519 'ref' => "new-$value",
528 * Function to delete Contact SubTypes
530 * @param int $contactTypeId ID of the Contact Subtype to be deleted.
535 static function del($contactTypeId) {
537 if (!$contactTypeId) {
541 $params = array('id' => $contactTypeId);
542 self
::retrieve($params, $typeInfo);
543 $name = $typeInfo['name'];
544 // check if any custom group
545 $custom = new CRM_Core_DAO_CustomGroup();
546 $custom->whereAdd("extends_entity_column_value LIKE '%" .
547 CRM_Core_DAO
::VALUE_SEPARATOR
.
549 CRM_Core_DAO
::VALUE_SEPARATOR
. "%'"
551 if ($custom->find()) {
555 // remove subtype for existing contacts
557 UPDATE civicrm_contact SET contact_sub_type = NULL
558 WHERE contact_sub_type = '$name'";
559 CRM_Core_DAO
::executeQuery($sql);
561 // remove subtype from contact type table
562 $contactType = new CRM_Contact_DAO_ContactType();
563 $contactType->id
= $contactTypeId;
564 $contactType->delete();
566 // remove navigation entry if any
570 FROM civicrm_navigation
572 $params = array(1 => array("New $name", 'String'));
573 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
574 CRM_Core_BAO_Navigation
::resetNavigation();
580 * Function to add or update Contact SubTypes
582 * @param array $params an assoc array of name/value pairs
588 static function add($params) {
591 if (!CRM_Utils_Array
::value('label', $params)) {
594 if (CRM_Utils_Array
::value('parent_id', $params) &&
595 !CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_ContactType', $params['parent_id'])
600 $contactType = new CRM_Contact_DAO_ContactType();
601 $contactType->copyValues($params);
602 $contactType->id
= CRM_Utils_Array
::value('id', $params);
603 $contactType->is_active
= CRM_Utils_Array
::value('is_active', $params, 0);
607 $contactType->save();
608 if ($contactType->find(TRUE)) {
609 $contactName = $contactType->name
;
610 $contact = ucfirst($contactType->label
);
611 $active = $contactType->is_active
;
614 if (CRM_Utils_Array
::value('id', $params)) {
615 $params = array('name' => "New $contactName");
617 'label' => "New $contact",
618 'is_active' => $active,
620 CRM_Core_BAO_Navigation
::processUpdate($params, $newParams);
623 $name = self
::getBasicType($contactName);
627 $value = array('name' => "New $name");
628 CRM_Core_BAO_Navigation
::retrieve($value, $navinfo);
630 'label' => "New $contact",
631 'name' => "New $contactName",
632 'url' => "civicrm/contact/add&ct=$name&cst=$contactName&reset=1",
633 'permission' => 'add contacts',
634 'parent_id' => $navinfo['id'],
635 'is_active' => $active,
637 CRM_Core_BAO_Navigation
::add($navigation);
639 CRM_Core_BAO_Navigation
::resetNavigation();
641 // reset the cache after adding
642 self
::subTypeInfo(NULL, FALSE, FALSE, TRUE);
648 * update the is_active flag in the db
650 * @param int $id id of the database record
651 * @param boolean $is_active value we want to set the is_active field
653 * @return Object DAO object on success, null otherwise
656 static function setIsActive($id, $is_active) {
657 $params = array('id' => $id);
658 self
::retrieve($params, $contactinfo);
659 $params = array('name' => "New $contactinfo[name]");
660 $newParams = array('is_active' => $is_active);
661 CRM_Core_BAO_Navigation
::processUpdate($params, $newParams);
662 CRM_Core_BAO_Navigation
::resetNavigation();
663 return CRM_Core_DAO
::setFieldValue('CRM_Contact_DAO_ContactType', $id,
664 'is_active', $is_active
668 static function getLabel($typeName) {
669 $types = self
::contactTypeInfo(TRUE);
671 if (array_key_exists($typeName, $types)) {
672 return $types[$typeName]['label'];
678 * Function to check whether allow to change any contact's subtype
679 * on the basis of custom data and relationship of specific subtype
680 * currently used in contact/edit form amd in import validation
682 * @param int $contactId contact id.
683 * @param string $subType subtype.
685 * @return boolean true/false.
688 static function isAllowEdit($contactId, $subType = NULL) {
694 if (empty($subType)) {
695 $subType = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
701 if (self
::hasCustomData($subType, $contactId) || self
::hasRelationships($contactId, $subType)) {
708 static function hasCustomData($contactType, $contactId = NULL) {
711 if (self
::isaSubType($contactType)) {
712 $subType = $contactType;
713 $contactType = self
::getBasicType($subType);
715 // check for empty custom data which extends subtype
716 $subTypeValue = CRM_Core_DAO
::VALUE_SEPARATOR
. $subType . CRM_Core_DAO
::VALUE_SEPARATOR
;
717 $subTypeClause = " AND extends_entity_column_value LIKE '%{$subTypeValue}%' ";
719 $query = "SELECT table_name FROM civicrm_custom_group WHERE extends = '{$contactType}' {$subTypeClause}";
721 $dao = CRM_Core_DAO
::executeQuery($query);
722 while ($dao->fetch()) {
723 $sql = "SELECT count(id) FROM {$dao->table_name}";
725 $sql .= " WHERE entity_id = {$contactId}";
729 $customDataCount = CRM_Core_DAO
::singleValueQuery($sql);
730 if (!empty($customDataCount)) {
738 static function hasRelationships($contactId, $contactType) {
739 $subTypeClause = NULL;
740 if (self
::isaSubType($contactType)) {
741 $subType = $contactType;
742 $contactType = self
::getBasicType($subType);
743 $subTypeClause = " AND ( ( crt.contact_type_a = '{$contactType}' AND crt.contact_sub_type_a = '{$subType}') OR
744 ( crt.contact_type_b = '{$contactType}' AND crt.contact_sub_type_b = '{$subType}') ) ";
747 $subTypeClause = " AND ( crt.contact_type_a = '{$contactType}' OR crt.contact_type_b = '{$contactType}' ) ";
750 // check relationships for
751 $relationshipQuery = "
752 SELECT count(cr.id) FROM civicrm_relationship cr
753 INNER JOIN civicrm_relationship_type crt ON
754 ( cr.relationship_type_id = crt.id {$subTypeClause} )
755 WHERE ( cr.contact_id_a = {$contactId} OR cr.contact_id_b = {$contactId} )
758 $relationshipCount = CRM_Core_DAO
::singleValueQuery($relationshipQuery);
760 if (!empty($relationshipCount)) {
767 static function getSubtypeCustomPair($contactType, $subtypeSet = array(
769 if (empty($subtypeSet)) {
773 $customSet = $subTypeClause = array();
774 foreach ($subtypeSet as $subtype) {
775 $subtype = CRM_Utils_Type
::escape($subtype, 'String');
776 $subType = CRM_Core_DAO
::VALUE_SEPARATOR
. $subtype . CRM_Core_DAO
::VALUE_SEPARATOR
;
777 $subTypeClause[] = "extends_entity_column_value LIKE '%{$subtype}%' ";
779 $query = "SELECT table_name
780 FROM civicrm_custom_group
781 WHERE extends = %1 AND " . implode(" OR ", $subTypeClause);
782 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($contactType, 'String')));
783 while ($dao->fetch()) {
784 $customSet[] = $dao->table_name
;
786 return array_unique($customSet);
789 static function deleteCustomSetForSubtypeMigration($contactID,
791 $oldSubtypeSet = array(),
792 $newSubtypeSet = array()
794 $oldCustomSet = self
::getSubtypeCustomPair($contactType, $oldSubtypeSet);
795 $newCustomSet = self
::getSubtypeCustomPair($contactType, $newSubtypeSet);
797 $customToBeRemoved = array_diff($oldCustomSet, $newCustomSet);
798 foreach ($customToBeRemoved as $customTable) {
799 self
::deleteCustomRowsForEntityID($customTable, $contactID);
805 * Delete content / rows of a custom table specific to a subtype for a given custom-group.
806 * This function currently works for contact subtypes only and could be later improved / genralized
807 * to work for other subtypes as well.
809 * @param int $gID - custom group id.
810 * @param array $subtypes - list of subtypes related to which entry is to be removed.
815 function deleteCustomRowsOfSubtype($gID, $subtypes = array(
817 if (!$gID or empty($subtypes)) {
821 $tableName = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomGroup', $gID, 'table_name');
823 $subtypeClause = array();
824 foreach ($subtypes as $subtype) {
825 $subtype = CRM_Utils_Type
::escape($subtype, 'String');
826 $subtypeClause[] = "civicrm_contact.contact_sub_type LIKE '%" . CRM_Core_DAO
::VALUE_SEPARATOR
. $subtype . CRM_Core_DAO
::VALUE_SEPARATOR
. "%'";
828 $subtypeClause = implode(' OR ', $subtypeClause);
830 $query = "DELETE custom.*
831 FROM {$tableName} custom
832 INNER JOIN civicrm_contact ON civicrm_contact.id = custom.entity_id
833 WHERE ($subtypeClause)";
834 return CRM_Core_DAO
::singleValueQuery($query);
838 * Delete content / rows of a custom table specific entity-id for a given custom-group table.
840 * @param int $customTable - custom table name.
841 * @param int $entityID - entity id.
846 function deleteCustomRowsForEntityID($customTable, $entityID) {
847 $customTable = CRM_Utils_Type
::escape($customTable, 'String');
848 $query = "DELETE FROM {$customTable} WHERE entity_id = %1";
849 return CRM_Core_DAO
::singleValueQuery($query, array(1 => array($entityID, 'Integer')));